ListOrderedMap.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.map;

  18. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.ObjectOutputStream;
  21. import java.io.Serializable;
  22. import java.util.AbstractList;
  23. import java.util.AbstractSet;
  24. import java.util.ArrayList;
  25. import java.util.Collection;
  26. import java.util.HashMap;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import java.util.ListIterator;
  30. import java.util.Map;
  31. import java.util.NoSuchElementException;
  32. import java.util.Set;

  33. import org.apache.commons.collections4.OrderedMap;
  34. import org.apache.commons.collections4.OrderedMapIterator;
  35. import org.apache.commons.collections4.ResettableIterator;
  36. import org.apache.commons.collections4.iterators.AbstractUntypedIteratorDecorator;
  37. import org.apache.commons.collections4.keyvalue.AbstractMapEntry;
  38. import org.apache.commons.collections4.list.UnmodifiableList;

  39. /**
  40.  * Decorates a {@code Map} to ensure that the order of addition is retained
  41.  * using a {@code List} to maintain order.
  42.  * <p>
  43.  * The order will be used via the iterators and toArray methods on the views.
  44.  * The order is also returned by the {@code MapIterator}.
  45.  * The {@code orderedMapIterator()} method accesses an iterator that can
  46.  * iterate both forwards and backwards through the map.
  47.  * In addition, non-interface methods are provided to access the map by index.
  48.  * </p>
  49.  * <p>
  50.  * If an object is added to the Map for a second time, it will remain in the
  51.  * original position in the iteration.
  52.  * </p>
  53.  * <p>
  54.  * <strong>Note that ListOrderedMap is not synchronized and is not thread-safe.</strong>
  55.  * If you wish to use this map from multiple threads concurrently, you must use
  56.  * appropriate synchronization. The simplest approach is to wrap this map
  57.  * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw
  58.  * exceptions when accessed by concurrent threads without synchronization.
  59.  * </p>
  60.  * <p>
  61.  * <strong>Note that ListOrderedMap doesn't work with
  62.  * {@link java.util.IdentityHashMap IdentityHashMap}, {@link CaseInsensitiveMap},
  63.  * or similar maps that violate the general contract of {@link java.util.Map}.</strong>
  64.  * The {@code ListOrderedMap} (or, more precisely, the underlying {@code List})
  65.  * is relying on {@link Object#equals(Object) equals()}. This is fine, as long as the
  66.  * decorated {@code Map} is also based on {@link Object#equals(Object) equals()},
  67.  * and {@link Object#hashCode() hashCode()}, which
  68.  * {@link java.util.IdentityHashMap IdentityHashMap}, and
  69.  * {@link CaseInsensitiveMap} don't: The former uses {@code ==}, and
  70.  * the latter uses {@link Object#equals(Object) equals()} on a lower-cased
  71.  * key.
  72.  * </p>
  73.  * <p>
  74.  * This class is {@link Serializable} starting with Commons Collections 3.1.
  75.  * </p>
  76.  *
  77.  * @param <K> the type of the keys in this map
  78.  * @param <V> the type of the values in this map
  79.  * @since 3.0
  80.  */
  81. public class ListOrderedMap<K, V>
  82.         extends AbstractMapDecorator<K, V>
  83.         implements OrderedMap<K, V>, Serializable {

  84.     static class EntrySetView<K, V> extends AbstractSet<Map.Entry<K, V>> {
  85.         private final ListOrderedMap<K, V> parent;
  86.         private final List<K> insertOrder;
  87.         private Set<Map.Entry<K, V>> entrySet;

  88.         EntrySetView(final ListOrderedMap<K, V> parent, final List<K> insertOrder) {
  89.             this.parent = parent;
  90.             this.insertOrder = insertOrder;
  91.         }

  92.         @Override
  93.         public void clear() {
  94.             parent.clear();
  95.         }

  96.         @Override
  97.         public boolean contains(final Object obj) {
  98.             return getEntrySet().contains(obj);
  99.         }
  100.         @Override
  101.         public boolean containsAll(final Collection<?> coll) {
  102.             return getEntrySet().containsAll(coll);
  103.         }

  104.         @Override
  105.         public boolean equals(final Object obj) {
  106.             if (obj == this) {
  107.                 return true;
  108.             }
  109.             return getEntrySet().equals(obj);
  110.         }

  111.         private Set<Map.Entry<K, V>> getEntrySet() {
  112.             if (entrySet == null) {
  113.                 entrySet = parent.decorated().entrySet();
  114.             }
  115.             return entrySet;
  116.         }

  117.         @Override
  118.         public int hashCode() {
  119.             return getEntrySet().hashCode();
  120.         }

  121.         @Override
  122.         public boolean isEmpty() {
  123.             return parent.isEmpty();
  124.         }

  125.         @Override
  126.         public Iterator<Map.Entry<K, V>> iterator() {
  127.             return new ListOrderedIterator<>(parent, insertOrder);
  128.         }

  129.         @Override
  130.         @SuppressWarnings("unchecked")
  131.         public boolean remove(final Object obj) {
  132.             if (!(obj instanceof Map.Entry)) {
  133.                 return false;
  134.             }
  135.             if (getEntrySet().contains(obj)) {
  136.                 final Object key = ((Map.Entry<K, V>) obj).getKey();
  137.                 parent.remove(key);
  138.                 return true;
  139.             }
  140.             return false;
  141.         }

  142.         @Override
  143.         public int size() {
  144.             return parent.size();
  145.         }

  146.         @Override
  147.         public String toString() {
  148.             return getEntrySet().toString();
  149.         }
  150.     }

  151.     static class KeySetView<K> extends AbstractSet<K> {
  152.         private final ListOrderedMap<K, Object> parent;

  153.         @SuppressWarnings("unchecked")
  154.         KeySetView(final ListOrderedMap<K, ?> parent) {
  155.             this.parent = (ListOrderedMap<K, Object>) parent;
  156.         }

  157.         @Override
  158.         public void clear() {
  159.             parent.clear();
  160.         }

  161.         @Override
  162.         public boolean contains(final Object value) {
  163.             return parent.containsKey(value);
  164.         }

  165.         @Override
  166.         public Iterator<K> iterator() {
  167.             return new AbstractUntypedIteratorDecorator<Map.Entry<K, Object>, K>(parent.entrySet().iterator()) {
  168.                 @Override
  169.                 public K next() {
  170.                     return getIterator().next().getKey();
  171.                 }
  172.             };
  173.         }

  174.         @Override
  175.         public int size() {
  176.             return parent.size();
  177.         }
  178.     }

  179.     static class ListOrderedIterator<K, V> extends AbstractUntypedIteratorDecorator<K, Map.Entry<K, V>> {
  180.         private final ListOrderedMap<K, V> parent;
  181.         private K last;

  182.         ListOrderedIterator(final ListOrderedMap<K, V> parent, final List<K> insertOrder) {
  183.             super(insertOrder.iterator());
  184.             this.parent = parent;
  185.         }

  186.         @Override
  187.         public Map.Entry<K, V> next() {
  188.             last = getIterator().next();
  189.             return new ListOrderedMapEntry<>(parent, last);
  190.         }

  191.         @Override
  192.         public void remove() {
  193.             super.remove();
  194.             parent.decorated().remove(last);
  195.         }
  196.     }

  197.     static class ListOrderedMapEntry<K, V> extends AbstractMapEntry<K, V> {
  198.         private final ListOrderedMap<K, V> parent;

  199.         ListOrderedMapEntry(final ListOrderedMap<K, V> parent, final K key) {
  200.             super(key, null);
  201.             this.parent = parent;
  202.         }

  203.         @Override
  204.         public V getValue() {
  205.             return parent.get(getKey());
  206.         }

  207.         @Override
  208.         public V setValue(final V value) {
  209.             return parent.decorated().put(getKey(), value);
  210.         }
  211.     }

  212.     static class ListOrderedMapIterator<K, V> implements OrderedMapIterator<K, V>, ResettableIterator<K> {
  213.         private final ListOrderedMap<K, V> parent;
  214.         private ListIterator<K> iterator;
  215.         private K last;
  216.         private boolean readable;

  217.         ListOrderedMapIterator(final ListOrderedMap<K, V> parent) {
  218.             this.parent = parent;
  219.             this.iterator = parent.insertOrder.listIterator();
  220.         }

  221.         @Override
  222.         public K getKey() {
  223.             if (!readable) {
  224.                 throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
  225.             }
  226.             return last;
  227.         }

  228.         @Override
  229.         public V getValue() {
  230.             if (!readable) {
  231.                 throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
  232.             }
  233.             return parent.get(last);
  234.         }

  235.         @Override
  236.         public boolean hasNext() {
  237.             return iterator.hasNext();
  238.         }

  239.         @Override
  240.         public boolean hasPrevious() {
  241.             return iterator.hasPrevious();
  242.         }

  243.         @Override
  244.         public K next() {
  245.             last = iterator.next();
  246.             readable = true;
  247.             return last;
  248.         }

  249.         @Override
  250.         public K previous() {
  251.             last = iterator.previous();
  252.             readable = true;
  253.             return last;
  254.         }

  255.         @Override
  256.         public void remove() {
  257.             if (!readable) {
  258.                 throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
  259.             }
  260.             iterator.remove();
  261.             parent.map.remove(last);
  262.             readable = false;
  263.         }

  264.         @Override
  265.         public void reset() {
  266.             iterator = parent.insertOrder.listIterator();
  267.             last = null;
  268.             readable = false;
  269.         }

  270.         @Override
  271.         public V setValue(final V value) {
  272.             if (!readable) {
  273.                 throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
  274.             }
  275.             return parent.map.put(last, value);
  276.         }

  277.         @Override
  278.         public String toString() {
  279.             if (readable) {
  280.                 return "Iterator[" + getKey() + "=" + getValue() + "]";
  281.             }
  282.             return "Iterator[]";
  283.         }
  284.     }

  285.     static class ValuesView<V> extends AbstractList<V> {
  286.         private final ListOrderedMap<Object, V> parent;

  287.         @SuppressWarnings("unchecked")
  288.         ValuesView(final ListOrderedMap<?, V> parent) {
  289.             this.parent = (ListOrderedMap<Object, V>) parent;
  290.         }

  291.         @Override
  292.         public void clear() {
  293.             parent.clear();
  294.         }

  295.         @Override
  296.         public boolean contains(final Object value) {
  297.             return parent.containsValue(value);
  298.         }

  299.         @Override
  300.         public V get(final int index) {
  301.             return parent.getValue(index);
  302.         }

  303.         @Override
  304.         public Iterator<V> iterator() {
  305.             return new AbstractUntypedIteratorDecorator<Map.Entry<Object, V>, V>(parent.entrySet().iterator()) {
  306.                 @Override
  307.                 public V next() {
  308.                     return getIterator().next().getValue();
  309.                 }
  310.             };
  311.         }

  312.         @Override
  313.         public V remove(final int index) {
  314.             return parent.remove(index);
  315.         }

  316.         @Override
  317.         public V set(final int index, final V value) {
  318.             return parent.setValue(index, value);
  319.         }

  320.         @Override
  321.         public int size() {
  322.             return parent.size();
  323.         }
  324.     }

  325.     /** Serialization version */
  326.     private static final long serialVersionUID = 2728177751851003750L;

  327.     /**
  328.      * Factory method to create an ordered map.
  329.      * <p>
  330.      * An {@code ArrayList} is used to retain order.
  331.      * </p>
  332.      *
  333.      * @param <K>  the key type
  334.      * @param <V>  the value type
  335.      * @param map  the map to decorate, must not be null
  336.      * @return a new list ordered map
  337.      * @throws NullPointerException if map is null
  338.      * @since 4.0
  339.      */
  340.     public static <K, V> ListOrderedMap<K, V> listOrderedMap(final Map<K, V> map) {
  341.         return new ListOrderedMap<>(map);
  342.     }

  343.     /** Internal list to hold the sequence of objects */
  344.     private final List<K> insertOrder = new ArrayList<>();

  345.     /**
  346.      * Constructs a new empty {@code ListOrderedMap} that decorates
  347.      * a {@code HashMap}.
  348.      *
  349.      * @since 3.1
  350.      */
  351.     public ListOrderedMap() {
  352.         this(new HashMap<>());
  353.     }

  354.     /**
  355.      * Constructor that wraps (not copies).
  356.      *
  357.      * @param map  the map to decorate, must not be null
  358.      * @throws NullPointerException if map is null
  359.      */
  360.     protected ListOrderedMap(final Map<K, V> map) {
  361.         super(map);
  362.         insertOrder.addAll(decorated().keySet());
  363.     }

  364.     /**
  365.      * Gets an unmodifiable List view of the keys which changes as the map changes.
  366.      * <p>
  367.      * The returned list is unmodifiable because changes to the values of
  368.      * the list (using {@link java.util.ListIterator#set(Object)}) will
  369.      * effectively remove the value from the list and reinsert that value at
  370.      * the end of the list, which is an unexpected side effect of changing the
  371.      * value of a list.  This occurs because changing the key, changes when the
  372.      * mapping is added to the map and thus where it appears in the list.
  373.      * </p>
  374.      * <p>
  375.      * An alternative to this method is to use the better named
  376.      * {@link #keyList()} or {@link #keySet()}.
  377.      * </p>
  378.      *
  379.      * @see #keyList()
  380.      * @see #keySet()
  381.      * @return The ordered list of keys.
  382.      */
  383.     public List<K> asList() {
  384.         return keyList();
  385.     }

  386.     @Override
  387.     public void clear() {
  388.         decorated().clear();
  389.         insertOrder.clear();
  390.     }

  391.     /**
  392.      * Gets a view over the entries in the map.
  393.      * <p>
  394.      * The Set will be ordered by object insertion into the map.
  395.      * </p>
  396.      *
  397.      * @return the fully modifiable set view over the entries
  398.      */
  399.     @Override
  400.     public Set<Map.Entry<K, V>> entrySet() {
  401.         return new EntrySetView<>(this, insertOrder);
  402.     }

  403.     /**
  404.      * Gets the first key in this map by insert order.
  405.      *
  406.      * @return the first key currently in this map
  407.      * @throws NoSuchElementException if this map is empty
  408.      */
  409.     @Override
  410.     public K firstKey() {
  411.         if (isEmpty()) {
  412.             throw new NoSuchElementException("Map is empty");
  413.         }
  414.         return insertOrder.get(0);
  415.     }

  416.     /**
  417.      * Gets the key at the specified index.
  418.      *
  419.      * @param index  the index to retrieve
  420.      * @return the key at the specified index
  421.      * @throws IndexOutOfBoundsException if the index is invalid
  422.      */
  423.     public K get(final int index) {
  424.         return insertOrder.get(index);
  425.     }

  426.     /**
  427.      * Gets the value at the specified index.
  428.      *
  429.      * @param index  the index to retrieve
  430.      * @return the key at the specified index
  431.      * @throws IndexOutOfBoundsException if the index is invalid
  432.      */
  433.     public V getValue(final int index) {
  434.         return get(insertOrder.get(index));
  435.     }

  436.     /**
  437.      * Gets the index of the specified key.
  438.      *
  439.      * @param key  the key to find the index of
  440.      * @return the index, or -1 if not found
  441.      */
  442.     public int indexOf(final Object key) {
  443.         return insertOrder.indexOf(key);
  444.     }

  445.     /**
  446.      * Gets a view over the keys in the map as a List.
  447.      * <p>
  448.      * The List will be ordered by object insertion into the map.
  449.      * The List is unmodifiable.
  450.      * </p>
  451.      *
  452.      * @see #keySet()
  453.      * @return the unmodifiable list view over the keys
  454.      * @since 3.2
  455.      */
  456.     public List<K> keyList() {
  457.         return UnmodifiableList.unmodifiableList(insertOrder);
  458.     }

  459.     /**
  460.      * Gets a view over the keys in the map.
  461.      * <p>
  462.      * The Collection will be ordered by object insertion into the map.
  463.      * </p>
  464.      *
  465.      * @see #keyList()
  466.      * @return the fully modifiable collection view over the keys
  467.      */
  468.     @Override
  469.     public Set<K> keySet() {
  470.         return new KeySetView<>(this);
  471.     }

  472.     /**
  473.      * Gets the last key in this map by insert order.
  474.      *
  475.      * @return the last key currently in this map
  476.      * @throws NoSuchElementException if this map is empty
  477.      */
  478.     @Override
  479.     public K lastKey() {
  480.         if (isEmpty()) {
  481.             throw new NoSuchElementException("Map is empty");
  482.         }
  483.         return insertOrder.get(size() - 1);
  484.     }

  485.     @Override
  486.     public OrderedMapIterator<K, V> mapIterator() {
  487.         return new ListOrderedMapIterator<>(this);
  488.     }

  489.     /**
  490.      * Gets the next key to the one specified using insert order.
  491.      * This method performs a list search to find the key and is O(n).
  492.      *
  493.      * @param key  the key to find previous for
  494.      * @return the next key, null if no match or at start
  495.      */
  496.     @Override
  497.     public K nextKey(final Object key) {
  498.         final int index = insertOrder.indexOf(key);
  499.         if (index >= 0 && index < size() - 1) {
  500.             return insertOrder.get(index + 1);
  501.         }
  502.         return null;
  503.     }

  504.     /**
  505.      * Gets the previous key to the one specified using insert order.
  506.      * This method performs a list search to find the key and is O(n).
  507.      *
  508.      * @param key  the key to find previous for
  509.      * @return the previous key, null if no match or at start
  510.      */
  511.     @Override
  512.     public K previousKey(final Object key) {
  513.         final int index = insertOrder.indexOf(key);
  514.         if (index > 0) {
  515.             return insertOrder.get(index - 1);
  516.         }
  517.         return null;
  518.     }

  519.     /**
  520.      * Puts a key-value mapping into the map at the specified index.
  521.      * <p>
  522.      * If the map already contains the key, then the original mapping
  523.      * is removed and the new mapping added at the specified index.
  524.      * The remove may change the effect of the index. The index is
  525.      * always calculated relative to the original state of the map.
  526.      * </p>
  527.      * <p>
  528.      * Thus, the steps are: (1) remove the existing key-value mapping,
  529.      * then (2) insert the new key-value mapping at the position it
  530.      * would have been inserted had the remove not occurred.
  531.      * </p>
  532.      *
  533.      * @param index  the index at which the mapping should be inserted
  534.      * @param key  the key
  535.      * @param value  the value
  536.      * @return the value previously mapped to the key
  537.      * @throws IndexOutOfBoundsException if the index is out of range [0, size]
  538.      * @since 3.2
  539.      */
  540.     public V put(int index, final K key, final V value) {
  541.         if (index < 0 || index > insertOrder.size()) {
  542.             throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + insertOrder.size());
  543.         }

  544.         final Map<K, V> m = decorated();
  545.         if (m.containsKey(key)) {
  546.             final V result = m.remove(key);
  547.             final int pos = insertOrder.indexOf(key);
  548.             insertOrder.remove(pos);
  549.             if (pos < index) {
  550.                 index--;
  551.             }
  552.             insertOrder.add(index, key);
  553.             m.put(key, value);
  554.             return result;
  555.         }
  556.         insertOrder.add(index, key);
  557.         m.put(key, value);
  558.         return null;
  559.     }

  560.     @Override
  561.     public V put(final K key, final V value) {
  562.         if (decorated().containsKey(key)) {
  563.             // re-adding doesn't change order
  564.             return decorated().put(key, value);
  565.         }
  566.         // first add, so add to both map and list
  567.         final V result = decorated().put(key, value);
  568.         insertOrder.add(key);
  569.         return result;
  570.     }

  571.     /**
  572.      * Puts the values contained in a supplied Map into the Map starting at
  573.      * the specified index.
  574.      *
  575.      * @param index the index in the Map to start at.
  576.      * @param map the Map containing the entries to be added.
  577.      * @throws IndexOutOfBoundsException if the index is out of range [0, size]
  578.      */
  579.     public void putAll(int index, final Map<? extends K, ? extends V> map) {
  580.         if (index < 0 || index > insertOrder.size()) {
  581.             throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + insertOrder.size());
  582.         }
  583.         for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
  584.             final K key = entry.getKey();
  585.             final boolean contains = containsKey(key);
  586.             // The return value of put is null if the key did not exist OR the value was null
  587.             // so it cannot be used to determine whether the key was added
  588.             put(index, entry.getKey(), entry.getValue());
  589.             if (!contains) {
  590.                 // if no key was replaced, increment the index
  591.                 index++;
  592.             } else {
  593.                 // otherwise put the next item after the currently inserted key
  594.                 index = indexOf(entry.getKey()) + 1;
  595.             }
  596.         }
  597.     }

  598.     @Override
  599.     public void putAll(final Map<? extends K, ? extends V> map) {
  600.         for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
  601.             put(entry.getKey(), entry.getValue());
  602.         }
  603.     }

  604.     /**
  605.      * Deserializes the map in using a custom routine.
  606.      *
  607.      * @param in  the input stream
  608.      * @throws IOException if an error occurs while reading from the stream
  609.      * @throws ClassNotFoundException if an object read from the stream cannot be loaded
  610.      * @since 3.1
  611.      */
  612.     @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect
  613.     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
  614.         in.defaultReadObject();
  615.         map = (Map<K, V>) in.readObject(); // (1)
  616.     }

  617.     /**
  618.      * Removes the element at the specified index.
  619.      *
  620.      * @param index  the index of the object to remove
  621.      * @return the removed value, or {@code null} if none existed
  622.      * @throws IndexOutOfBoundsException if the index is invalid
  623.      */
  624.     public V remove(final int index) {
  625.         return remove(get(index));
  626.     }

  627.     @Override
  628.     public V remove(final Object key) {
  629.         V result = null;
  630.         if (decorated().containsKey(key)) {
  631.             result = decorated().remove(key);
  632.             insertOrder.remove(key);
  633.         }
  634.         return result;
  635.     }

  636.     /**
  637.      * Sets the value at the specified index.
  638.      *
  639.      * @param index  the index of the value to set
  640.      * @param value  the new value to set
  641.      * @return the previous value at that index
  642.      * @throws IndexOutOfBoundsException if the index is invalid
  643.      * @since 3.2
  644.      */
  645.     public V setValue(final int index, final V value) {
  646.         final K key = insertOrder.get(index);
  647.         return put(key, value);
  648.     }

  649.     /**
  650.      * Returns the Map as a string.
  651.      *
  652.      * @return the Map as a String
  653.      */
  654.     @Override
  655.     public String toString() {
  656.         if (isEmpty()) {
  657.             return "{}";
  658.         }
  659.         final StringBuilder buf = new StringBuilder();
  660.         buf.append('{');
  661.         boolean first = true;
  662.         for (final Map.Entry<K, V> entry : entrySet()) {
  663.             final K key = entry.getKey();
  664.             final V value = entry.getValue();
  665.             if (first) {
  666.                 first = false;
  667.             } else {
  668.                 buf.append(", ");
  669.             }
  670.             buf.append(key == this ? "(this Map)" : key);
  671.             buf.append('=');
  672.             buf.append(value == this ? "(this Map)" : value);
  673.         }
  674.         buf.append('}');
  675.         return buf.toString();
  676.     }

  677.     /**
  678.      * Gets a view over the values in the map as a List.
  679.      * <p>
  680.      * The List will be ordered by object insertion into the map.
  681.      * The List supports remove and set, but does not support add.
  682.      * </p>
  683.      *
  684.      * @see #values()
  685.      * @return the partially modifiable list view over the values
  686.      * @since 3.2
  687.      */
  688.     public List<V> valueList() {
  689.         return new ValuesView<>(this);
  690.     }

  691.     /**
  692.      * Gets a view over the values in the map.
  693.      * <p>
  694.      * The Collection will be ordered by object insertion into the map.
  695.      * </p>
  696.      * <p>
  697.      * From Commons Collections 3.2, this Collection can be cast
  698.      * to a list, see {@link #valueList()}
  699.      * </p>
  700.      *
  701.      * @see #valueList()
  702.      * @return the fully modifiable collection view over the values
  703.      */
  704.     @Override
  705.     public Collection<V> values() {
  706.         return new ValuesView<>(this);
  707.     }

  708.     /**
  709.      * Serializes this object to an ObjectOutputStream.
  710.      *
  711.      * @param out the target ObjectOutputStream.
  712.      * @throws IOException thrown when an I/O errors occur writing to the target stream.
  713.      * @since 3.1
  714.      */
  715.     private void writeObject(final ObjectOutputStream out) throws IOException {
  716.         out.defaultWriteObject();
  717.         out.writeObject(map);
  718.     }

  719. }