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    }