ENH: HashTable sorted() method

- the sorted() method fills a UPtrList with sorted entries. In some
  places this can provide a more convenient means of traversing a
  HashTable in consistent order, without the extra step of creating
  a sortedToc(). The sorted() method with a UPtrList will also have
  a lower overhead than creating any sortedToc() or toc() since it is
  list of pointers and not full copies of the keys.

  Instead of this:

      HashTable<someType> table = ...;

      for (const word& key : table.sortedToc())
      {
          Info<< key << " => " << table[key] << nl;
      }

  can write this:

      for (const auto& iter : table.sorted())
      {
          Info<< iter.key() << " => " << iter.val() << nl;
      }

STYLE:

- declare hash entry key 'const' since it is immutable
This commit is contained in:
Mark Olesen
2022-05-09 14:02:36 +02:00
parent d68902f4a7
commit 7afebef509
5 changed files with 218 additions and 82 deletions

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2021 OpenCFD Ltd.
Copyright (C) 2017-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -33,7 +33,7 @@ License
#include "IOstreams.H"
#include "StringStream.H"
#include "ListOps.H"
#include "StringListOps.H"
#include "stringListOps.H"
using namespace Foam;
@ -71,9 +71,18 @@ int main()
<< "table1 [" << table1.size() << "] " << endl;
forAllConstIters(table1, iter)
{
Info<< iter.key() << " => " << iter() << nl;
Info<< iter.key() << " => " << iter.val() << nl;
}
Info<< "\ntable1 sorted() :" << endl;
for (const auto& iter : table1.sorted())
{
Info<< " " << iter.key() << " => " << iter.val() << nl;
}
Info<< endl;
table1.set("acr", 108);
table1.set("adx", 109);
table1.set("aec", 100);
@ -82,6 +91,22 @@ int main()
Info<< "\noverwrote some values table1: " << table1 << endl;
// Test writable sorted access
for (auto& iter : table1.sorted())
{
// Should not compile: iter.key() = "illegal";
iter.val() *= 2;
}
Info<< "\nInplace modified - via sorted() access :" << endl;
for (const auto& iter : table1.sorted())
{
Info<< " " << iter.key() << " => " << iter.val() << nl;
}
Info<< endl;
Info<< "\ntest find:" << endl;
Info<< table1.find("aaa")() << nl
<< table1.find("aba")() << nl

View File

@ -34,7 +34,7 @@ Description
#include "argList.H"
#include "ITstream.H"
#include "ListOps.H"
#include "StringListOps.H"
#include "stringListOps.H"
using namespace Foam;

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2021 OpenCFD Ltd.
Copyright (C) 2017-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -26,12 +26,13 @@ License
\*---------------------------------------------------------------------------*/
#ifndef HashTable_C
#define HashTable_C
#ifndef Foam_HashTable_C
#define Foam_HashTable_C
#include "HashTable.H"
#include "List.H"
#include "FixedList.H"
#include "UPtrList.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
@ -156,6 +157,45 @@ Foam::List<Key> Foam::HashTable<T, Key, Hash>::sortedToc
}
template<class T, class Key, class Hash>
Foam::UPtrList<const typename Foam::HashTable<T, Key, Hash>::node_type>
Foam::HashTable<T, Key, Hash>::sorted() const
{
UPtrList<const node_type> list(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
{
list.set(count++, iter.node());
}
Foam::sort(list);
return list;
}
template<class T, class Key, class Hash>
Foam::UPtrList<typename Foam::HashTable<T, Key, Hash>::node_type>
Foam::HashTable<T, Key, Hash>::sorted()
{
UPtrList<node_type> list(size_);
label count = 0;
for (iterator iter = begin(); iter != end(); ++iter)
{
list.set(count++, iter.node());
}
Foam::sort(list);
return list;
}
template<class T, class Key, class Hash>
template<class UnaryPredicate>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocKeys

View File

@ -34,7 +34,8 @@ Description
depends on the method used to generate the hash key index, the
table capacity, insertion order etc. When the key order is
important, use the sortedToc() method to obtain a list of sorted
keys and use that for further access.
keys and use that for further access, or the sorted() method
to obtain a UPtrList of entries to traverse in sorted order.
Internally the table uses closed addressing into a flat storage space
with collisions handled by linked-list chaining.
@ -80,8 +81,8 @@ SourceFiles
\*---------------------------------------------------------------------------*/
#ifndef HashTable_H
#define HashTable_H
#ifndef Foam_HashTable_H
#define Foam_HashTable_H
#include "word.H"
#include "zero.H"
@ -102,6 +103,7 @@ namespace Foam
template<class T> class List;
template<class T> class UList;
template<class T> class UPtrList;
template<class T, unsigned N> class FixedList;
template<class T, class Key, class Hash> class HashTable;
@ -120,9 +122,15 @@ class HashTable
:
public HashTableCore
{
// Types
public:
//- Table entry. A hashed node with a linked-list for collisions
// Data Types
//- The template instance used for this HashTable
typedef HashTable<T, Key, Hash> this_type;
//- A table entry (node) that encapsulates the key/val tuple
//- with an additional linked-list entry for hash collisions
typedef typename std::conditional
<
std::is_same<zero::null, typename std::remove_cv<T>::type>::value,
@ -131,42 +139,6 @@ class HashTable
>::type node_type;
// Private Data
//- The number of nodes currently stored in table
label size_;
//- Number of nodes allocated in table
label capacity_;
//- The table of primary nodes
node_type** table_;
// Private Member Functions
//- Return the hash index of the Key within the current table size.
// No checks for zero-sized tables.
inline label hashKeyIndex(const Key& key) const;
//- Assign a new hash-entry to a possibly already existing key.
// \return True if the new entry was set.
template<class... Args>
bool setEntry(const bool overwrite, const Key& key, Args&&... args);
//- Read hash table
Istream& readTable(Istream& is);
//- Write hash table
Ostream& writeTable(Ostream& os) const;
public:
//- The template instance used for this HashTable
typedef HashTable<T, Key, Hash> this_type;
// STL type definitions
//- The second template parameter, type of keys used.
@ -209,6 +181,39 @@ public:
//- Forward iterator with const access
class const_iterator;
private:
// Private Data
//- The number of nodes currently stored in table
label size_;
//- Number of nodes allocated in table
label capacity_;
//- The table of primary nodes
node_type** table_;
// Private Member Functions
//- Return the hash index of the Key within the current table size.
// No checks for zero-sized tables.
inline label hashKeyIndex(const Key& key) const;
//- Assign a new hash-entry to a possibly already existing key.
// \return True if the new entry was set.
template<class... Args>
bool setEntry(const bool overwrite, const Key& key, Args&&... args);
//- Read hash table
Istream& readTable(Istream& is);
//- Write hash table
Ostream& writeTable(Ostream& os) const;
protected:
//- Internally used base for iterator and const_iterator
@ -220,6 +225,7 @@ protected:
//- An iterator with non-const access to HashTable internals.
friend class Iterator<false>;
public:
// Constructors
@ -336,6 +342,17 @@ public:
) const;
// Sorted entries
//- Const access to the hash-table contents in sorted order
//- (sorted by keys).
UPtrList<const node_type> sorted() const;
//- Non-const access to the hash-table contents in sorted order
//- (sorted by keys).
UPtrList<node_type> sorted();
// Counting
//- Count the number of keys that satisfy the unary predicate
@ -701,6 +718,7 @@ public:
using iterator_category = std::forward_iterator_tag;
using difference_type = this_type::difference_type;
using node_type = this_type::node_type;
using key_type = this_type::key_type;
using mapped_type = this_type::mapped_type;
using value_type = this_type::value_type;
@ -724,25 +742,37 @@ public:
// Member Functions/Operators
//- Const access to the entry (node)
const node_type* node() const noexcept
{
return Iterator<false>::entry_;
}
//- Non-const access to the entry (node)
node_type* node() noexcept
{
return Iterator<false>::entry_;
}
//- Const access to referenced object (value)
inline const_reference val() const
const_reference val() const
{
return Iterator<false>::entry_->cval();
}
//- Non-const access to referenced object (value)
inline reference val()
reference val()
{
return Iterator<false>::entry_->val();
}
//- Const access to referenced object (value)
inline const_reference operator*() const { return this->val(); }
inline const_reference operator()() const { return this->val(); }
const_reference operator*() const { return this->val(); }
const_reference operator()() const { return this->val(); }
//- Non-const access to referenced object (value)
inline reference operator*() { return this->val(); }
inline reference operator()() { return this->val(); }
reference operator*() { return this->val(); }
reference operator()() { return this->val(); }
inline iterator& operator++();
inline iterator operator++(int);
@ -762,6 +792,7 @@ public:
using iterator_category = std::forward_iterator_tag;
using difference_type = this_type::difference_type;
using node_type = this_type::node_type;
using key_type = this_type::key_type;
using mapped_type = const this_type::mapped_type;
using value_type = const this_type::value_type;
@ -799,15 +830,21 @@ public:
// Member Functions/Operators
//- Const access to the entry (node)
const node_type* node() const noexcept
{
return Iterator<true>::entry_;
}
//- Const access to referenced object (value)
inline reference val() const
reference val() const
{
return Iterator<true>::entry_->cval();
}
//- Const access to referenced object (value)
inline reference operator*() const { return this->val(); }
inline reference operator()() const { return this->val(); }
reference operator*() const { return this->val(); }
reference operator()() const { return this->val(); }
inline const_iterator& operator++();
inline const_iterator operator++(int);
@ -853,8 +890,8 @@ public:
{}
//- Return the key
inline reference operator*() const { return this->key(); }
inline reference operator()() const { return this->key(); }
reference operator*() const { return this->key(); }
reference operator()() const { return this->key(); }
inline key_iterator_base& operator++()
{

View File

@ -34,8 +34,8 @@ SourceFiles
\*---------------------------------------------------------------------------*/
#ifndef HashTableDetail_H
#define HashTableDetail_H
#ifndef Foam_HashTableDetail_H
#define Foam_HashTableDetail_H
#include "zero.H"
#include <memory>
@ -73,13 +73,13 @@ template<class T> struct isPointerLike<std::unique_ptr<T>> : std::true_type {};
\*---------------------------------------------------------------------------*/
//- Internal storage type for HashTable entries
// Structure with a (K,V) tuple and a linked-list for collisions
// Comprises a (K,V) tuple and a linked-list entry for hash collisions.
// Could store key/val as std::pair, but no particular advantage
// unless the iterator dereference type changes.
template<class K, class V>
struct HashTablePair
{
// Types
// Data Types
//- Type of key
typedef K key_type;
@ -87,26 +87,26 @@ struct HashTablePair
//- Type of content
typedef V mapped_type;
//- This struct stores a value
//- This class stores a value
static constexpr bool stores_value() noexcept
{
return true;
}
// Member Data
// Data
//- The lookup key
key_type key_;
const K key_;
//- The mapped data
mapped_type val_;
V val_;
//- Addressing (next in collision list)
HashTablePair* next_;
// Member Functions
// Generated Methods
//- No copy construct
HashTablePair(const HashTablePair&) = delete;
@ -115,6 +115,8 @@ struct HashTablePair
void operator=(const HashTablePair&) = delete;
// Constructors
//- Construct from next pointer, key, contents
template<class... Args>
HashTablePair
@ -130,24 +132,39 @@ struct HashTablePair
{}
// Member Functions
//- The key
const key_type& key() const
const key_type& key() const noexcept
{
return key_;
}
//- Const access to the mapped value
const mapped_type& cval() const
const mapped_type& cval() const noexcept
{
return val_;
}
//- Const access to the mapped value
const mapped_type& val() const noexcept
{
return val_;
}
//- Non-const access to the mapped value
mapped_type& val()
mapped_type& val() noexcept
{
return val_;
}
//- Compare keys
bool operator<(const HashTablePair& rhs) const
{
return key_ < rhs.key_;
}
//- Write (key, val) pair - for pointer types
template<class TypeT = V>
typename std::enable_if
@ -190,38 +207,38 @@ struct HashTablePair
\*---------------------------------------------------------------------------*/
//- Internal storage type for HashSet entries
// Structure with a single (K) value and a linked-list for collisions
// Comprises a single (K) value and a linked-list entry for hash collisions
template<class K>
struct HashTableSingle
{
// Types
// Data Types
//- Type of key
typedef K key_type;
//- Type of content
//- Type of content (no content: placeholder)
typedef Foam::zero::null mapped_type;
//- Content storage type to the entry
typedef key_type value_type;
//- This struct does not store a value
//- This class does not store any value
static constexpr bool stores_value() noexcept
{
return false;
}
// Member Data
// Data
//- The lookup key == content
key_type key_;
const K key_;
//- Addressing (next in collision list)
HashTableSingle* next_;
// Member Functions
// Generated Methods
//- No copy construct
HashTableSingle(const HashTableSingle&) = delete;
@ -230,6 +247,8 @@ struct HashTableSingle
void operator=(const HashTableSingle&) = delete;
// Constructors
//- Construct from next pointer, key, (unused) contents
template<class... Args>
HashTableSingle
@ -244,25 +263,40 @@ struct HashTableSingle
{}
// Member Functions
//- The key
const key_type& key() const
const key_type& key() const noexcept
{
return key_;
}
//- Const access to the (dummy) mapped value
const mapped_type& cval() const
const mapped_type& cval() const noexcept
{
return Foam::zero::null::dummy;
}
//- Const access to the (dummy) mapped value
const mapped_type& val() const noexcept
{
return Foam::zero::null::dummy;
}
//- Non-const access to the (dummy) mapped value
mapped_type& val()
mapped_type& val() noexcept
{
return Foam::zero::null::dummy;
}
//- Write the key. There is no value
//- Compare keys
bool operator<(const HashTableSingle& rhs) const
{
return key_ < rhs.key_;
}
//- Write the key. There is no value to write.
void print(Ostream& os) const
{
os << key_;