    001    /*
    002     * Copyright (C) 2007 The Guava Authors
    003     *
    004     * Licensed under the Apache License, Version 2.0 (the "License");
    005     * you may not use this file except in compliance with the License.
    006     * You may obtain a copy of the License at
    007     *
    008     * http://www.apache.org/licenses/LICENSE-2.0
    009     *
    010     * Unless required by applicable law or agreed to in writing, software
    011     * distributed under the License is distributed on an "AS IS" BASIS,
    012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    013     * See the License for the specific language governing permissions and
    014     * limitations under the License.
    015     */
    016
    017    package com.google.common.collect;
    018
    019    import static com.google.common.base.Preconditions.checkArgument;
    020
    021    import com.google.common.annotations.GwtCompatible;
    022    import com.google.common.annotations.GwtIncompatible;
    023    import com.google.common.annotations.VisibleForTesting;
    024    import com.google.common.base.Objects;
    025    import com.google.common.primitives.Ints;
    026
    027    import java.io.IOException;
    028    import java.io.ObjectInputStream;
    029    import java.io.ObjectOutputStream;
    030    import java.util.Arrays;
    031    import java.util.Collection;
    032    import java.util.ConcurrentModificationException;
    033    import java.util.Iterator;
    034    import java.util.LinkedHashMap;
    035    import java.util.LinkedHashSet;
    036    import java.util.Map;
    037    import java.util.NoSuchElementException;
    038    import java.util.Set;
    039
    040    import javax.annotation.Nullable;
    041
    042    /**
    043     * Implementation of {@code Multimap} that does not allow duplicate key-value
    044     * entries and that returns collections whose iterators follow the ordering in
    045     * which the data was added to the multimap.
    046     *
    047     * <p>The collections returned by {@code keySet}, {@code keys}, and {@code
    048     * asMap} iterate through the keys in the order they were first added to the
    049     * multimap. Similarly, {@code get}, {@code removeAll}, and {@code
    050     * replaceValues} return collections that iterate through the values in the
    051     * order they were added. The collections generated by {@code entries} and
    052     * {@code values} iterate across the key-value mappings in the order they were
    053     * added to the multimap.
    054     *
    055     * <p>The iteration ordering of the collections generated by {@code keySet},
    056     * {@code keys}, and {@code asMap} has a few subtleties. As long as the set of
    057     * keys remains unchanged, adding or removing mappings does not affect the key
    058     * iteration order. However, if you remove all values associated with a key and
    059     * then add the key back to the multimap, that key will come last in the key
    060     * iteration order.
    061     *
    062     * <p>The multimap does not store duplicate key-value pairs. Adding a new
    063     * key-value pair equal to an existing key-value pair has no effect.
    064     *
    065     * <p>Keys and values may be null. All optional multimap methods are supported,
    066     * and all returned views are modifiable.
    067     *
    068     * <p>This class is not threadsafe when any concurrent operations update the
    069     * multimap. Concurrent read operations will work correctly. To allow concurrent
    070     * update operations, wrap your multimap with a call to {@link
    071     * Multimaps#synchronizedSetMultimap}.
    072     *
    073     * <p>See the Guava User Guide article on <a href=
    074     * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap">
    075     * {@code Multimap}</a>.
    076     *
    077     * @author Jared Levy
    078     * @author Louis Wasserman
    079     * @since 2.0 (imported from Google Collections Library)
    080     */
    081    @GwtCompatible(serializable = true, emulated = true)
    082    public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> {
    083
    084      /**
    085       * Creates a new, empty {@code LinkedHashMultimap} with the default initial
    086       * capacities.
    087       */
    088      public static <K, V> LinkedHashMultimap<K, V> create() {
    089        return new LinkedHashMultimap<K, V>(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY);
    090      }
    091
    092      /**
    093       * Constructs an empty {@code LinkedHashMultimap} with enough capacity to hold
    094       * the specified numbers of keys and values without rehashing.
    095       *
    096       * @param expectedKeys the expected number of distinct keys
    097       * @param expectedValuesPerKey the expected average number of values per key
    098       * @throws IllegalArgumentException if {@code expectedKeys} or {@code
    099       *      expectedValuesPerKey} is negative
    100       */
    101      public static <K, V> LinkedHashMultimap<K, V> create(
    102          int expectedKeys, int expectedValuesPerKey) {
    103        return new LinkedHashMultimap<K, V>(
    104            Maps.capacity(expectedKeys),
    105            Maps.capacity(expectedValuesPerKey));
    106      }
    107
    108      /**
    109       * Constructs a {@code LinkedHashMultimap} with the same mappings as the
    110       * specified multimap. If a key-value mapping appears multiple times in the
    111       * input multimap, it only appears once in the constructed multimap. The new
    112       * multimap has the same {@link Multimap#entries()} iteration order as the
    113       * input multimap, except for excluding duplicate mappings.
    114       *
    115       * @param multimap the multimap whose contents are copied to this multimap
    116       */
    117      public static <K, V> LinkedHashMultimap<K, V> create(
    118          Multimap<? extends K, ? extends V> multimap) {
    119        LinkedHashMultimap<K, V> result = create(multimap.keySet().size(), DEFAULT_VALUE_SET_CAPACITY);
    120        result.putAll(multimap);
    121        return result;
    122      }
    123
    124      private interface ValueSetLink<K, V> {
    125        ValueSetLink<K, V> getPredecessorInValueSet();
    126        ValueSetLink<K, V> getSuccessorInValueSet();
    127
    128        void setPredecessorInValueSet(ValueSetLink<K, V> entry);
    129        void setSuccessorInValueSet(ValueSetLink<K, V> entry);
    130      }
    131
    132      private static <K, V> void succeedsInValueSet(ValueSetLink<K, V> pred, ValueSetLink<K, V> succ) {
    133        pred.setSuccessorInValueSet(succ);
    134        succ.setPredecessorInValueSet(pred);
    135      }
    136
    137      private static <K, V> void succeedsInMultimap(
    138          ValueEntry<K, V> pred, ValueEntry<K, V> succ) {
    139        pred.setSuccessorInMultimap(succ);
    140        succ.setPredecessorInMultimap(pred);
    141      }
    142
    143      private static <K, V> void deleteFromValueSet(ValueSetLink<K, V> entry) {
    144        succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet());
    145      }
    146
    147      private static <K, V> void deleteFromMultimap(ValueEntry<K, V> entry) {
    148        succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap());
    149      }
    150
    151      /**
    152       * LinkedHashMultimap entries are in no less than three coexisting linked lists:
    153       * a row in the hash table for a Set<V> associated with a key, the linked list
    154       * of insertion-ordered entries in that Set<V>, and the linked list of entries
    155       * in the LinkedHashMultimap as a whole.
    156       */
    157      private static final class ValueEntry<K, V> extends AbstractMapEntry<K, V>
    158          implements ValueSetLink<K, V> {
    159        final K key;
    160        final V value;
    161        final int valueHash;
    162
    163        @Nullable ValueEntry<K, V> nextInValueSetHashRow;
    164
    165        ValueSetLink<K, V> predecessorInValueSet;
    166        ValueSetLink<K, V> successorInValueSet;
    167
    168        ValueEntry<K, V> predecessorInMultimap;
    169        ValueEntry<K, V> successorInMultimap;
    170
    171        ValueEntry(@Nullable K key, @Nullable V value, int valueHash,
    172            @Nullable ValueEntry<K, V> nextInValueSetHashRow) {
    173          this.key = key;
    174          this.value = value;
    175          this.valueHash = valueHash;
    176          this.nextInValueSetHashRow = nextInValueSetHashRow;
    177        }
    178
    179        @Override
    180        public K getKey() {
    181          return key;
    182        }
    183
    184        @Override
    185        public V getValue() {
    186          return value;
    187        }
    188
    189        @Override
    190        public ValueSetLink<K, V> getPredecessorInValueSet() {
    191          return predecessorInValueSet;
    192        }
    193
    194        @Override
    195        public ValueSetLink<K, V> getSuccessorInValueSet() {
    196          return successorInValueSet;
    197        }
    198
    199        @Override
    200        public void setPredecessorInValueSet(ValueSetLink<K, V> entry) {
    201          predecessorInValueSet = entry;
    202        }
    203
    204        @Override
    205        public void setSuccessorInValueSet(ValueSetLink<K, V> entry) {
    206          successorInValueSet = entry;
    207        }
    208
    209        public ValueEntry<K, V> getPredecessorInMultimap() {
    210          return predecessorInMultimap;
    211        }
    212
    213        public ValueEntry<K, V> getSuccessorInMultimap() {
    214          return successorInMultimap;
    215        }
    216
    217        public void setSuccessorInMultimap(ValueEntry<K, V> multimapSuccessor) {
    218          this.successorInMultimap = multimapSuccessor;
    219        }
    220
    221        public void setPredecessorInMultimap(ValueEntry<K, V> multimapPredecessor) {
    222          this.predecessorInMultimap = multimapPredecessor;
    223        }
    224      }
    225
    226      private static final int DEFAULT_KEY_CAPACITY = 16;
    227      private static final int DEFAULT_VALUE_SET_CAPACITY = 2;
    228
    229      private static final int MAX_VALUE_SET_TABLE_SIZE = Ints.MAX_POWER_OF_TWO;
    230
    231      @VisibleForTesting transient int valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY;
    232      private transient ValueEntry<K, V> multimapHeaderEntry;
    233
    234      private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) {
    235        super(new LinkedHashMap<K, Collection<V>>(keyCapacity));
    236
    237        checkArgument(valueSetCapacity >= 0,
    238            "expectedValuesPerKey must be >= 0 but was %s", valueSetCapacity);
    239
    240        this.valueSetCapacity = valueSetCapacity;
    241        this.multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null);
    242        succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
    243      }
    244
    245      /**
    246       * {@inheritDoc}
    247       *
    248       * <p>Creates an empty {@code LinkedHashSet} for a collection of values for
    249       * one key.
    250       *
    251       * @return a new {@code LinkedHashSet} containing a collection of values for
    252       *     one key
    253       */
    254      @Override
    255      Set<V> createCollection() {
    256        return new LinkedHashSet<V>(valueSetCapacity);
    257      }
    258
    259      /**
    260       * {@inheritDoc}
    261       *
    262       * <p>Creates a decorated insertion-ordered set that also keeps track of the
    263       * order in which key-value pairs are added to the multimap.
    264       *
    265       * @param key key to associate with values in the collection
    266       * @return a new decorated set containing a collection of values for one key
    267       */
    268      @Override
    269      Collection<V> createCollection(K key) {
    270        return new ValueSet(key, valueSetCapacity);
    271      }
    272
    273      /**
    274       * {@inheritDoc}
    275       *
    276       * <p>If {@code values} is not empty and the multimap already contains a
    277       * mapping for {@code key}, the {@code keySet()} ordering is unchanged.
    278       * However, the provided values always come last in the {@link #entries()} and
    279       * {@link #values()} iteration orderings.
    280       */
    281      @Override
    282      public Set<V> replaceValues(K key, Iterable<? extends V> values) {
    283        return super.replaceValues(key, values);
    284      }
    285
    286      /**
    287       * Returns a set of all key-value pairs. Changes to the returned set will
    288       * update the underlying multimap, and vice versa. The entries set does not
    289       * support the {@code add} or {@code addAll} operations.
    290       *
    291       * <p>The iterator generated by the returned set traverses the entries in the
    292       * order they were added to the multimap.
    293       *
    294       * <p>Each entry is an immutable snapshot of a key-value mapping in the
    295       * multimap, taken at the time the entry is returned by a method call to the
    296       * collection or its iterator.
    297       */
    298      @Override public Set<Map.Entry<K, V>> entries() {
    299        return super.entries();
    300      }
    301
    302      /**
    303       * Returns a collection of all values in the multimap. Changes to the returned
    304       * collection will update the underlying multimap, and vice versa.
    305       *
    306       * <p>The iterator generated by the returned collection traverses the values
    307       * in the order they were added to the multimap.
    308       */
    309      @Override public Collection<V> values() {
    310        return super.values();
    311      }
    312
    313      @VisibleForTesting
    314      final class ValueSet extends Sets.ImprovedAbstractSet<V> implements ValueSetLink<K, V> {
    315        /*
    316         * We currently use a fixed load factor of 1.0, a bit higher than normal to reduce memory
    317         * consumption.
    318         */
    319
    320        private final K key;
    321        private ValueEntry<K, V>[] hashTable;
    322        private int size = 0;
    323        private int modCount = 0;
    324
    325        // We use the set object itself as the end of the linked list, avoiding an unnecessary
    326        // entry object per key.
    327        private ValueSetLink<K, V> firstEntry;
    328        private ValueSetLink<K, V> lastEntry;
    329
    330        ValueSet(K key, int expectedValues) {
    331          this.key = key;
    332          this.firstEntry = this;
    333          this.lastEntry = this;
    334          // Round expected values up to a power of 2 to get the table size.
    335          int tableSize = Integer.highestOneBit(Math.max(expectedValues, 2) - 1) << 1;
    336          if (tableSize < 0) {
    337            tableSize = MAX_VALUE_SET_TABLE_SIZE;
    338          }
    339
    340          @SuppressWarnings("unchecked")
    341          ValueEntry<K, V>[] hashTable = new ValueEntry[tableSize];
    342          this.hashTable = hashTable;
    343        }
    344
    345        @Override
    346        public ValueSetLink<K, V> getPredecessorInValueSet() {
    347          return lastEntry;
    348        }
    349
    350        @Override
    351        public ValueSetLink<K, V> getSuccessorInValueSet() {
    352          return firstEntry;
    353        }
    354
    355        @Override
    356        public void setPredecessorInValueSet(ValueSetLink<K, V> entry) {
    357          lastEntry = entry;
    358        }
    359
    360        @Override
    361        public void setSuccessorInValueSet(ValueSetLink<K, V> entry) {
    362          firstEntry = entry;
    363        }
    364
    365        @Override
    366        public Iterator<V> iterator() {
    367          return new Iterator<V>() {
    368            ValueSetLink<K, V> nextEntry = firstEntry;
    369            ValueEntry<K, V> toRemove;
    370            int expectedModCount = modCount;
    371
    372            private void checkForComodification() {
    373              if (modCount != expectedModCount) {
    374                throw new ConcurrentModificationException();
    375              }
    376            }
    377
    378            @Override
    379            public boolean hasNext() {
    380              checkForComodification();
    381              return nextEntry != ValueSet.this;
    382            }
    383
    384            @Override
    385            public V next() {
    386              if (!hasNext()) {
    387                throw new NoSuchElementException();
    388              }
    389              ValueEntry<K, V> entry = (ValueEntry<K, V>) nextEntry;
    390              V result = entry.getValue();
    391              toRemove = entry;
    392              nextEntry = entry.getSuccessorInValueSet();
    393              return result;
    394            }
    395
    396            @Override
    397            public void remove() {
    398              checkForComodification();
    399              Iterators.checkRemove(toRemove != null);
    400              Object o = toRemove.getValue();
    401              int hash = (o == null) ? 0 : o.hashCode();
    402              int row = Hashing.smear(hash) & (hashTable.length - 1);
    403              ValueEntry<K, V> prev = null;
    404              for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
    405                   prev = entry, entry = entry.nextInValueSetHashRow) {
    406                if (entry == toRemove) {
    407                  if (prev == null) {
    408                    // first entry in row
    409                    hashTable[row] = entry.nextInValueSetHashRow;
    410                  } else {
    411                    prev.nextInValueSetHashRow = entry.nextInValueSetHashRow;
    412                  }
    413                  deleteFromValueSet(toRemove);
    414                  deleteFromMultimap(toRemove);
    415                  size--;
    416                  expectedModCount = ++modCount;
    417                  break;
    418                }
    419              }
    420              toRemove = null;
    421            }
    422          };
    423        }
    424
    425        @Override
    426        public int size() {
    427          return size;
    428        }
    429
    430        @Override
    431        public boolean contains(@Nullable Object o) {
    432          int hash = (o == null) ? 0 : o.hashCode();
    433          int row = Hashing.smear(hash) & (hashTable.length - 1);
    434
    435          for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
    436              entry = entry.nextInValueSetHashRow) {
    437            if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) {
    438              return true;
    439            }
    440          }
    441          return false;
    442        }
    443
    444        /**
    445         * The threshold above which the hash table should be rebuilt.
    446         */
    447        @VisibleForTesting int threshold() {
    448          return hashTable.length; // load factor of 1.0
    449        }
    450
    451        @Override
    452        public boolean add(@Nullable V value) {
    453          int hash = (value == null) ? 0 : value.hashCode();
    454          int row = Hashing.smear(hash) & (hashTable.length - 1);
    455
    456          ValueEntry<K, V> rowHead = hashTable[row];
    457          for (ValueEntry<K, V> entry = rowHead; entry != null;
    458              entry = entry.nextInValueSetHashRow) {
    459            if (hash == entry.valueHash && Objects.equal(value, entry.getValue())) {
    460              return false;
    461            }
    462          }
    463
    464          ValueEntry<K, V> newEntry = new ValueEntry<K, V>(key, value, hash, rowHead);
    465          succeedsInValueSet(lastEntry, newEntry);
    466          succeedsInValueSet(newEntry, this);
    467          succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry);
    468          succeedsInMultimap(newEntry, multimapHeaderEntry);
    469          hashTable[row] = newEntry;
    470          size++;
    471          modCount++;
    472          rehashIfNecessary();
    473          return true;
    474        }
    475
    476        private void rehashIfNecessary() {
    477          if (size > threshold() && hashTable.length < MAX_VALUE_SET_TABLE_SIZE) {
    478            @SuppressWarnings("unchecked")
    479            ValueEntry<K, V>[] hashTable = new ValueEntry[this.hashTable.length * 2];
    480            this.hashTable = hashTable;
    481            int mask = hashTable.length - 1;
    482            for (ValueSetLink<K, V> entry = firstEntry;
    483                  entry != this; entry = entry.getSuccessorInValueSet()) {
    484              ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
    485              int row = Hashing.smear(valueEntry.valueHash) & mask;
    486              valueEntry.nextInValueSetHashRow = hashTable[row];
    487              hashTable[row] = valueEntry;
    488            }
    489          }
    490        }
    491
    492        @Override
    493        public boolean remove(@Nullable Object o) {
    494          int hash = (o == null) ? 0 : o.hashCode();
    495          int row = Hashing.smear(hash) & (hashTable.length - 1);
    496
    497          ValueEntry<K, V> prev = null;
    498          for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
    499               prev = entry, entry = entry.nextInValueSetHashRow) {
    500            if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) {
    501              if (prev == null) {
    502                // first entry in the row
    503                hashTable[row] = entry.nextInValueSetHashRow;
    504              } else {
    505                prev.nextInValueSetHashRow = entry.nextInValueSetHashRow;
    506              }
    507              deleteFromValueSet(entry);
    508              deleteFromMultimap(entry);
    509              size--;
    510              modCount++;
    511              return true;
    512            }
    513          }
    514          return false;
    515        }
    516
    517        @Override
    518        public void clear() {
    519          Arrays.fill(hashTable, null);
    520          size = 0;
    521          for (ValueSetLink<K, V> entry = firstEntry;
    522               entry != this; entry = entry.getSuccessorInValueSet()) {
    523            ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
    524            deleteFromMultimap(valueEntry);
    525          }
    526          succeedsInValueSet(this, this);
    527          modCount++;
    528        }
    529      }
    530
    531      @Override
    532      Iterator<Map.Entry<K, V>> createEntryIterator() {
    533        return new Iterator<Map.Entry<K, V>>() {
    534          ValueEntry<K, V> nextEntry = multimapHeaderEntry.successorInMultimap;
    535          ValueEntry<K, V> toRemove;
    536
    537          @Override
    538          public boolean hasNext() {
    539            return nextEntry != multimapHeaderEntry;
    540          }
    541
    542          @Override
    543          public Map.Entry<K, V> next() {
    544            if (!hasNext()) {
    545              throw new NoSuchElementException();
    546            }
    547            ValueEntry<K, V> result = nextEntry;
    548            toRemove = result;
    549            nextEntry = nextEntry.successorInMultimap;
    550            return result;
    551          }
    552
    553          @Override
    554          public void remove() {
    555            Iterators.checkRemove(toRemove != null);
    556            LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue());
    557            toRemove = null;
    558          }
    559        };
    560      }
    561
    562      @Override
    563      public void clear() {
    564        super.clear();
    565        succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
    566      }
    567
    568      /**
    569       * @serialData the expected values per key, the number of distinct keys,
    570       * the number of entries, and the entries in order
    571       */
    572      @GwtIncompatible("java.io.ObjectOutputStream")
    573      private void writeObject(ObjectOutputStream stream) throws IOException {
    574        stream.defaultWriteObject();
    575        stream.writeInt(valueSetCapacity);
    576        stream.writeInt(keySet().size());
    577        for (K key : keySet()) {
    578          stream.writeObject(key);
    579        }
    580        stream.writeInt(size());
    581        for (Map.Entry<K, V> entry : entries()) {
    582          stream.writeObject(entry.getKey());
    583          stream.writeObject(entry.getValue());
    584        }
    585      }
    586
    587      @GwtIncompatible("java.io.ObjectInputStream")
    588      private void readObject(ObjectInputStream stream)
    589          throws IOException, ClassNotFoundException {
    590        stream.defaultReadObject();
    591        multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null);
    592        succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
    593        valueSetCapacity = stream.readInt();
    594        int distinctKeys = stream.readInt();
    595        Map<K, Collection<V>> map =
    596            new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys));
    597        for (int i = 0; i < distinctKeys; i++) {
    598          @SuppressWarnings("unchecked")
    599          K key = (K) stream.readObject();
    600          map.put(key, createCollection(key));
    601        }
    602        int entries = stream.readInt();
    603        for (int i = 0; i < entries; i++) {
    604          @SuppressWarnings("unchecked")
    605          K key = (K) stream.readObject();
    606          @SuppressWarnings("unchecked")
    607          V value = (V) stream.readObject();
    608          map.get(key).add(value);
    609        }
    610        setMap(map);
    611      }
    612
    613      @GwtIncompatible("java serialization not supported")
    614      private static final long serialVersionUID = 1;
    615    }