AbstractMultiValuedMap.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.collections4.multimap;

  18. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.ObjectOutputStream;
  21. import java.util.AbstractCollection;
  22. import java.util.AbstractMap;
  23. import java.util.AbstractSet;
  24. import java.util.ArrayList;
  25. import java.util.Collection;
  26. import java.util.Iterator;
  27. import java.util.Map;
  28. import java.util.Map.Entry;
  29. import java.util.Objects;
  30. import java.util.Set;

  31. import org.apache.commons.collections4.CollectionUtils;
  32. import org.apache.commons.collections4.IteratorUtils;
  33. import org.apache.commons.collections4.MapIterator;
  34. import org.apache.commons.collections4.MultiSet;
  35. import org.apache.commons.collections4.MultiValuedMap;
  36. import org.apache.commons.collections4.Transformer;
  37. import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
  38. import org.apache.commons.collections4.iterators.EmptyMapIterator;
  39. import org.apache.commons.collections4.iterators.IteratorChain;
  40. import org.apache.commons.collections4.iterators.LazyIteratorChain;
  41. import org.apache.commons.collections4.iterators.TransformIterator;
  42. import org.apache.commons.collections4.keyvalue.AbstractMapEntry;
  43. import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry;
  44. import org.apache.commons.collections4.multiset.AbstractMultiSet;
  45. import org.apache.commons.collections4.multiset.UnmodifiableMultiSet;

  46. /**
  47.  * Abstract implementation of the {@link MultiValuedMap} interface to simplify
  48.  * the creation of subclass implementations.
  49.  * <p>
  50.  * Subclasses specify a Map implementation to use as the internal storage.
  51.  * </p>
  52.  *
  53.  * @param <K> the type of the keys in this map
  54.  * @param <V> the type of the values in this map
  55.  * @since 4.1
  56.  */
  57. public abstract class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V> {

  58.     /**
  59.      * Inner class that provides the AsMap view.
  60.      */
  61.     private final class AsMap extends AbstractMap<K, Collection<V>> {
  62.         final class AsMapEntrySet extends AbstractSet<Map.Entry<K, Collection<V>>> {

  63.             @Override
  64.             public void clear() {
  65.                 AsMap.this.clear();
  66.             }

  67.             @Override
  68.             public boolean contains(final Object o) {
  69.                 return map.entrySet().contains(o);
  70.             }

  71.             @Override
  72.             public Iterator<Map.Entry<K, Collection<V>>> iterator() {
  73.                 return new AsMapEntrySetIterator(map.entrySet().iterator());
  74.             }

  75.             @Override
  76.             public boolean remove(final Object o) {
  77.                 if (!contains(o)) {
  78.                     return false;
  79.                 }
  80.                 final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
  81.                 AbstractMultiValuedMap.this.remove(entry.getKey());
  82.                 return true;
  83.             }

  84.             @Override
  85.             public int size() {
  86.                 return AsMap.this.size();
  87.             }
  88.         }

  89.         /**
  90.          * EntrySet iterator for the asMap view.
  91.          */
  92.         final class AsMapEntrySetIterator extends AbstractIteratorDecorator<Map.Entry<K, Collection<V>>> {

  93.             AsMapEntrySetIterator(final Iterator<Map.Entry<K, Collection<V>>> iterator) {
  94.                 super(iterator);
  95.             }

  96.             @Override
  97.             public Map.Entry<K, Collection<V>> next() {
  98.                 final Map.Entry<K, Collection<V>> entry = super.next();
  99.                 final K key = entry.getKey();
  100.                 return new UnmodifiableMapEntry<>(key, wrappedCollection(key));
  101.             }
  102.         }

  103.         final transient Map<K, Collection<V>> map;

  104.         AsMap(final Map<K, Collection<V>> map) {
  105.             this.map = map;
  106.         }

  107.         @Override
  108.         public void clear() {
  109.             AbstractMultiValuedMap.this.clear();
  110.         }

  111.         @Override
  112.         public boolean containsKey(final Object key) {
  113.             return map.containsKey(key);
  114.         }

  115.         @Override
  116.         public Set<Map.Entry<K, Collection<V>>> entrySet() {
  117.             return new AsMapEntrySet();
  118.         }

  119.         @Override
  120.         public boolean equals(final Object object) {
  121.             return this == object || map.equals(object);
  122.         }

  123.         @Override
  124.         public Collection<V> get(final Object key) {
  125.             final Collection<V> collection = map.get(key);
  126.             if (collection == null) {
  127.                 return null;
  128.             }
  129.             @SuppressWarnings("unchecked")
  130.             final K k = (K) key;
  131.             return wrappedCollection(k);
  132.         }

  133.         @Override
  134.         public int hashCode() {
  135.             return map.hashCode();
  136.         }

  137.         @Override
  138.         public Set<K> keySet() {
  139.             return AbstractMultiValuedMap.this.keySet();
  140.         }

  141.         @Override
  142.         public Collection<V> remove(final Object key) {
  143.             final Collection<V> collection = map.remove(key);
  144.             if (collection == null) {
  145.                 return null;
  146.             }

  147.             final Collection<V> output = createCollection();
  148.             output.addAll(collection);
  149.             collection.clear();
  150.             return output;
  151.         }

  152.         @Override
  153.         public int size() {
  154.             return map.size();
  155.         }

  156.         @Override
  157.         public String toString() {
  158.             return map.toString();
  159.         }
  160.     }

  161.     /**
  162.      * Inner class that provides the Entry<K, V> view
  163.      */
  164.     private final class EntryValues extends AbstractCollection<Entry<K, V>> {

  165.         @Override
  166.         public Iterator<Entry<K, V>> iterator() {
  167.             return new LazyIteratorChain<Entry<K, V>>() {

  168.                 final Collection<K> keysCol = new ArrayList<>(getMap().keySet());
  169.                 final Iterator<K> keyIterator = keysCol.iterator();

  170.                 @Override
  171.                 protected Iterator<? extends Entry<K, V>> nextIterator(final int count) {
  172.                     if (!keyIterator.hasNext()) {
  173.                         return null;
  174.                     }
  175.                     final K key = keyIterator.next();
  176.                     final Transformer<V, Entry<K, V>> entryTransformer = input -> new MultiValuedMapEntry(key, input);
  177.                     return new TransformIterator<>(new ValuesIterator(key), entryTransformer);
  178.                 }
  179.             };
  180.         }

  181.         @Override
  182.         public int size() {
  183.             return AbstractMultiValuedMap.this.size();
  184.         }

  185.     }

  186.     /**
  187.      * Inner class that provides a MultiSet<K> keys view.
  188.      */
  189.     private final class KeysMultiSet extends AbstractMultiSet<K> {

  190.         private final class MapEntryTransformer implements Transformer<Map.Entry<K, Collection<V>>, MultiSet.Entry<K>> {

  191.             @Override
  192.             public MultiSet.Entry<K> transform(final Map.Entry<K, Collection<V>> mapEntry) {
  193.                 return new AbstractMultiSet.AbstractEntry<K>() {

  194.                     @Override
  195.                     public int getCount() {
  196.                         return mapEntry.getValue().size();
  197.                     }

  198.                     @Override
  199.                     public K getElement() {
  200.                         return mapEntry.getKey();
  201.                     }
  202.                 };
  203.             }
  204.         }

  205.         @Override
  206.         public boolean contains(final Object o) {
  207.             return getMap().containsKey(o);
  208.         }

  209.         @Override
  210.         protected Iterator<MultiSet.Entry<K>> createEntrySetIterator() {
  211.             final MapEntryTransformer transformer = new MapEntryTransformer();
  212.             return IteratorUtils.transformedIterator(map.entrySet().iterator(), transformer);
  213.         }

  214.         @Override
  215.         public int getCount(final Object object) {
  216.             int count = 0;
  217.             final Collection<V> col = AbstractMultiValuedMap.this.getMap().get(object);
  218.             if (col != null) {
  219.                 count = col.size();
  220.             }
  221.             return count;
  222.         }

  223.         @Override
  224.         public boolean isEmpty() {
  225.             return getMap().isEmpty();
  226.         }

  227.         @Override
  228.         public int size() {
  229.             return AbstractMultiValuedMap.this.size();
  230.         }

  231.         @Override
  232.         protected int uniqueElements() {
  233.             return getMap().size();
  234.         }
  235.     }

  236.     /**
  237.      * Inner class for MultiValuedMap Entries.
  238.      */
  239.     private final class MultiValuedMapEntry extends AbstractMapEntry<K, V> {

  240.         MultiValuedMapEntry(final K key, final V value) {
  241.             super(key, value);
  242.         }

  243.         @Override
  244.         public V setValue(final V value) {
  245.             throw new UnsupportedOperationException();
  246.         }

  247.     }

  248.     /**
  249.      * Inner class for MapIterator.
  250.      */
  251.     private final class MultiValuedMapIterator implements MapIterator<K, V> {

  252.         private final Iterator<Entry<K, V>> it;

  253.         private Entry<K, V> current;

  254.         MultiValuedMapIterator() {
  255.             this.it = AbstractMultiValuedMap.this.entries().iterator();
  256.         }

  257.         @Override
  258.         public K getKey() {
  259.             if (current == null) {
  260.                 throw new IllegalStateException();
  261.             }
  262.             return current.getKey();
  263.         }

  264.         @Override
  265.         public V getValue() {
  266.             if (current == null) {
  267.                 throw new IllegalStateException();
  268.             }
  269.             return current.getValue();
  270.         }

  271.         @Override
  272.         public boolean hasNext() {
  273.             return it.hasNext();
  274.         }

  275.         @Override
  276.         public K next() {
  277.             current = it.next();
  278.             return current.getKey();
  279.         }

  280.         @Override
  281.         public void remove() {
  282.             it.remove();
  283.         }

  284.         @Override
  285.         public V setValue(final V value) {
  286.             if (current == null) {
  287.                 throw new IllegalStateException();
  288.             }
  289.             return current.setValue(value);
  290.         }

  291.     }

  292.     /**
  293.      * Inner class that provides the values view.
  294.      */
  295.     private final class Values extends AbstractCollection<V> {
  296.         @Override
  297.         public void clear() {
  298.             AbstractMultiValuedMap.this.clear();
  299.         }

  300.         @Override
  301.         public Iterator<V> iterator() {
  302.             final IteratorChain<V> chain = new IteratorChain<>();
  303.             for (final K k : keySet()) {
  304.                 chain.addIterator(new ValuesIterator(k));
  305.             }
  306.             return chain;
  307.         }

  308.         @Override
  309.         public int size() {
  310.             return AbstractMultiValuedMap.this.size();
  311.         }
  312.     }

  313.     /**
  314.      * Inner class that provides the values iterator.
  315.      */
  316.     private final class ValuesIterator implements Iterator<V> {
  317.         private final Object key;
  318.         private final Collection<V> values;
  319.         private final Iterator<V> iterator;

  320.         ValuesIterator(final Object key) {
  321.             this.key = key;
  322.             this.values = getMap().get(key);
  323.             this.iterator = values.iterator();
  324.         }

  325.         @Override
  326.         public boolean hasNext() {
  327.             return iterator.hasNext();
  328.         }

  329.         @Override
  330.         public V next() {
  331.             return iterator.next();
  332.         }

  333.         @Override
  334.         public void remove() {
  335.             iterator.remove();
  336.             if (values.isEmpty()) {
  337.                 AbstractMultiValuedMap.this.remove(key);
  338.             }
  339.         }
  340.     }

  341.     /**
  342.      * Wrapped collection to handle add and remove on the collection returned
  343.      * by get(object).
  344.      * <p>
  345.      * Currently, the wrapped collection is not cached and has to be retrieved
  346.      * from the underlying map. This is safe, but not very efficient and
  347.      * should be improved in subsequent releases. For this purpose, the
  348.      * scope of this collection is set to package private to simplify later
  349.      * refactoring.
  350.      */
  351.     class WrappedCollection implements Collection<V> {

  352.         protected final K key;

  353.         WrappedCollection(final K key) {
  354.             this.key = key;
  355.         }

  356.         @Override
  357.         public boolean add(final V value) {
  358.             Collection<V> coll = getMapping();
  359.             if (coll == null) {
  360.                 coll = createCollection();
  361.                 AbstractMultiValuedMap.this.map.put(key, coll);
  362.             }
  363.             return coll.add(value);
  364.         }

  365.         @Override
  366.         public boolean addAll(final Collection<? extends V> other) {
  367.             Collection<V> coll = getMapping();
  368.             if (coll == null) {
  369.                 coll = createCollection();
  370.                 AbstractMultiValuedMap.this.map.put(key, coll);
  371.             }
  372.             return coll.addAll(other);
  373.         }

  374.         @Override
  375.         public void clear() {
  376.             final Collection<V> coll = getMapping();
  377.             if (coll != null) {
  378.                 coll.clear();
  379.                 AbstractMultiValuedMap.this.remove(key);
  380.             }
  381.         }

  382.         @Override
  383.         public boolean contains(final Object obj) {
  384.             final Collection<V> coll = getMapping();
  385.             return coll != null && coll.contains(obj);
  386.         }

  387.         @Override
  388.         public boolean containsAll(final Collection<?> other) {
  389.             final Collection<V> coll = getMapping();
  390.             return coll != null && coll.containsAll(other);
  391.         }

  392.         protected Collection<V> getMapping() {
  393.             return getMap().get(key);
  394.         }

  395.         @Override
  396.         public boolean isEmpty() {
  397.             final Collection<V> coll = getMapping();
  398.             return coll == null || coll.isEmpty();
  399.         }

  400.         @Override
  401.         public Iterator<V> iterator() {
  402.             final Collection<V> coll = getMapping();
  403.             if (coll == null) {
  404.                 return IteratorUtils.EMPTY_ITERATOR;
  405.             }
  406.             return new ValuesIterator(key);
  407.         }

  408.         @Override
  409.         public boolean remove(final Object item) {
  410.             final Collection<V> coll = getMapping();
  411.             if (coll == null) {
  412.                 return false;
  413.             }

  414.             final boolean result = coll.remove(item);
  415.             if (coll.isEmpty()) {
  416.                 AbstractMultiValuedMap.this.remove(key);
  417.             }
  418.             return result;
  419.         }

  420.         @Override
  421.         public boolean removeAll(final Collection<?> c) {
  422.             final Collection<V> coll = getMapping();
  423.             if (coll == null) {
  424.                 return false;
  425.             }

  426.             final boolean result = coll.removeAll(c);
  427.             if (coll.isEmpty()) {
  428.                 AbstractMultiValuedMap.this.remove(key);
  429.             }
  430.             return result;
  431.         }

  432.         @Override
  433.         public boolean retainAll(final Collection<?> c) {
  434.             final Collection<V> coll = getMapping();
  435.             if (coll == null) {
  436.                 return false;
  437.             }

  438.             final boolean result = coll.retainAll(c);
  439.             if (coll.isEmpty()) {
  440.                 AbstractMultiValuedMap.this.remove(key);
  441.             }
  442.             return result;
  443.         }

  444.         @Override
  445.         public int size() {
  446.             final Collection<V> coll = getMapping();
  447.             return coll == null ? 0 : coll.size();
  448.         }

  449.         @Override
  450.         public Object[] toArray() {
  451.             final Collection<V> coll = getMapping();
  452.             if (coll == null) {
  453.                 return CollectionUtils.EMPTY_COLLECTION.toArray();
  454.             }
  455.             return coll.toArray();
  456.         }

  457.         @Override
  458.         @SuppressWarnings("unchecked")
  459.         public <T> T[] toArray(final T[] a) {
  460.             final Collection<V> coll = getMapping();
  461.             if (coll == null) {
  462.                 return (T[]) CollectionUtils.EMPTY_COLLECTION.toArray(a);
  463.             }
  464.             return coll.toArray(a);
  465.         }

  466.         @Override
  467.         public String toString() {
  468.             final Collection<V> coll = getMapping();
  469.             if (coll == null) {
  470.                 return CollectionUtils.EMPTY_COLLECTION.toString();
  471.             }
  472.             return coll.toString();
  473.         }

  474.     }

  475.     /** The values view */
  476.     private transient Collection<V> valuesView;

  477.     /** The EntryValues view */
  478.     private transient EntryValues entryValuesView;

  479.     /** The KeyMultiSet view */
  480.     private transient MultiSet<K> keysMultiSetView;

  481.     /** The AsMap view */
  482.     private transient AsMap asMapView;

  483.     /** The map used to store the data */
  484.     private transient Map<K, Collection<V>> map;

  485.     /**
  486.      * Constructor needed for subclass serialization.
  487.      */
  488.     protected AbstractMultiValuedMap() {
  489.     }

  490.     /**
  491.      * Constructor that wraps (not copies).
  492.      *
  493.      * @param map  the map to wrap, must not be null
  494.      * @throws NullPointerException if the map is null
  495.      */
  496.     @SuppressWarnings("unchecked")
  497.     protected AbstractMultiValuedMap(final Map<K, ? extends Collection<V>> map) {
  498.         this.map = (Map<K, Collection<V>>) Objects.requireNonNull(map, "map");
  499.     }

  500.     @Override
  501.     public Map<K, Collection<V>> asMap() {
  502.         return asMapView != null ? asMapView : (asMapView = new AsMap(map));
  503.     }

  504.     @Override
  505.     public void clear() {
  506.         getMap().clear();
  507.     }

  508.     @Override
  509.     public boolean containsKey(final Object key) {
  510.         return getMap().containsKey(key);
  511.     }

  512.     @Override
  513.     public boolean containsMapping(final Object key, final Object value) {
  514.         final Collection<V> coll = getMap().get(key);
  515.         return coll != null && coll.contains(value);
  516.     }

  517.     @Override
  518.     public boolean containsValue(final Object value) {
  519.         return values().contains(value);
  520.     }

  521.     /**
  522.      * Creates a new Collection typed for a given subclass.
  523.      *
  524.      * @return a new Collection typed for a given subclass.
  525.      */
  526.     protected abstract Collection<V> createCollection();

  527.     /**
  528.      * Reads the map in using a custom routine.
  529.      *
  530.      * @param in the input stream
  531.      * @throws IOException any of the usual I/O related exceptions
  532.      * @throws ClassNotFoundException if the stream contains an object which class cannot be loaded
  533.      * @throws ClassCastException if the stream does not contain the correct objects
  534.      */
  535.     protected void doReadObject(final ObjectInputStream in)
  536.             throws IOException, ClassNotFoundException {
  537.         final int entrySize = in.readInt();
  538.         for (int i = 0; i < entrySize; i++) {
  539.             @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
  540.             final K key = (K) in.readObject();
  541.             final Collection<V> values = get(key);
  542.             final int valueSize = in.readInt();
  543.             for (int j = 0; j < valueSize; j++) {
  544.                 @SuppressWarnings("unchecked") // see above
  545.                 final V value = (V) in.readObject();
  546.                 values.add(value);
  547.             }
  548.         }
  549.     }

  550.     /**
  551.      * Writes the map out using a custom routine.
  552.      *
  553.      * @param out the output stream
  554.      * @throws IOException any of the usual I/O related exceptions
  555.      */
  556.     protected void doWriteObject(final ObjectOutputStream out) throws IOException {
  557.         out.writeInt(map.size());
  558.         for (final Map.Entry<K, Collection<V>> entry : map.entrySet()) {
  559.             out.writeObject(entry.getKey());
  560.             out.writeInt(entry.getValue().size());
  561.             for (final V value : entry.getValue()) {
  562.                 out.writeObject(value);
  563.             }
  564.         }
  565.     }

  566.     @Override
  567.     public Collection<Entry<K, V>> entries() {
  568.         return entryValuesView != null ? entryValuesView : (entryValuesView = new EntryValues());
  569.     }

  570.     @Override
  571.     public boolean equals(final Object obj) {
  572.         if (this == obj) {
  573.             return true;
  574.         }
  575.         if (obj instanceof MultiValuedMap) {
  576.             return asMap().equals(((MultiValuedMap<?, ?>) obj).asMap());
  577.         }
  578.         return false;
  579.     }

  580.     /**
  581.      * Gets the collection of values associated with the specified key. This
  582.      * would return an empty collection in case the mapping is not present
  583.      *
  584.      * @param key the key to retrieve
  585.      * @return the {@code Collection} of values, will return an empty {@code Collection} for no mapping
  586.      */
  587.     @Override
  588.     public Collection<V> get(final K key) {
  589.         return wrappedCollection(key);
  590.     }

  591.     /**
  592.      * Gets the map being wrapped.
  593.      *
  594.      * @return the wrapped map
  595.      */
  596.     protected Map<K, ? extends Collection<V>> getMap() {
  597.         return map;
  598.     }

  599.     @Override
  600.     public int hashCode() {
  601.         return getMap().hashCode();
  602.     }

  603.     @Override
  604.     public boolean isEmpty() {
  605.         return getMap().isEmpty();
  606.     }

  607.     /**
  608.      * Returns a {@link MultiSet} view of the key mapping contained in this map.
  609.      * <p>
  610.      * Returns a MultiSet of keys with its values count as the count of the MultiSet.
  611.      * This multiset is backed by the map, so any changes in the map is reflected here.
  612.      * Any method which modifies this multiset like {@code add}, {@code remove},
  613.      * {@link Iterator#remove()} etc throws {@code UnsupportedOperationException}.
  614.      *
  615.      * @return a bag view of the key mapping contained in this map
  616.      */
  617.     @Override
  618.     public MultiSet<K> keys() {
  619.         if (keysMultiSetView == null) {
  620.             keysMultiSetView = UnmodifiableMultiSet.unmodifiableMultiSet(new KeysMultiSet());
  621.         }
  622.         return keysMultiSetView;
  623.     }

  624.     @Override
  625.     public Set<K> keySet() {
  626.         return getMap().keySet();
  627.     }

  628.     @Override
  629.     public MapIterator<K, V> mapIterator() {
  630.         if (isEmpty()) {
  631.             return EmptyMapIterator.emptyMapIterator();
  632.         }
  633.         return new MultiValuedMapIterator();
  634.     }

  635.     /**
  636.      * Adds the value to the collection associated with the specified key.
  637.      * <p>
  638.      * Unlike a normal {@code Map} the previous value is not replaced.
  639.      * Instead the new value is added to the collection stored against the key.
  640.      *
  641.      * @param key the key to store against
  642.      * @param value the value to add to the collection at the key
  643.      * @return the value added if the map changed and null if the map did not change
  644.      */
  645.     @Override
  646.     public boolean put(final K key, final V value) {
  647.         Collection<V> coll = getMap().get(key);
  648.         if (coll == null) {
  649.             coll = createCollection();
  650.             if (coll.add(value)) {
  651.                 map.put(key, coll);
  652.                 return true;
  653.             }
  654.             return false;
  655.         }
  656.         return coll.add(value);
  657.     }

  658.     /**
  659.      * Adds Iterable values to the collection associated with the specified key.
  660.      *
  661.      * @param key the key to store against
  662.      * @param values the values to add to the collection at the key, may not be null
  663.      * @return true if this map changed
  664.      * @throws NullPointerException if values is null
  665.      */
  666.     @Override
  667.     public boolean putAll(final K key, final Iterable<? extends V> values) {
  668.         Objects.requireNonNull(values, "values");

  669.         if (values instanceof Collection<?>) {
  670.             final Collection<? extends V> valueCollection = (Collection<? extends V>) values;
  671.             return !valueCollection.isEmpty() && get(key).addAll(valueCollection);
  672.         }
  673.         final Iterator<? extends V> it = values.iterator();
  674.         return it.hasNext() && CollectionUtils.addAll(get(key), it);
  675.     }

  676.     /**
  677.      * Copies all of the mappings from the specified map to this map. The effect
  678.      * of this call is equivalent to that of calling {@link #put(Object,Object)
  679.      * put(k, v)} on this map once for each mapping from key {@code k} to value
  680.      * {@code v} in the specified map. The behavior of this operation is
  681.      * undefined if the specified map is modified while the operation is in
  682.      * progress.
  683.      *
  684.      * @param map mappings to be stored in this map, may not be null
  685.      * @return true if the map changed as a result of this operation
  686.      * @throws NullPointerException if map is null
  687.      */
  688.     @Override
  689.     public boolean putAll(final Map<? extends K, ? extends V> map) {
  690.         Objects.requireNonNull(map, "map");
  691.         boolean changed = false;
  692.         for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
  693.             changed |= put(entry.getKey(), entry.getValue());
  694.         }
  695.         return changed;
  696.     }

  697.     /**
  698.      * Copies all of the mappings from the specified MultiValuedMap to this map.
  699.      * The effect of this call is equivalent to that of calling
  700.      * {@link #put(Object,Object) put(k, v)} on this map once for each mapping
  701.      * from key {@code k} to value {@code v} in the specified map. The
  702.      * behavior of this operation is undefined if the specified map is modified
  703.      * while the operation is in progress.
  704.      *
  705.      * @param map mappings to be stored in this map, may not be null
  706.      * @return true if the map changed as a result of this operation
  707.      * @throws NullPointerException if map is null
  708.      */
  709.     @Override
  710.     public boolean putAll(final MultiValuedMap<? extends K, ? extends V> map) {
  711.         Objects.requireNonNull(map, "map");
  712.         boolean changed = false;
  713.         for (final Map.Entry<? extends K, ? extends V> entry : map.entries()) {
  714.             changed |= put(entry.getKey(), entry.getValue());
  715.         }
  716.         return changed;
  717.     }

  718.     /**
  719.      * Removes all values associated with the specified key.
  720.      * <p>
  721.      * A subsequent {@code get(Object)} would return an empty collection.
  722.      *
  723.      * @param key  the key to remove values from
  724.      * @return the {@code Collection} of values removed, will return an
  725.      *   empty, unmodifiable collection for no mapping found
  726.      */
  727.     @Override
  728.     public Collection<V> remove(final Object key) {
  729.         return CollectionUtils.emptyIfNull(getMap().remove(key));
  730.     }

  731.     /**
  732.      * Removes a specific key/value mapping from the multivalued map.
  733.      * <p>
  734.      * The value is removed from the collection mapped to the specified key.
  735.      * Other values attached to that key are unaffected.
  736.      * <p>
  737.      * If the last value for a key is removed, an empty collection would be
  738.      * returned from a subsequent {@link #get(Object)}.
  739.      *
  740.      * @param key the key to remove from
  741.      * @param value the value to remove
  742.      * @return true if the mapping was removed, false otherwise
  743.      */
  744.     @Override
  745.     public boolean removeMapping(final Object key, final Object value) {
  746.         final Collection<V> coll = getMap().get(key);
  747.         if (coll == null) {
  748.             return false;
  749.         }
  750.         final boolean changed = coll.remove(value);
  751.         if (coll.isEmpty()) {
  752.             getMap().remove(key);
  753.         }
  754.         return changed;
  755.     }

  756.     /**
  757.      * Sets the map being wrapped.
  758.      * <p>
  759.      * <strong>NOTE:</strong> this method should only be used during deserialization
  760.      *
  761.      * @param map the map to wrap
  762.      */
  763.     @SuppressWarnings("unchecked")
  764.     protected void setMap(final Map<K, ? extends Collection<V>> map) {
  765.         this.map = (Map<K, Collection<V>>) map;
  766.     }

  767.     /**
  768.      * {@inheritDoc}
  769.      * <p>
  770.      * This implementation does <strong>not</strong> cache the total size
  771.      * of the multivalued map, but rather calculates it by iterating
  772.      * over the entries of the underlying map.
  773.      */
  774.     @Override
  775.     public int size() {
  776.         // the total size should be cached to improve performance
  777.         // but this requires that all modifications of the multimap
  778.         // (including the wrapped collections and entry/value
  779.         // collections) are tracked.
  780.         int size = 0;
  781.         for (final Collection<V> col : getMap().values()) {
  782.             size += col.size();
  783.         }
  784.         return size;
  785.     }

  786.     @Override
  787.     public String toString() {
  788.         return getMap().toString();
  789.     }

  790.     /**
  791.      * Gets a collection containing all the values in the map.
  792.      * <p>
  793.      * Returns a collection containing all the values from all keys.
  794.      *
  795.      * @return a collection view of the values contained in this map
  796.      */
  797.     @Override
  798.     public Collection<V> values() {
  799.         final Collection<V> vs = valuesView;
  800.         return vs != null ? vs : (valuesView = new Values());
  801.     }

  802.     Collection<V> wrappedCollection(final K key) {
  803.         return new WrappedCollection(key);
  804.     }

  805. }