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 | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2021 OpenCFD Ltd. Copyright (C) 2017-2022 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -33,7 +33,7 @@ License
#include "IOstreams.H" #include "IOstreams.H"
#include "StringStream.H" #include "StringStream.H"
#include "ListOps.H" #include "ListOps.H"
#include "StringListOps.H" #include "stringListOps.H"
using namespace Foam; using namespace Foam;
@ -71,9 +71,18 @@ int main()
<< "table1 [" << table1.size() << "] " << endl; << "table1 [" << table1.size() << "] " << endl;
forAllConstIters(table1, iter) 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("acr", 108);
table1.set("adx", 109); table1.set("adx", 109);
table1.set("aec", 100); table1.set("aec", 100);
@ -82,6 +91,22 @@ int main()
Info<< "\noverwrote some values table1: " << table1 << endl; 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<< "\ntest find:" << endl;
Info<< table1.find("aaa")() << nl Info<< table1.find("aaa")() << nl
<< table1.find("aba")() << nl << table1.find("aba")() << nl

View File

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

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2021 OpenCFD Ltd. Copyright (C) 2017-2022 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -26,12 +26,13 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef HashTable_C #ifndef Foam_HashTable_C
#define HashTable_C #define Foam_HashTable_C
#include "HashTable.H" #include "HashTable.H"
#include "List.H" #include "List.H"
#include "FixedList.H" #include "FixedList.H"
#include "UPtrList.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * 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 T, class Key, class Hash>
template<class UnaryPredicate> template<class UnaryPredicate>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocKeys 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 depends on the method used to generate the hash key index, the
table capacity, insertion order etc. When the key order is table capacity, insertion order etc. When the key order is
important, use the sortedToc() method to obtain a list of sorted 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 Internally the table uses closed addressing into a flat storage space
with collisions handled by linked-list chaining. with collisions handled by linked-list chaining.
@ -80,8 +81,8 @@ SourceFiles
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef HashTable_H #ifndef Foam_HashTable_H
#define HashTable_H #define Foam_HashTable_H
#include "word.H" #include "word.H"
#include "zero.H" #include "zero.H"
@ -102,6 +103,7 @@ namespace Foam
template<class T> class List; template<class T> class List;
template<class T> class UList; template<class T> class UList;
template<class T> class UPtrList;
template<class T, unsigned N> class FixedList; template<class T, unsigned N> class FixedList;
template<class T, class Key, class Hash> class HashTable; template<class T, class Key, class Hash> class HashTable;
@ -120,9 +122,15 @@ class HashTable
: :
public HashTableCore 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 typedef typename std::conditional
< <
std::is_same<zero::null, typename std::remove_cv<T>::type>::value, std::is_same<zero::null, typename std::remove_cv<T>::type>::value,
@ -131,42 +139,6 @@ class HashTable
>::type node_type; >::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 // STL type definitions
//- The second template parameter, type of keys used. //- The second template parameter, type of keys used.
@ -209,6 +181,39 @@ public:
//- Forward iterator with const access //- Forward iterator with const access
class const_iterator; 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: protected:
//- Internally used base for iterator and const_iterator //- Internally used base for iterator and const_iterator
@ -220,6 +225,7 @@ protected:
//- An iterator with non-const access to HashTable internals. //- An iterator with non-const access to HashTable internals.
friend class Iterator<false>; friend class Iterator<false>;
public: public:
// Constructors // Constructors
@ -336,6 +342,17 @@ public:
) const; ) 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 // Counting
//- Count the number of keys that satisfy the unary predicate //- Count the number of keys that satisfy the unary predicate
@ -701,6 +718,7 @@ public:
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
using difference_type = this_type::difference_type; using difference_type = this_type::difference_type;
using node_type = this_type::node_type;
using key_type = this_type::key_type; using key_type = this_type::key_type;
using mapped_type = this_type::mapped_type; using mapped_type = this_type::mapped_type;
using value_type = this_type::value_type; using value_type = this_type::value_type;
@ -724,25 +742,37 @@ public:
// Member Functions/Operators // 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) //- Const access to referenced object (value)
inline const_reference val() const const_reference val() const
{ {
return Iterator<false>::entry_->cval(); return Iterator<false>::entry_->cval();
} }
//- Non-const access to referenced object (value) //- Non-const access to referenced object (value)
inline reference val() reference val()
{ {
return Iterator<false>::entry_->val(); return Iterator<false>::entry_->val();
} }
//- Const access to referenced object (value) //- Const access to referenced object (value)
inline const_reference operator*() const { return this->val(); } const_reference operator*() const { return this->val(); }
inline const_reference operator()() const { return this->val(); } const_reference operator()() const { return this->val(); }
//- Non-const access to referenced object (value) //- Non-const access to referenced object (value)
inline reference operator*() { return this->val(); } reference operator*() { return this->val(); }
inline reference operator()() { return this->val(); } reference operator()() { return this->val(); }
inline iterator& operator++(); inline iterator& operator++();
inline iterator operator++(int); inline iterator operator++(int);
@ -762,6 +792,7 @@ public:
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
using difference_type = this_type::difference_type; using difference_type = this_type::difference_type;
using node_type = this_type::node_type;
using key_type = this_type::key_type; using key_type = this_type::key_type;
using mapped_type = const this_type::mapped_type; using mapped_type = const this_type::mapped_type;
using value_type = const this_type::value_type; using value_type = const this_type::value_type;
@ -799,15 +830,21 @@ public:
// Member Functions/Operators // 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) //- Const access to referenced object (value)
inline reference val() const reference val() const
{ {
return Iterator<true>::entry_->cval(); return Iterator<true>::entry_->cval();
} }
//- Const access to referenced object (value) //- Const access to referenced object (value)
inline reference operator*() const { return this->val(); } reference operator*() const { return this->val(); }
inline reference operator()() const { return this->val(); } reference operator()() const { return this->val(); }
inline const_iterator& operator++(); inline const_iterator& operator++();
inline const_iterator operator++(int); inline const_iterator operator++(int);
@ -853,8 +890,8 @@ public:
{} {}
//- Return the key //- Return the key
inline reference operator*() const { return this->key(); } reference operator*() const { return this->key(); }
inline reference operator()() const { return this->key(); } reference operator()() const { return this->key(); }
inline key_iterator_base& operator++() inline key_iterator_base& operator++()
{ {

View File

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