ENH: added HashTable 'lookup' and 'retain' methods

- lookup(): with a default value (const access)
  For example,
      Map<label> something;
      value = something.lookup(key, -1);

    being equivalent to the following:

      Map<label> something;
      value = -1;  // bad value
      if (something.found(key))
      {
          value = something[key];
      }

    except that lookup also makes it convenient to handle const references.
    Eg,

      const labelList& ids = someHash.lookup(key, labelList());

- For consistency, provide a two parameter HashTable '()' operator.
  The lookup() method is, however, normally preferable when
  const-only access is to be ensured.

- retain(): the counterpart to erase(), it only retains entries
  corresponding to the listed keys.

  For example,
      HashTable<someType> largeCache;
      wordHashSet preserve = ...;

      largeCache.retain(preserve);

    being roughly equivalent to the following two-stage process,
    but with reduced overhead and typing, and fewer potential mistakes.

      HashTable<someType> largeCache;
      wordHashSet preserve = ...;

      {
          wordHashSet cull(largeCache.toc()); // all keys
          cull.erase(preserve);               // except those to preserve
          largeCache.erase(cull);             //
      }

  The HashSet &= operator and retain() are functionally equivalent,
  but retain() also works with dissimilar value types.
This commit is contained in:
Mark Olesen 2017-05-11 12:25:35 +02:00
parent 8728e8353f
commit f73b5b629f
10 changed files with 168 additions and 64 deletions

View File

@ -197,8 +197,12 @@ int main(int argc, char *argv[])
Info<< "setD has no 11" << endl;
}
Info<< "setB : " << flatOutput(setB) << endl;
Info<< "setD : " << flatOutput(setD) << endl;
setD -= setB;
Info<< "setD -= setB : " << flatOutput(setD) << endl;
// This should not work (yet?)
// setD[12] = true;

View File

@ -25,6 +25,7 @@ License
#include "HashTable.H"
#include "List.H"
#include "FlatOutput.H"
#include "IOstreams.H"
#include "IStringStream.H"
#include "OStringStream.H"
@ -163,15 +164,15 @@ int main()
<< "\ntable2" << table2 << nl;
Info<< "\ntable3" << table3
<< "\nclearStorage table3 ... ";
table3.clearStorage();
Info<< table3 << nl;
Info<< "\ntable3" << table2
<< "\nclearStorage table2 ... ";
table2.clearStorage();
Info<< table2 << nl;
table1 =
{
{"aca", 3.0},
{"aaw", 6.0},
{"abc", 3.0},
{"def", 6.0},
{"acr", 8.0},
{"aec", 10.0}
};
@ -195,6 +196,25 @@ int main()
// List<scalar> table1vals(table1.begin(), table1.end());
// wordList table1keys(table1.begin(), table1.end());
Info<< "\nFrom table1: " << flatOutput(table1.sortedToc()) << nl
<< "retain keys: " << flatOutput(table3.sortedToc()) << nl;
table1.retain(table3);
Info<< "-> " << flatOutput(table1.sortedToc()) << nl;
Info<< "Lookup non-existent" << nl;
Info<< table1.lookup("missing-const", 1.2345e+6)
<< " // const-access" << nl;
Info<< table1("missing-inadvertent", 3.14159)
<< " // (inadvertent?) non-const access" << nl;
Info<< table1("missing-autovivify")
<< " // Known auto-vivification (non-const access)" << nl;
Info<<"\ntable1: " << table1 << endl;
Info<< "\nDone\n";
return 0;

View File

@ -227,16 +227,9 @@ void Foam::HashSet<Key, Hash>::operator|=(const HashSet<Key, Hash>& rhs)
template<class Key, class Hash>
void Foam::HashSet<Key, Hash>::operator&=(const HashSet<Key, Hash>& rhs)
inline void Foam::HashSet<Key, Hash>::operator&=(const HashSet<Key, Hash>& rhs)
{
// Remove elements not also found in rhs
for (iterator iter = this->begin(); iter != this->end(); ++iter)
{
if (!rhs.found(iter.key()))
{
this->erase(iter);
}
}
this->parent_type::retain(rhs);
}
@ -259,13 +252,9 @@ void Foam::HashSet<Key, Hash>::operator^=(const HashSet<Key, Hash>& rhs)
template<class Key, class Hash>
void Foam::HashSet<Key, Hash>::operator-=(const HashSet<Key, Hash>& rhs)
inline void Foam::HashSet<Key, Hash>::operator-=(const HashSet<Key, Hash>& rhs)
{
// Remove rhs elements from lhs
for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
{
this->erase(iter.key());
}
this->parent_type::erase(rhs);
}

View File

@ -277,7 +277,7 @@ public:
void operator|=(const HashSet<Key, Hash>& rhs);
//- Only retain entries found in both HashSets
void operator&=(const HashSet<Key, Hash>& rhs);
inline void operator&=(const HashSet<Key, Hash>& rhs);
//- Only retain unique entries (xor)
void operator^=(const HashSet<Key, Hash>& rhs);
@ -289,7 +289,7 @@ public:
}
//- Remove entries listed in the given HashSet from this HashSet
void operator-=(const HashSet<Key, Hash>& rhs);
inline void operator-=(const HashSet<Key, Hash>& rhs);
// IOstream Operator

View File

@ -257,7 +257,7 @@ template<class T, class Key, class Hash>
bool Foam::HashTable<T, Key, Hash>::set
(
const Key& key,
const T& newEntry,
const T& obj,
const bool protect
)
{
@ -284,7 +284,7 @@ bool Foam::HashTable<T, Key, Hash>::set
if (!existing)
{
// Not found, insert it at the head
table_[hashIdx] = new hashedEntry(key, newEntry, table_[hashIdx]);
table_[hashIdx] = new hashedEntry(key, obj, table_[hashIdx]);
nElmts_++;
if (double(nElmts_)/tableSize_ > 0.8 && tableSize_ < maxTableSize)
@ -316,7 +316,7 @@ bool Foam::HashTable<T, Key, Hash>::set
{
// Found - overwrite existing entry
// this corresponds to the Perl convention
hashedEntry* ep = new hashedEntry(key, newEntry, existing->next_);
hashedEntry* ep = new hashedEntry(key, obj, existing->next_);
// Replace existing element - within list or insert at the head
if (prev)
@ -450,15 +450,15 @@ Foam::label Foam::HashTable<T, Key, Hash>::erase
const HashTable<AnyType, Key, AnyHash>& other
)
{
// Remove other keys from this table
const label nTotal = this->size();
label changed = 0;
if (other.size() < nTotal)
using other_iter =
typename HashTable<AnyType, Key, AnyHash>::const_iterator;
if (other.size() <= nTotal)
{
// other is smaller, use its keys for removal
using other_iter =
typename HashTable<AnyType, Key, AnyHash>::const_iterator;
// The other is smaller/same-size, use its keys for removal
for
(
@ -475,7 +475,7 @@ Foam::label Foam::HashTable<T, Key, Hash>::erase
}
else
{
// other is same/larger: iterate ourselves and check for key in other
// We are smaller: remove if found in the other hash
for
(
iterator iter = begin();
@ -494,6 +494,39 @@ Foam::label Foam::HashTable<T, Key, Hash>::erase
}
template<class T, class Key, class Hash>
template<class AnyType, class AnyHash>
Foam::label Foam::HashTable<T, Key, Hash>::retain
(
const HashTable<AnyType, Key, AnyHash>& other
)
{
const label nTotal = this->size();
label changed = 0;
if (other.empty())
{
// Trivial case
changed = nTotal;
this->clear();
}
else
{
// Inverted logic: remove if *not* found in the other hash
for (iterator iter = begin(); iter != end(); ++iter)
{
if (!other.found(iter.key()) && erase(iter))
{
++changed;
}
}
}
return changed;
}
template<class T, class Key, class Hash>
void Foam::HashTable<T, Key, Hash>::resize(const label sz)
{

View File

@ -44,6 +44,7 @@ Note
SourceFiles
HashTableI.H
HashTable.C
HashTableCoreI.H
HashTableCore.C
HashTableIO.C
@ -247,7 +248,7 @@ private:
//- Assign a new hash-entry to a possibly already existing key.
// Return true if the new entry was set.
bool set(const Key& key, const T& newEntry, const bool protect);
bool set(const Key& key, const T& obj, const bool protect);
protected:
@ -307,17 +308,20 @@ public:
//- Return true if the hash table is empty
inline bool empty() const;
//- Return true if hashedEntry is found in table
//- Return true if hashed entry is found in table
bool found(const Key& key) const;
//- Find and return an iterator set at the hashedEntry
//- Find and return an iterator set at the hashed entry
// If not found iterator = end()
iterator find(const Key& key);
//- Find and return an const_iterator set at the hashedEntry
//- Find and return an const_iterator set at the hashed entry
// If not found iterator = end()
const_iterator find(const Key& key) const;
//- Return hashed entry if it exists, or return the given default
inline const T& lookup(const Key& key, const T& deflt) const;
//- Return the table of contents
List<Key> toc() const;
@ -327,42 +331,50 @@ public:
// Edit
//- Insert a new hashedEntry
//- Insert a new entry
// Return true if the entry inserted, which means that it did
// not previously exist in the table.
inline bool insert(const Key& key, const T& newEntry);
inline bool insert(const Key& key, const T& obj);
//- Assign a new hashedEntry, overwriting existing entries.
//- Assign a new entry, overwriting existing entries.
// Returns true.
inline bool set(const Key& key, const T& newEntry);
inline bool set(const Key& key, const T& obj);
//- Erase a hashedEntry specified by given iterator
//- Erase an entry specified by given iterator
// This invalidates the iterator until the next ++ operation
bool erase(const iterator& iter);
//- Erase a hashedEntry specified by the given key
//- Erase an entry specified by the given key
bool erase(const Key& key);
//- Remove entries given by the listed keys from this HashTable
//- Remove table entries given by the listed keys
// Return the number of elements removed
label erase(const UList<Key>& keys);
//- Remove entries given by the listed keys from this HashTable
//- Remove table entries given by the listed keys
// Return the number of elements removed
template<unsigned Size>
label erase(const FixedList<Key, Size>& keys);
//- Remove entries given by the listed keys from this HashTable
//- Remove table entries given by the listed keys
// Return the number of elements removed
label erase(std::initializer_list<Key> keys);
//- Remove entries given by the given keys from this HashTable
//- Remove table entries given by keys of the other hash-table.
// Return the number of elements removed.
// The parameter HashTable needs the same type of key, but the
//
// The other hash-table must have the same type of key, but the
// type of values held and the hashing function are arbitrary.
template<class AnyType, class AnyHash>
label erase(const HashTable<AnyType, Key, AnyHash>& other);
//- Retain table entries given by keys of the other hash-table.
//
// The other hash-table must have the same type of key, but the
// type of values held and the hashing function are arbitrary.
template<class AnyType, class AnyHash>
label retain(const HashTable<AnyType, Key, AnyHash>& other);
//- Resize the hash table for efficiency
void resize(const label sz);
@ -383,10 +395,10 @@ public:
// Member Operators
//- Find and return a hashedEntry
//- Find and return a hashed entry. FatalError if it does not exist.
inline T& operator[](const Key& key);
//- Find and return a hashedEntry
//- Find and return a hashed entry. FatalError if it does not exist.
inline const T& operator[](const Key& key) const;
//- Return existing entry or create a new entry.
@ -394,6 +406,12 @@ public:
// value-initialized. For primitives, this will be zero.
inline T& operator()(const Key& key);
//- Return existing entry or insert a new entry.
inline T& operator()(const Key& key, const T& deflt);
//- Return hashed entry if it exists, or return the given default
inline const T& operator()(const Key& key, const T& deflt) const;
//- Assignment
void operator=(const HashTable<T, Key, Hash>& rhs);

View File

@ -79,10 +79,10 @@ template<class T, class Key, class Hash>
inline bool Foam::HashTable<T, Key, Hash>::insert
(
const Key& key,
const T& newEntry
const T& obj
)
{
return this->set(key, newEntry, true);
return this->set(key, obj, true);
}
@ -90,10 +90,10 @@ template<class T, class Key, class Hash>
inline bool Foam::HashTable<T, Key, Hash>::set
(
const Key& key,
const T& newEntry
const T& obj
)
{
return this->set(key, newEntry, false);
return this->set(key, obj, false);
}
@ -105,6 +105,18 @@ Foam::HashTable<T, Key, Hash>::xfer()
}
template<class T, class Key, class Hash>
inline const T& Foam::HashTable<T, Key, Hash>::lookup
(
const Key& key,
const T& deflt
) const
{
const_iterator iter = this->find(key);
return iter.found() ? iter.object() : deflt;
}
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
@ -156,6 +168,36 @@ inline T& Foam::HashTable<T, Key, Hash>::operator()(const Key& key)
}
template<class T, class Key, class Hash>
inline T& Foam::HashTable<T, Key, Hash>::operator()
(
const Key& key,
const T& deflt
)
{
iterator iter = this->find(key);
if (iter.found())
{
return iter.object();
}
this->insert(key, deflt);
return find(key).object();
}
template<class T, class Key, class Hash>
inline const T& Foam::HashTable<T, Key, Hash>::operator()
(
const Key& key,
const T& deflt
) const
{
return this->lookup(key, deflt);
}
// * * * * * * * * * * * * * * * iterator base * * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>

View File

@ -203,7 +203,7 @@ template<class T, class Key, class Hash>
bool Foam::StaticHashTable<T, Key, Hash>::set
(
const Key& key,
const T& newEntry,
const T& obj,
const bool protect
)
{
@ -229,7 +229,7 @@ bool Foam::StaticHashTable<T, Key, Hash>::set
localObjects.setSize(existing+1);
localKeys[existing] = key;
localObjects[existing] = newEntry;
localObjects[existing] = obj;
nElmts_++;
}
@ -250,7 +250,7 @@ bool Foam::StaticHashTable<T, Key, Hash>::set
{
// Found - overwrite existing entry
// this corresponds to the Perl convention
objects_[hashIdx][existing] = newEntry;
objects_[hashIdx][existing] = obj;
}
return true;

View File

@ -121,10 +121,10 @@ class StaticHashTable
//- Return the hash index of the Key within the current table size.
// No checks for zero-sized tables.
inline label hashKeyIndex(const Key&) const;
inline label hashKeyIndex(const Key& key) const;
//- Assign a new hashed entry to a possibly already existing key
bool set(const Key&, const T& newElmt, bool protect);
bool set(const Key& key, const T& obj, bool protect);
public:

View File

@ -26,8 +26,6 @@ License
#include "error.H"
#include "IOstreams.H"
// * * * * * * * * * * * * * Private Member Classes * * * * * * * * * * * * //
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
template<class T, class Key, class Hash>
@ -59,10 +57,10 @@ template<class T, class Key, class Hash>
inline bool Foam::StaticHashTable<T, Key, Hash>::insert
(
const Key& key,
const T& newEntry
const T& obj
)
{
return set(key, newEntry, true);
return set(key, obj, true);
}
@ -70,10 +68,10 @@ template<class T, class Key, class Hash>
inline bool Foam::StaticHashTable<T, Key, Hash>::set
(
const Key& key,
const T& newEntry
const T& obj
)
{
return set(key, newEntry, false);
return set(key, obj, false);
}