    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.collections4.bidimap;
    018
    019import java.util.Collection;
    020import java.util.Iterator;
    021import java.util.Map;
    022import java.util.Set;
    023import java.util.Objects;
    024import java.util.function.Predicate;
    025
    026import org.apache.commons.collections4.BidiMap;
    027import org.apache.commons.collections4.MapIterator;
    028import org.apache.commons.collections4.ResettableIterator;
    029import org.apache.commons.collections4.collection.AbstractCollectionDecorator;
    030import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
    031import org.apache.commons.collections4.keyvalue.AbstractMapEntryDecorator;
    032
    033/**
    034 * Abstract {@link BidiMap} implemented using two maps.
    035 * <p>
    036 * An implementation can be written simply by implementing the
    037 * {@link #createBidiMap(Map, Map, BidiMap)} method.
    038 * </p>
    039 *
    040 * @param <K> the type of the keys in the map
    041 * @param <V> the type of the values in the map
    042 *
    043 * @see DualHashBidiMap
    044 * @see DualTreeBidiMap
    045 * @since 3.0
    046 */
    047public abstract class AbstractDualBidiMap<K, V> implements BidiMap<K, V> {
    048
    049    /**
    050     * Normal delegate map.
    051     */
    052    transient Map<K, V> normalMap;
    053
    054    /**
    055     * Reverse delegate map.
    056     */
    057    transient Map<V, K> reverseMap;
    058
    059    /**
    060     * Inverse view of this map.
    061     */
    062    transient BidiMap<V, K> inverseBidiMap = null;
    063
    064    /**
    065     * View of the keys.
    066     */
    067    transient Set<K> keySet = null;
    068
    069    /**
    070     * View of the values.
    071     */
    072    transient Set<V> values = null;
    073
    074    /**
    075     * View of the entries.
    076     */
    077    transient Set<Map.Entry<K, V>> entrySet = null;
    078
    079    /**
    080     * Creates an empty map, initialised by <code>createMap</code>.
    081     * <p>
    082     * This constructor remains in place for deserialization.
    083     * All other usage is deprecated in favour of
    084     * {@link #AbstractDualBidiMap(Map, Map)}.
    085     */
    086    protected AbstractDualBidiMap() {
    087        super();
    088    }
    089
    090    /**
    091     * Creates an empty map using the two maps specified as storage.
    092     * <p>
    093     * The two maps must be a matching pair, normal and reverse.
    094     * They will typically both be empty.
    095     * <p>
    096     * Neither map is validated, so nulls may be passed in.
    097     * If you choose to do this then the subclass constructor must populate
    098     * the <code>maps[]</code> instance variable itself.
    099     *
    100     * @param normalMap  the normal direction map
    101     * @param reverseMap  the reverse direction map
    102     * @since 3.1
    103     */
    104    protected AbstractDualBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap) {
    105        super();
    106        this.normalMap = normalMap;
    107        this.reverseMap = reverseMap;
    108    }
    109
    110    /**
    111     * Constructs a map that decorates the specified maps,
    112     * used by the subclass <code>createBidiMap</code> implementation.
    113     *
    114     * @param normalMap  the normal direction map
    115     * @param reverseMap  the reverse direction map
    116     * @param inverseBidiMap  the inverse BidiMap
    117     */
    118    protected AbstractDualBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap,
    119                                  final BidiMap<V, K> inverseBidiMap) {
    120        super();
    121        this.normalMap = normalMap;
    122        this.reverseMap = reverseMap;
    123        this.inverseBidiMap = inverseBidiMap;
    124    }
    125
    126    /**
    127     * Creates a new instance of the subclass.
    128     *
    129     * @param normalMap  the normal direction map
    130     * @param reverseMap  the reverse direction map
    131     * @param inverseMap  this map, which is the inverse in the new map
    132     * @return the inverse map
    133     */
    134    protected abstract BidiMap<V, K> createBidiMap(Map<V, K> normalMap, Map<K, V> reverseMap, BidiMap<K, V> inverseMap);
    135
    136    // Map delegation
    137    //-----------------------------------------------------------------------
    138
    139    @Override
    140    public V get(final Object key) {
    141        return normalMap.get(key);
    142    }
    143
    144    @Override
    145    public int size() {
    146        return normalMap.size();
    147    }
    148
    149    @Override
    150    public boolean isEmpty() {
    151        return normalMap.isEmpty();
    152    }
    153
    154    @Override
    155    public boolean containsKey(final Object key) {
    156        return normalMap.containsKey(key);
    157    }
    158
    159    @Override
    160    public boolean equals(final Object obj) {
    161        return normalMap.equals(obj);
    162    }
    163
    164    @Override
    165    public int hashCode() {
    166        return normalMap.hashCode();
    167    }
    168
    169    @Override
    170    public String toString() {
    171        return normalMap.toString();
    172    }
    173
    174    // BidiMap changes
    175    //-----------------------------------------------------------------------
    176
    177    @Override
    178    public V put(final K key, final V value) {
    179        if (normalMap.containsKey(key)) {
    180            reverseMap.remove(normalMap.get(key));
    181        }
    182        if (reverseMap.containsKey(value)) {
    183            normalMap.remove(reverseMap.get(value));
    184        }
    185        final V obj = normalMap.put(key, value);
    186        reverseMap.put(value, key);
    187        return obj;
    188    }
    189
    190    @Override
    191    public void putAll(final Map<? extends K, ? extends V> map) {
    192        for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
    193            put(entry.getKey(), entry.getValue());
    194        }
    195    }
    196
    197    @Override
    198    public V remove(final Object key) {
    199        V value = null;
    200        if (normalMap.containsKey(key)) {
    201            value = normalMap.remove(key);
    202            reverseMap.remove(value);
    203        }
    204        return value;
    205    }
    206
    207    @Override
    208    public void clear() {
    209        normalMap.clear();
    210        reverseMap.clear();
    211    }
    212
    213    @Override
    214    public boolean containsValue(final Object value) {
    215        return reverseMap.containsKey(value);
    216    }
    217
    218    // BidiMap
    219    //-----------------------------------------------------------------------
    220    /**
    221     * Obtains a <code>MapIterator</code> over the map.
    222     * The iterator implements <code>ResetableMapIterator</code>.
    223     * This implementation relies on the entrySet iterator.
    224     * <p>
    225     * The setValue() methods only allow a new value to be set.
    226     * If the value being set is already in the map, an IllegalArgumentException
    227     * is thrown (as setValue cannot change the size of the map).
    228     *
    229     * @return a map iterator
    230     */
    231    @Override
    232    public MapIterator<K, V> mapIterator() {
    233        return new BidiMapIterator<>(this);
    234    }
    235
    236    @Override
    237    public K getKey(final Object value) {
    238        return reverseMap.get(value);
    239    }
    240
    241    @Override
    242    public K removeValue(final Object value) {
    243        K key = null;
    244        if (reverseMap.containsKey(value)) {
    245            key = reverseMap.remove(value);
    246            normalMap.remove(key);
    247        }
    248        return key;
    249    }
    250
    251    @Override
    252    public BidiMap<V, K> inverseBidiMap() {
    253        if (inverseBidiMap == null) {
    254            inverseBidiMap = createBidiMap(reverseMap, normalMap, this);
    255        }
    256        return inverseBidiMap;
    257    }
    258
    259    // Map views
    260    //-----------------------------------------------------------------------
    261    /**
    262     * Gets a keySet view of the map.
    263     * Changes made on the view are reflected in the map.
    264     * The set supports remove and clear but not add.
    265     *
    266     * @return the keySet view
    267     */
    268    @Override
    269    public Set<K> keySet() {
    270        if (keySet == null) {
    271            keySet = new KeySet<>(this);
    272        }
    273        return keySet;
    274    }
    275
    276    /**
    277     * Creates a key set iterator.
    278     * Subclasses can override this to return iterators with different properties.
    279     *
    280     * @param iterator  the iterator to decorate
    281     * @return the keySet iterator
    282     */
    283    protected Iterator<K> createKeySetIterator(final Iterator<K> iterator) {
    284        return new KeySetIterator<>(iterator, this);
    285    }
    286
    287    /**
    288     * Gets a values view of the map.
    289     * Changes made on the view are reflected in the map.
    290     * The set supports remove and clear but not add.
    291     *
    292     * @return the values view
    293     */
    294    @Override
    295    public Set<V> values() {
    296        if (values == null) {
    297            values = new Values<>(this);
    298        }
    299        return values;
    300    }
    301
    302    /**
    303     * Creates a values iterator.
    304     * Subclasses can override this to return iterators with different properties.
    305     *
    306     * @param iterator  the iterator to decorate
    307     * @return the values iterator
    308     */
    309    protected Iterator<V> createValuesIterator(final Iterator<V> iterator) {
    310        return new ValuesIterator<>(iterator, this);
    311    }
    312
    313    /**
    314     * Gets an entrySet view of the map.
    315     * Changes made on the set are reflected in the map.
    316     * The set supports remove and clear but not add.
    317     * <p>
    318     * The Map Entry setValue() method only allow a new value to be set.
    319     * If the value being set is already in the map, an IllegalArgumentException
    320     * is thrown (as setValue cannot change the size of the map).
    321     *
    322     * @return the entrySet view
    323     */
    324    @Override
    325    public Set<Map.Entry<K, V>> entrySet() {
    326        if (entrySet == null) {
    327            entrySet = new EntrySet<>(this);
    328        }
    329        return entrySet;
    330    }
    331
    332    /**
    333     * Creates an entry set iterator.
    334     * Subclasses can override this to return iterators with different properties.
    335     *
    336     * @param iterator  the iterator to decorate
    337     * @return the entrySet iterator
    338     */
    339    protected Iterator<Map.Entry<K, V>> createEntrySetIterator(final Iterator<Map.Entry<K, V>> iterator) {
    340        return new EntrySetIterator<>(iterator, this);
    341    }
    342
    343    //-----------------------------------------------------------------------
    344    /**
    345     * Inner class View.
    346     */
    347    protected static abstract class View<K, V, E> extends AbstractCollectionDecorator<E> {
    348
    349        /** Generated serial version ID. */
    350        private static final long serialVersionUID = 4621510560119690639L;
    351
    352        /** The parent map */
    353        protected final AbstractDualBidiMap<K, V> parent;
    354
    355        /**
    356         * Constructs a new view of the BidiMap.
    357         *
    358         * @param coll  the collection view being decorated
    359         * @param parent  the parent BidiMap
    360         */
    361        protected View(final Collection<E> coll, final AbstractDualBidiMap<K, V> parent) {
    362            super(coll);
    363            this.parent = parent;
    364        }
    365
    366        @Override
    367        public boolean equals(final Object object) {
    368            return object == this || decorated().equals(object);
    369        }
    370
    371        @Override
    372        public int hashCode() {
    373            return decorated().hashCode();
    374        }
    375
    376        /**
    377         * @since 4.4
    378         */
    379        @Override
    380        public boolean removeIf(Predicate<? super E> filter) {
    381            if (parent.isEmpty() || Objects.isNull(filter)) {
    382                return false;
    383            }
    384            boolean modified = false;
    385            final Iterator<?> it = iterator();
    386            while (it.hasNext()) {
    387                @SuppressWarnings("unchecked")
    388                final E e = (E) it.next();
    389                if (filter.test(e)) {
    390                    it.remove();
    391                    modified = true;
    392                }
    393            }
    394            return modified;
    395        }
    396
    397        @Override
    398        public boolean removeAll(final Collection<?> coll) {
    399            if (parent.isEmpty() || coll.isEmpty()) {
    400                return false;
    401            }
    402            boolean modified = false;
    403            final Iterator<?> it = coll.iterator();
    404            while (it.hasNext()) {
    405                modified |= remove(it.next());
    406            }
    407            return modified;
    408        }
    409
    410        /**
    411         * {@inheritDoc}
    412         * <p>
    413         * This implementation iterates over the elements of this bidi map, checking each element in
    414         * turn to see if it's contained in <code>coll</code>. If it's not contained, it's removed
    415         * from this bidi map. As a consequence, it is advised to use a collection type for
    416         * <code>coll</code> that provides a fast (e.g. O(1)) implementation of
    417         * {@link Collection#contains(Object)}.
    418         */
    419        @Override
    420        public boolean retainAll(final Collection<?> coll) {
    421            if (parent.isEmpty()) {
    422                return false;
    423            }
    424            if (coll.isEmpty()) {
    425                parent.clear();
    426                return true;
    427            }
    428            boolean modified = false;
    429            final Iterator<E> it = iterator();
    430            while (it.hasNext()) {
    431                if (coll.contains(it.next()) == false) {
    432                    it.remove();
    433                    modified = true;
    434                }
    435            }
    436            return modified;
    437        }
    438
    439        @Override
    440        public void clear() {
    441            parent.clear();
    442        }
    443    }
    444
    445    //-----------------------------------------------------------------------
    446    /**
    447     * Inner class KeySet.
    448     */
    449    protected static class KeySet<K> extends View<K, Object, K> implements Set<K> {
    450
    451        /** Serialization version */
    452        private static final long serialVersionUID = -7107935777385040694L;
    453
    454        /**
    455         * Constructs a new view of the BidiMap.
    456         *
    457         * @param parent  the parent BidiMap
    458         */
    459        @SuppressWarnings("unchecked")
    460        protected KeySet(final AbstractDualBidiMap<K, ?> parent) {
    461            super(parent.normalMap.keySet(), (AbstractDualBidiMap<K, Object>) parent);
    462        }
    463
    464        @Override
    465        public Iterator<K> iterator() {
    466            return parent.createKeySetIterator(super.iterator());
    467        }
    468
    469        @Override
    470        public boolean contains(final Object key) {
    471            return parent.normalMap.containsKey(key);
    472        }
    473
    474        @Override
    475        public boolean remove(final Object key) {
    476            if (parent.normalMap.containsKey(key)) {
    477                final Object value = parent.normalMap.remove(key);
    478                parent.reverseMap.remove(value);
    479                return true;
    480            }
    481            return false;
    482        }
    483    }
    484
    485    /**
    486     * Inner class KeySetIterator.
    487     */
    488    protected static class KeySetIterator<K> extends AbstractIteratorDecorator<K> {
    489
    490        /** The parent map */
    491        protected final AbstractDualBidiMap<K, ?> parent;
    492
    493        /** The last returned key */
    494        protected K lastKey = null;
    495
    496        /** Whether remove is allowed at present */
    497        protected boolean canRemove = false;
    498
    499        /**
    500         * Constructor.
    501         * @param iterator  the iterator to decorate
    502         * @param parent  the parent map
    503         */
    504        protected KeySetIterator(final Iterator<K> iterator, final AbstractDualBidiMap<K, ?> parent) {
    505            super(iterator);
    506            this.parent = parent;
    507        }
    508
    509        @Override
    510        public K next() {
    511            lastKey = super.next();
    512            canRemove = true;
    513            return lastKey;
    514        }
    515
    516        @Override
    517        public void remove() {
    518            if (canRemove == false) {
    519                throw new IllegalStateException("Iterator remove() can only be called once after next()");
    520            }
    521            final Object value = parent.normalMap.get(lastKey);
    522            super.remove();
    523            parent.reverseMap.remove(value);
    524            lastKey = null;
    525            canRemove = false;
    526        }
    527    }
    528
    529    //-----------------------------------------------------------------------
    530    /**
    531     * Inner class Values.
    532     */
    533    protected static class Values<V> extends View<Object, V, V> implements Set<V> {
    534
    535        /** Serialization version */
    536        private static final long serialVersionUID = 4023777119829639864L;
    537
    538        /**
    539         * Constructs a new view of the BidiMap.
    540         *
    541         * @param parent  the parent BidiMap
    542         */
    543        @SuppressWarnings("unchecked")
    544        protected Values(final AbstractDualBidiMap<?, V> parent) {
    545            super(parent.normalMap.values(), (AbstractDualBidiMap<Object, V>) parent);
    546        }
    547
    548        @Override
    549        public Iterator<V> iterator() {
    550            return parent.createValuesIterator(super.iterator());
    551        }
    552
    553        @Override
    554        public boolean contains(final Object value) {
    555            return parent.reverseMap.containsKey(value);
    556        }
    557
    558        @Override
    559        public boolean remove(final Object value) {
    560            if (parent.reverseMap.containsKey(value)) {
    561                final Object key = parent.reverseMap.remove(value);
    562                parent.normalMap.remove(key);
    563                return true;
    564            }
    565            return false;
    566        }
    567    }
    568
    569    /**
    570     * Inner class ValuesIterator.
    571     */
    572    protected static class ValuesIterator<V> extends AbstractIteratorDecorator<V> {
    573
    574        /** The parent map */
    575        protected final AbstractDualBidiMap<Object, V> parent;
    576
    577        /** The last returned value */
    578        protected V lastValue = null;
    579
    580        /** Whether remove is allowed at present */
    581        protected boolean canRemove = false;
    582
    583        /**
    584         * Constructor.
    585         * @param iterator  the iterator to decorate
    586         * @param parent  the parent map
    587         */
    588        @SuppressWarnings("unchecked")
    589        protected ValuesIterator(final Iterator<V> iterator, final AbstractDualBidiMap<?, V> parent) {
    590            super(iterator);
    591            this.parent = (AbstractDualBidiMap<Object, V>) parent;
    592        }
    593
    594        @Override
    595        public V next() {
    596            lastValue = super.next();
    597            canRemove = true;
    598            return lastValue;
    599        }
    600
    601        @Override
    602        public void remove() {
    603            if (canRemove == false) {
    604                throw new IllegalStateException("Iterator remove() can only be called once after next()");
    605            }
    606            super.remove(); // removes from maps[0]
    607            parent.reverseMap.remove(lastValue);
    608            lastValue = null;
    609            canRemove = false;
    610        }
    611    }
    612
    613    //-----------------------------------------------------------------------
    614    /**
    615     * Inner class EntrySet.
    616     */
    617    protected static class EntrySet<K, V> extends View<K, V, Map.Entry<K, V>> implements Set<Map.Entry<K, V>> {
    618
    619        /** Serialization version */
    620        private static final long serialVersionUID = 4040410962603292348L;
    621
    622        /**
    623         * Constructs a new view of the BidiMap.
    624         *
    625         * @param parent  the parent BidiMap
    626         */
    627        protected EntrySet(final AbstractDualBidiMap<K, V> parent) {
    628            super(parent.normalMap.entrySet(), parent);
    629        }
    630
    631        @Override
    632        public Iterator<Map.Entry<K, V>> iterator() {
    633            return parent.createEntrySetIterator(super.iterator());
    634        }
    635
    636        @Override
    637        public boolean remove(final Object obj) {
    638            if (obj instanceof Map.Entry == false) {
    639                return false;
    640            }
    641            final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
    642            final Object key = entry.getKey();
    643            if (parent.containsKey(key)) {
    644                final V value = parent.normalMap.get(key);
    645                if (value == null ? entry.getValue() == null : value.equals(entry.getValue())) {
    646                    parent.normalMap.remove(key);
    647                    parent.reverseMap.remove(value);
    648                    return true;
    649                }
    650            }
    651            return false;
    652        }
    653    }
    654
    655    /**
    656     * Inner class EntrySetIterator.
    657     */
    658    protected static class EntrySetIterator<K, V> extends AbstractIteratorDecorator<Map.Entry<K, V>> {
    659
    660        /** The parent map */
    661        protected final AbstractDualBidiMap<K, V> parent;
    662
    663        /** The last returned entry */
    664        protected Map.Entry<K, V> last = null;
    665
    666        /** Whether remove is allowed at present */
    667        protected boolean canRemove = false;
    668
    669        /**
    670         * Constructor.
    671         * @param iterator  the iterator to decorate
    672         * @param parent  the parent map
    673         */
    674        protected EntrySetIterator(final Iterator<Map.Entry<K, V>> iterator, final AbstractDualBidiMap<K, V> parent) {
    675            super(iterator);
    676            this.parent = parent;
    677        }
    678
    679        @Override
    680        public Map.Entry<K, V> next() {
    681            last = new MapEntry<>(super.next(), parent);
    682            canRemove = true;
    683            return last;
    684        }
    685
    686        @Override
    687        public void remove() {
    688            if (canRemove == false) {
    689                throw new IllegalStateException("Iterator remove() can only be called once after next()");
    690            }
    691            // store value as remove may change the entry in the decorator (eg.TreeMap)
    692            final Object value = last.getValue();
    693            super.remove();
    694            parent.reverseMap.remove(value);
    695            last = null;
    696            canRemove = false;
    697        }
    698    }
    699
    700    /**
    701     * Inner class MapEntry.
    702     */
    703    protected static class MapEntry<K, V> extends AbstractMapEntryDecorator<K, V> {
    704
    705        /** The parent map */
    706        protected final AbstractDualBidiMap<K, V> parent;
    707
    708        /**
    709         * Constructor.
    710         * @param entry  the entry to decorate
    711         * @param parent  the parent map
    712         */
    713        protected MapEntry(final Map.Entry<K, V> entry, final AbstractDualBidiMap<K, V> parent) {
    714            super(entry);
    715            this.parent = parent;
    716        }
    717
    718        @Override
    719        public V setValue(final V value) {
    720            final K key = MapEntry.this.getKey();
    721            if (parent.reverseMap.containsKey(value) &&
    722                parent.reverseMap.get(value) != key) {
    723                throw new IllegalArgumentException(
    724                        "Cannot use setValue() when the object being set is already in the map");
    725            }
    726            parent.put(key, value);
    727            return super.setValue(value);
    728        }
    729    }
    730
    731    /**
    732     * Inner class MapIterator.
    733     */
    734    protected static class BidiMapIterator<K, V> implements MapIterator<K, V>, ResettableIterator<K> {
    735
    736        /** The parent map */
    737        protected final AbstractDualBidiMap<K, V> parent;
    738
    739        /** The iterator being wrapped */
    740        protected Iterator<Map.Entry<K, V>> iterator;
    741
    742        /** The last returned entry */
    743        protected Map.Entry<K, V> last = null;
    744
    745        /** Whether remove is allowed at present */
    746        protected boolean canRemove = false;
    747
    748        /**
    749         * Constructor.
    750         * @param parent  the parent map
    751         */
    752        protected BidiMapIterator(final AbstractDualBidiMap<K, V> parent) {
    753            super();
    754            this.parent = parent;
    755            this.iterator = parent.normalMap.entrySet().iterator();
    756        }
    757
    758        @Override
    759        public boolean hasNext() {
    760            return iterator.hasNext();
    761        }
    762
    763        @Override
    764        public K next() {
    765            last = iterator.next();
    766            canRemove = true;
    767            return last.getKey();
    768        }
    769
    770        @Override
    771        public void remove() {
    772            if (canRemove == false) {
    773                throw new IllegalStateException("Iterator remove() can only be called once after next()");
    774            }
    775            // store value as remove may change the entry in the decorator (eg.TreeMap)
    776            final V value = last.getValue();
    777            iterator.remove();
    778            parent.reverseMap.remove(value);
    779            last = null;
    780            canRemove = false;
    781        }
    782
    783        @Override
    784        public K getKey() {
    785            if (last == null) {
    786                throw new IllegalStateException(
    787                        "Iterator getKey() can only be called after next() and before remove()");
    788            }
    789            return last.getKey();
    790        }
    791
    792        @Override
    793        public V getValue() {
    794            if (last == null) {
    795                throw new IllegalStateException(
    796                        "Iterator getValue() can only be called after next() and before remove()");
    797            }
    798            return last.getValue();
    799        }
    800
    801        @Override
    802        public V setValue(final V value) {
    803            if (last == null) {
    804                throw new IllegalStateException(
    805                        "Iterator setValue() can only be called after next() and before remove()");
    806            }
    807            if (parent.reverseMap.containsKey(value) &&
    808                parent.reverseMap.get(value) != last.getKey()) {
    809                throw new IllegalArgumentException(
    810                        "Cannot use setValue() when the object being set is already in the map");
    811            }
    812            return parent.put(last.getKey(), value);
    813        }
    814
    815        @Override
    816        public void reset() {
    817            iterator = parent.normalMap.entrySet().iterator();
    818            last = null;
    819            canRemove = false;
    820        }
    821
    822        @Override
    823        public String toString() {
    824            if (last != null) {
    825                return "MapIterator[" + getKey() + "=" + getValue() + "]";
    826            }
    827            return "MapIterator[]";
    828        }
    829    }
    830
    831}