HashTable.C 18.7 KB
Newer Older
1 2 3 4
/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
OpenFOAM bot's avatar
OpenFOAM bot committed
5
    \\  /    A nd           | www.openfoam.com
OpenFOAM bot's avatar
OpenFOAM bot committed
6 7
     \\/     M anipulation  |
-------------------------------------------------------------------------------
OpenFOAM bot's avatar
OpenFOAM bot committed
8 9
    Copyright (C) 2011-2016 OpenFOAM Foundation
    Copyright (C) 2017-2019 OpenCFD Ltd.
10 11 12 13
-------------------------------------------------------------------------------
License
    This file is part of OpenFOAM.

14 15 16 17
    OpenFOAM is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
18 19 20 21 22 23 24

    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.

    You should have received a copy of the GNU General Public License
25
    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
26 27 28 29 30 31 32 33

\*---------------------------------------------------------------------------*/

#ifndef HashTable_C
#define HashTable_C

#include "HashTable.H"
#include "List.H"
34
#include "FixedList.H"
35 36 37

// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //

38 39 40 41 42 43 44
template<class T, class Key, class Hash>
Foam::HashTable<T, Key, Hash>::HashTable()
:
    HashTable<T, Key, Hash>(128)
{}


45
template<class T, class Key, class Hash>
46
Foam::HashTable<T, Key, Hash>::HashTable(const label size)
47
:
Mark Olesen's avatar
Mark Olesen committed
48
    HashTableCore(),
49 50
    size_(0),
    capacity_(HashTableCore::canonicalSize(size)),
51
    table_(nullptr)
52
{
53
    if (capacity_)
54
    {
55 56
        table_ = new node_type*[capacity_];
        for (label i=0; i < capacity_; ++i)
57
        {
58
            table_[i] = nullptr;
59 60 61 62 63 64
        }
    }
}


template<class T, class Key, class Hash>
65
Foam::HashTable<T, Key, Hash>::HashTable(const HashTable<T, Key, Hash>& ht)
66
:
67
    HashTable<T, Key, Hash>(ht.capacity_)
68
{
69
    for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter)
70
    {
71
        insert(iter.key(), iter.val());
72 73 74
    }
}

75

76
template<class T, class Key, class Hash>
77
Foam::HashTable<T, Key, Hash>::HashTable(HashTable<T, Key, Hash>&& rhs)
78
:
79 80 81 82
    HashTableCore(),
    size_(rhs.size_),
    capacity_(rhs.capacity_),
    table_(rhs.table_)
83
{
84 85 86
    rhs.size_ = 0;
    rhs.capacity_ = 0;
    rhs.table_ = nullptr;
87 88 89
}


90 91 92
template<class T, class Key, class Hash>
Foam::HashTable<T, Key, Hash>::HashTable
(
93
    std::initializer_list<std::pair<Key, T>> list
94 95
)
:
96
    HashTable<T, Key, Hash>(2*list.size())
97
{
98
    for (const auto& keyval : list)
99
    {
100
        set(keyval.first, keyval.second);
101 102 103 104
    }
}


105 106 107
// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //

template<class T, class Key, class Hash>
108
Foam::HashTable<T, Key, Hash>::~HashTable()
109 110 111 112 113 114 115 116 117 118 119 120
{
    if (table_)
    {
        clear();
        delete[] table_;
    }
}


// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //

template<class T, class Key, class Hash>
121
Foam::List<Key> Foam::HashTable<T, Key, Hash>::toc() const
122
{
123
    List<Key> list(size_);
124
    label count = 0;
125

126
    for (const_iterator iter = cbegin(); iter != cend(); ++iter)
127
    {
128
        list[count++] = iter.key();
129 130
    }

131
    return list;
132 133 134
}


Andrew Heather's avatar
Andrew Heather committed
135 136 137
template<class T, class Key, class Hash>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::sortedToc() const
{
138 139
    List<Key> list(this->toc());
    Foam::sort(list);
Andrew Heather's avatar
Andrew Heather committed
140

141
    return list;
142 143 144
}


145 146 147 148 149 150 151
template<class T, class Key, class Hash>
template<class Compare>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::sortedToc
(
    const Compare& comp
) const
{
152 153
    List<Key> list(this->toc());
    Foam::sort(list, comp);
154

155
    return list;
156 157 158
}


159 160 161 162 163 164 165 166
template<class T, class Key, class Hash>
template<class UnaryPredicate>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocKeys
(
    const UnaryPredicate& pred,
    const bool invert
) const
{
167
    List<Key> list(size_);
168 169 170 171 172 173
    label count = 0;

    for (const_iterator iter = cbegin(); iter != cend(); ++iter)
    {
        if ((pred(iter.key()) ? !invert : invert))
        {
174
            list[count++] = iter.key();
175 176 177
        }
    }

178
    list.resize(count);
179
    Foam::sort(list);
180

181
    return list;
182 183 184 185 186 187 188 189 190 191 192
}


template<class T, class Key, class Hash>
template<class UnaryPredicate>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocValues
(
    const UnaryPredicate& pred,
    const bool invert
) const
{
193
    List<Key> list(size_);
194 195 196 197
    label count = 0;

    for (const_iterator iter = cbegin(); iter != cend(); ++iter)
    {
198
        if ((pred(iter.val()) ? !invert : invert))
199
        {
200
            list[count++] = iter.key();
201 202 203
        }
    }

204
    list.resize(count);
205
    Foam::sort(list);
206

207
    return list;
208 209 210 211 212 213 214 215 216 217 218
}


template<class T, class Key, class Hash>
template<class BinaryPredicate>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocEntries
(
    const BinaryPredicate& pred,
    const bool invert
) const
{
219
    List<Key> list(size_);
220 221 222 223
    label count = 0;

    for (const_iterator iter = cbegin(); iter != cend(); ++iter)
    {
224
        if ((pred(iter.key(), iter.val()) ? !invert : invert))
225
        {
226
            list[count++] = iter.key();
227 228 229
        }
    }

230
    list.resize(count);
231
    Foam::sort(list);
232

233
    return list;
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
}


template<class T, class Key, class Hash>
template<class UnaryPredicate>
Foam::label Foam::HashTable<T, Key, Hash>::countKeys
(
    const UnaryPredicate& pred,
    const bool invert
) const
{
    label count = 0;

    for (const_iterator iter = cbegin(); iter != cend(); ++iter)
    {
        if ((pred(iter.key()) ? !invert : invert))
        {
            ++count;
        }
    }

    return count;
}


template<class T, class Key, class Hash>
template<class UnaryPredicate>
Foam::label Foam::HashTable<T, Key, Hash>::countValues
(
    const UnaryPredicate& pred,
    const bool invert
) const
{
    label count = 0;

    for (const_iterator iter = cbegin(); iter != cend(); ++iter)
    {
271
        if ((pred(iter.val()) ? !invert : invert))
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
        {
            ++count;
        }
    }

    return count;
}


template<class T, class Key, class Hash>
template<class BinaryPredicate>
Foam::label Foam::HashTable<T, Key, Hash>::countEntries
(
    const BinaryPredicate& pred,
    const bool invert
) const
{
    label count = 0;

    for (const_iterator iter = cbegin(); iter != cend(); ++iter)
    {
293
        if ((pred(iter.key(), iter.val()) ? !invert : invert))
294 295 296 297 298 299
        {
            ++count;
        }
    }

    return count;
Andrew Heather's avatar
Andrew Heather committed
300 301 302
}


303
template<class T, class Key, class Hash>
304
template<class... Args>
305
bool Foam::HashTable<T, Key, Hash>::setEntry
306
(
307
    const bool overwrite,
308
    const Key& key,
309
    Args&&... args
310
)
311
{
312
    if (!capacity_)
313 314 315 316
    {
        resize(2);
    }

317
    const label index = hashKeyIndex(key);
Mark Olesen's avatar
Mark Olesen committed
318

319 320
    node_type* curr = nullptr;
    node_type* prev = nullptr;
321

322
    for (node_type* ep = table_[index]; ep; ep = ep->next_)
323
    {
324
        if (key == ep->key())
325
        {
326
            curr = ep;
327 328
            break;
        }
329
        prev = ep;
330
    }
331

332
    if (!curr)
333
    {
334
        // Not found, insert it at the head
335 336
        table_[index] =
            new node_type(table_[index], key, std::forward<Args>(args)...);
337

338
        ++size_;
339
        if (double(size_)/capacity_ > 0.8 && capacity_ < maxTableSize)
340
        {
341
            #ifdef FULLDEBUG
342
            DebugInFunction << "Doubling table size\n";
343
            #endif
344

345
            resize(2*capacity_);
346 347
        }
    }
348
    else if (overwrite)
349
    {
350
        // Overwrite current entry (Perl convention).
351

352 353 354 355 356 357 358
        // Can skip if the value is not stored anyhow (Eg, HashSet)
        // - this avoids a useless delete/new
        if (!node_type::stores_value())
        {
            return true;
        }

359
        node_type* ep = curr->next_;  // next in the linked list
360

361 362 363
        // In some cases the delete/new could be avoided in favour of move
        // assignment, but cannot be certain that all objects support this
        // or that it behaves the same as a copy construct.
364

365
        delete curr;
366
        ep = new node_type(ep, key, std::forward<Args>(args)...);
367

368
        // Replace current element - within list or insert at the head
369
        if (prev)
370
        {
371
            prev->next_ = ep;
372 373 374
        }
        else
        {
375
            table_[index] = ep;
376 377 378 379
        }
    }
    else
    {
380 381
        // Do not overwrite existing entry (STL 'insert' convention)
        #ifdef FULLDEBUG
382
        DebugInFunction << "Not inserting " << key << ": already in table\n";
383
        #endif
384 385
        return false;
    }
386 387

    return true;
388 389 390 391
}


template<class T, class Key, class Hash>
Mark Olesen's avatar
Mark Olesen committed
392
bool Foam::HashTable<T, Key, Hash>::erase(const iterator& iter)
393
{
394 395
    // NOTE: we use (const iterator&) here, but treat its contents as mutable.
    //
396 397
    // The parameter should be (iterator&), but then the compiler doesn't find
    // it correctly and tries to call as (iterator) instead.
398 399 400 401

    iterator& it = const_cast<iterator&>(iter);

    return iterator_erase(it.entry_, it.index_);
Mark Olesen's avatar
Mark Olesen committed
402
}
403

Mark Olesen's avatar
Mark Olesen committed
404 405 406 407

template<class T, class Key, class Hash>
bool Foam::HashTable<T, Key, Hash>::erase(const Key& key)
{
408 409
    auto iter = find(key);
    return erase(iter);
410 411 412
}


413
template<class T, class Key, class Hash>
414 415 416 417 418 419
template<class InputIter>
inline Foam::label Foam::HashTable<T, Key, Hash>::erase
(
    InputIter first,
    InputIter last
)
420
{
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
    label changed = 0;

    for
    (
        const label nTotal = this->size();
        changed < nTotal && first != last; // terminate early
        ++first
    )
    {
        if (this->erase(*first))
        {
            ++changed;
        }
    }

    return changed;
}


template<class T, class Key, class Hash>
inline Foam::label Foam::HashTable<T, Key, Hash>::erase
(
    std::initializer_list<Key> keys
)
{
    return erase(keys.begin(), keys.end());
447
}
448 449


450
template<class T, class Key, class Hash>
451
template<unsigned N>
452
inline Foam::label Foam::HashTable<T, Key, Hash>::erase
453
(
454
    const FixedList<Key, N>& keys
455 456
)
{
457
    return erase(keys.cbegin(), keys.cend());
458 459 460 461
}


template<class T, class Key, class Hash>
462
inline Foam::label Foam::HashTable<T, Key, Hash>::erase
463
(
464
    const UList<Key>& keys
465 466
)
{
467
    return erase(keys.cbegin(), keys.cend());
468 469 470 471
}


template<class T, class Key, class Hash>
Mark Olesen's avatar
Mark Olesen committed
472
template<class AnyType, class AnyHash>
473 474
Foam::label Foam::HashTable<T, Key, Hash>::erase
(
Mark Olesen's avatar
Mark Olesen committed
475
    const HashTable<AnyType, Key, AnyHash>& other
476 477
)
{
478 479
    const label nTotal = this->size();
    label changed = 0;
480

481
    if (other.size() <= nTotal)
Mark Olesen's avatar
Mark Olesen committed
482
    {
483
        // The other is smaller/same-size, use its keys for removal
484

Mark Olesen's avatar
Mark Olesen committed
485 486
        for
        (
487 488
            auto iter = other.cbegin();
            changed < nTotal && iter != other.cend(); // Terminate early
Mark Olesen's avatar
Mark Olesen committed
489 490 491 492 493 494 495 496 497 498
            ++iter
        )
        {
            if (erase(iter.key()))
            {
                ++changed;
            }
        }
    }
    else
499
    {
500
        // We are smaller: remove if found in the other hash
Mark Olesen's avatar
Mark Olesen committed
501 502 503
        for
        (
            iterator iter = begin();
504
            changed < nTotal && iter != end(); // Terminate early
Mark Olesen's avatar
Mark Olesen committed
505 506
            ++iter
        )
507
        {
Mark Olesen's avatar
Mark Olesen committed
508 509 510 511
            if (other.found(iter.key()) && erase(iter))
            {
                ++changed;
            }
512 513
        }
    }
Mark Olesen's avatar
Mark Olesen committed
514

515
    return changed;
516 517 518
}


519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
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;
}


552
template<class T, class Key, class Hash>
Mark Olesen's avatar
Mark Olesen committed
553
void Foam::HashTable<T, Key, Hash>::resize(const label sz)
554
{
555 556
    const label newCapacity = HashTableCore::canonicalSize(sz);
    const label oldCapacity = capacity_;
Mark Olesen's avatar
Mark Olesen committed
557

558
    if (newCapacity == oldCapacity)
559
    {
560
        #ifdef FULLDEBUG
561
        DebugInFunction << "New table size == old table size\n";
562
        #endif
563 564 565

        return;
    }
566 567 568 569 570 571
    else if (!newCapacity)
    {
        // Special treatment for resize(0)
        if (size_)
        {
            WarningInFunction
572
                << "HashTable contains " << size_ << " cannot resize(0)" << nl;
573 574 575 576 577 578 579 580
        }
        else
        {
            if (table_)
            {
                delete[] table_;
                capacity_ = 0;
            }
581

582 583
            table_ = nullptr;
        }
584

585 586 587 588 589 590 591 592 593 594
        return;
    }

    // Swap primary table entries: size_ is left untouched

    auto oldTable = table_;
    capacity_ = newCapacity;

    table_ = new node_type*[capacity_];
    for (label i=0; i < capacity_; ++i)
595
    {
596
        table_[i] = nullptr;
597 598
    }

599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
    // Move to new table[] but with new chaining.

    label nMove = size_;  // Allow early completion
    for (label i=0; nMove && i < oldCapacity; ++i)
    {
        for (node_type* ep = oldTable[i]; ep; /*nil*/)
        {
            node_type* next = ep->next_;

            // Move to new location
            {
                const label newIdx = hashKeyIndex(ep->key());

                ep->next_ = table_[newIdx];  // add to head
                table_[newIdx] = ep;
            }
615

616 617 618 619 620
            ep = next;  // continue in the linked-list
            --nMove;    // note any early completion
        }
        oldTable[i] = nullptr;
    }
621

622 623 624 625
    if (oldTable)
    {
        delete[] oldTable;
    }
626 627 628 629
}


template<class T, class Key, class Hash>
630
void Foam::HashTable<T, Key, Hash>::clear()
631
{
632
    for (label i=0; size_ && i<capacity_; ++i)
633
    {
634
        for (node_type* ep = table_[i]; ep; /*nil*/)
635
        {
636 637 638 639 640 641
            node_type* next = ep->next_;

            delete ep;

            ep = next;  // continue in the linked-list
            --size_;    // note any early completion
642
        }
643
        table_[i] = nullptr;
644 645 646 647
    }
}


648
template<class T, class Key, class Hash>
649
void Foam::HashTable<T, Key, Hash>::clearStorage()
650 651 652 653 654 655
{
    clear();
    resize(0);
}


656
template<class T, class Key, class Hash>
657
void Foam::HashTable<T, Key, Hash>::swap(HashTable<T, Key, Hash>& rhs)
658
{
659 660 661 662 663
    if (this == &rhs)
    {
        return;  // Self-swap is a no-op
    }

664 665 666
    Foam::Swap(size_, rhs.size_);
    Foam::Swap(capacity_, rhs.capacity_);
    Foam::Swap(table_, rhs.table_);
667 668 669
}


670
template<class T, class Key, class Hash>
671
void Foam::HashTable<T, Key, Hash>::transfer(HashTable<T, Key, Hash>& rhs)
672
{
673 674 675 676 677
    if (this == &rhs)
    {
        return;  // Self-assignment is a no-op
    }

678 679
    clear();
    swap(rhs);
680 681 682
}


683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
template<class T, class Key, class Hash>
template<class UnaryPredicate>
Foam::label Foam::HashTable<T, Key, Hash>::filterKeys
(
    const UnaryPredicate& pred,
    const bool pruning
)
{
    label changed = 0;

    for (iterator iter = begin(); iter != end(); ++iter)
    {
        // Matches? either prune (pruning) or keep (!pruning)
        if
        (
            (pred(iter.key()) ? pruning : !pruning)
         && erase(iter)
        )
        {
            ++changed;
        }
    }

    return changed;
}


template<class T, class Key, class Hash>
template<class UnaryPredicate>
Foam::label Foam::HashTable<T, Key, Hash>::filterValues
(
    const UnaryPredicate& pred,
    const bool pruning
)
{
    label changed = 0;

    for (iterator iter = begin(); iter != end(); ++iter)
    {
        // Matches? either prune (pruning) or keep (!pruning)
        if
        (
725
            (pred(iter.val()) ? pruning : !pruning)
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
         && erase(iter)
        )
        {
            ++changed;
        }
    }

    return changed;
}


template<class T, class Key, class Hash>
template<class BinaryPredicate>
Foam::label Foam::HashTable<T, Key, Hash>::filterEntries
(
    const BinaryPredicate& pred,
    const bool pruning
)
{
    label changed = 0;

    for (iterator iter = begin(); iter != end(); ++iter)
    {
        // Matches? either prune (pruning) or keep (!pruning)
        if
        (
752
            (pred(iter.key(), iter.val()) ? pruning : !pruning)
753 754 755 756 757 758 759 760 761 762 763
         && erase(iter)
        )
        {
            ++changed;
        }
    }

    return changed;
}


764 765 766
// * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //

template<class T, class Key, class Hash>
767 768 769 770
void Foam::HashTable<T, Key, Hash>::operator=
(
    const HashTable<T, Key, Hash>& rhs
)
771
{
772
    if (this == &rhs)
773
    {
774
        return;  // Self-assignment is a no-op
775 776
    }

777
    if (!capacity_)
778
    {
779
        // Zero-sized from a previous transfer()?
780
        resize(rhs.capacity_);
781 782 783 784 785
    }
    else
    {
        clear();
    }
786

787
    for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
788
    {
789
        insert(iter.key(), iter.val());
790 791 792 793
    }
}


794 795 796
template<class T, class Key, class Hash>
void Foam::HashTable<T, Key, Hash>::operator=
(
797
    std::initializer_list<std::pair<Key, T>> rhs
798 799
)
{
800
    if (!capacity_)
801
    {
802
        // Zero-sized from a previous transfer()?
803
        resize(2*rhs.size());
804 805 806 807 808 809
    }
    else
    {
        clear();
    }

810
    for (const auto& keyval : rhs)
811
    {
812
        set(keyval.first, keyval.second);
813 814 815 816
    }
}


817 818 819 820 821 822 823 824
template<class T, class Key, class Hash>
void Foam::HashTable<T, Key, Hash>::operator=
(
    HashTable<T, Key, Hash>&& rhs
)
{
    if (this == &rhs)
    {
825
        return;  // Self-assignment is a no-op
826 827 828 829 830 831
    }

    transfer(rhs);
}


Mattijs Janssens's avatar
Mattijs Janssens committed
832
template<class T, class Key, class Hash>
833 834 835 836
bool Foam::HashTable<T, Key, Hash>::operator==
(
    const HashTable<T, Key, Hash>& rhs
) const
Mattijs Janssens's avatar
Mattijs Janssens committed
837
{
838
    // Sizes (number of keys) must match
Mark Olesen's avatar
Mark Olesen committed
839
    if (size() != rhs.size())
Mattijs Janssens's avatar
Mattijs Janssens committed
840
    {
Mark Olesen's avatar
Mark Olesen committed
841
        return false;
Mattijs Janssens's avatar
Mattijs Janssens committed
842 843
    }

844
    for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
Mattijs Janssens's avatar
Mattijs Janssens committed
845
    {
846
        const const_iterator other(this->cfind(iter.key()));
Mattijs Janssens's avatar
Mattijs Janssens committed
847

848
        if (!other.good() || other.val() != iter.val())
Mattijs Janssens's avatar
Mattijs Janssens committed
849 850 851 852
        {
            return false;
        }
    }
Mark Olesen's avatar
Mark Olesen committed
853

Mattijs Janssens's avatar
Mattijs Janssens committed
854 855 856 857 858
    return true;
}


template<class T, class Key, class Hash>
859 860 861 862
bool Foam::HashTable<T, Key, Hash>::operator!=
(
    const HashTable<T, Key, Hash>& rhs
) const
Mattijs Janssens's avatar
Mattijs Janssens committed
863
{
864
    return !operator==(rhs);
Mattijs Janssens's avatar
Mattijs Janssens committed
865 866 867
}


868 869 870 871 872 873 874 875 876 877 878 879 880
template<class T, class Key, class Hash>
Foam::HashTable<T, Key, Hash>& Foam::HashTable<T, Key, Hash>::operator+=
(
    const HashTable<T, Key, Hash>& rhs
)
{
    // Avoid no-ops:
    if (rhs.size() || this != &rhs)
    {
        if (this->size())
        {
            for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
            {
881
                insert(iter.key(), iter.val());
882 883 884 885 886 887 888 889 890 891 892 893
            }
        }
        else
        {
            (*this) = rhs;
        }
    }

    return *this;
}


894 895 896
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

// Iterators, Friend Operators
897

898
#include "HashTableIter.C"
899 900 901 902 903 904 905
#include "HashTableIO.C"

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

#endif

// ************************************************************************* //