HashTbl: improve efficiency when deleting many keys and in operator==

- fix off by one error in erase/increment code
This commit is contained in:
Mark Olesen
2009-10-31 00:03:02 +01:00
parent 946aac500a
commit a61c03a6a4
3 changed files with 35 additions and 32 deletions

View File

@ -341,10 +341,15 @@ bool Foam::HashTbl<T, Key, Hash>::set
} }
// NOTE:
// We use (const iterator&) here, but manipulate its contents anyhow.
// The parameter should be (iterator&), but then the compiler doesn't find
// it correctly and tries to call as (iterator) instead.
//
template<class T, class Key, class Hash> template<class T, class Key, class Hash>
bool Foam::HashTbl<T, Key, Hash>::erase(const iterator& cit) bool Foam::HashTbl<T, Key, Hash>::erase(const iterator& cit)
{ {
// note: endIter_ has NULL elmtPtr_, so this also catches that // note: elmtPtr_ is NULL for end(), so this catches that too
if (cit.elmtPtr_) if (cit.elmtPtr_)
{ {
// Search element before elmtPtr_ // Search element before elmtPtr_
@ -378,8 +383,14 @@ bool Foam::HashTbl<T, Key, Hash>::erase(const iterator& cit)
// assign an non-NULL value so it doesn't look like end()/cend() // assign an non-NULL value so it doesn't look like end()/cend()
iter.elmtPtr_ = reinterpret_cast<hashedEntry*>(this); iter.elmtPtr_ = reinterpret_cast<hashedEntry*>(this);
// mark with special hashIndex value to signal it has been rewound // Mark with special hashIndex value to signal it has been rewound.
// the next increment will bring it back to the present location // The next increment will bring it back to the present location.
//
// For the current position 'X', mark it as '-(X+1)', which is
// written as '-X-1' to avoid overflow.
// The extra '-1' is needed to avoid ambiguity for position '0'.
// To retrieve the previous position 'X-1' we would later
// use '-(-X-1) - 2'
iter.hashIndex_ = -iter.hashIndex_ - 1; iter.hashIndex_ = -iter.hashIndex_ - 1;
} }
@ -413,6 +424,7 @@ bool Foam::HashTbl<T, Key, Hash>::erase(const iterator& cit)
template<class T, class Key, class Hash> template<class T, class Key, class Hash>
bool Foam::HashTbl<T, Key, Hash>::erase(const Key& key) bool Foam::HashTbl<T, Key, Hash>::erase(const Key& key)
{ {
// we could also simply do: erase(find(key));
iterator fnd = find(key); iterator fnd = find(key);
if (fnd != end()) if (fnd != end())
@ -429,19 +441,17 @@ bool Foam::HashTbl<T, Key, Hash>::erase(const Key& key)
template<class T, class Key, class Hash> template<class T, class Key, class Hash>
Foam::label Foam::HashTbl<T, Key, Hash>::erase(const UList<Key>& keys) Foam::label Foam::HashTbl<T, Key, Hash>::erase(const UList<Key>& keys)
{ {
const label nTotal = nElmts_;
label count = 0; label count = 0;
// Remove listed keys from this table // Remove listed keys from this table - terminates early if possible
if (this->size()) for (label keyI = 0; count < nTotal && keyI < keys.size(); ++keyI)
{
forAll(keys, keyI)
{ {
if (erase(keys[keyI])) if (erase(keys[keyI]))
{ {
count++; count++;
} }
} }
}
return count; return count;
} }
@ -456,10 +466,8 @@ Foam::label Foam::HashTbl<T, Key, Hash>::erase
{ {
label count = 0; label count = 0;
// Remove rhs elements from this table // Remove rhs keys from this table - terminates early if possible
if (this->size()) // Could optimize depending on which hash is smaller ...
{
// NOTE: could further optimize depending on which hash is smaller
for (iterator iter = begin(); iter != end(); ++iter) for (iterator iter = begin(); iter != end(); ++iter)
{ {
if (rhs.found(iter.key()) && erase(iter)) if (rhs.found(iter.key()) && erase(iter))
@ -467,7 +475,6 @@ Foam::label Foam::HashTbl<T, Key, Hash>::erase
count++; count++;
} }
} }
}
return count; return count;
} }
@ -618,18 +625,12 @@ bool Foam::HashTbl<T, Key, Hash>::operator==
const HashTbl<T, Key, Hash>& rhs const HashTbl<T, Key, Hash>& rhs
) const ) const
{ {
// Are all my elements in rhs? // sizes (ie, number of keys) must match
for (const_iterator iter = cbegin(); iter != cend(); ++iter) if (size() != rhs.size())
{
const_iterator fnd = rhs.find(iter.key());
if (fnd == rhs.cend() || fnd() != iter())
{ {
return false; return false;
} }
}
// Are all rhs elements in me?
for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter) for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
{ {
const_iterator fnd = find(iter.key()); const_iterator fnd = find(iter.key());
@ -639,6 +640,7 @@ bool Foam::HashTbl<T, Key, Hash>::operator==
return false; return false;
} }
} }
return true; return true;
} }

View File

@ -269,9 +269,8 @@ public:
//- Assignment //- Assignment
void operator=(const HashTbl<T, Key, Hash>&); void operator=(const HashTbl<T, Key, Hash>&);
//- Equality. Two hash tables are equal if all contents of first are //- Equality. Hash tables are equal if the keys and values are equal.
// also in second and vice versa. So does not depend on table size or // Independent of table storage size and table order.
// order!
bool operator==(const HashTbl<T, Key, Hash>&) const; bool operator==(const HashTbl<T, Key, Hash>&) const;
//- The opposite of the equality operation. Takes linear time. //- The opposite of the equality operation. Takes linear time.

View File

@ -261,10 +261,12 @@ inline
typename Foam::HashTbl<T, Key, Hash>::iterator& typename Foam::HashTbl<T, Key, Hash>::iterator&
Foam::HashTbl<T, Key, Hash>::iterator::operator++() Foam::HashTbl<T, Key, Hash>::iterator::operator++()
{ {
// Check for special value (from erase) - this indicates the previous bin // A negative index is a special value from erase
if (hashIndex_ < 0) if (hashIndex_ < 0)
{ {
hashIndex_ = -hashIndex_; // old position 'X' was marked as '-(X+1)'
// but we wish to continue at 'X-1'
hashIndex_ = -hashIndex_ - 2;
} }
else if (elmtPtr_ && elmtPtr_->next_) else if (elmtPtr_ && elmtPtr_->next_)
{ {