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      private final class ValueSet extends Sets.ImprovedAbstractSet<V> implements ValueSetLink<K, V> {
314        /*
315         * We currently use a fixed load factor of 1.0, a bit higher than normal to reduce memory
316         * consumption.
317         */
318    
319        private final K key;
320        private ValueEntry<K, V>[] hashTable;
321        private int size = 0;
322        private int modCount = 0;
323    
324        // We use the set object itself as the end of the linked list, avoiding an unnecessary
325        // entry object per key.
326        private ValueSetLink<K, V> firstEntry;
327        private ValueSetLink<K, V> lastEntry;
328    
329        ValueSet(K key, int expectedValues) {
330          this.key = key;
331          this.firstEntry = this;
332          this.lastEntry = this;
333          // Round expected values up to a power of 2 to get the table size.
334          int tableSize = Integer.highestOneBit(Math.max(expectedValues, 2) - 1) << 1;
335          if (tableSize < 0) {
336            tableSize = MAX_VALUE_SET_TABLE_SIZE;
337          }
338    
339          @SuppressWarnings("unchecked")
340          ValueEntry<K, V>[] hashTable = new ValueEntry[tableSize];
341          this.hashTable = hashTable;
342        }
343    
344        @Override
345        public ValueSetLink<K, V> getPredecessorInValueSet() {
346          return lastEntry;
347        }
348    
349        @Override
350        public ValueSetLink<K, V> getSuccessorInValueSet() {
351          return firstEntry;
352        }
353    
354        @Override
355        public void setPredecessorInValueSet(ValueSetLink<K, V> entry) {
356          lastEntry = entry;
357        }
358    
359        @Override
360        public void setSuccessorInValueSet(ValueSetLink<K, V> entry) {
361          firstEntry = entry;
362        }
363    
364        @Override
365        public Iterator<V> iterator() {
366          return new Iterator<V>() {
367            ValueSetLink<K, V> nextEntry = firstEntry;
368            ValueEntry<K, V> toRemove;
369            int expectedModCount = modCount;
370    
371            private void checkForComodification() {
372              if (modCount != expectedModCount) {
373                throw new ConcurrentModificationException();
374              }
375            }
376    
377            @Override
378            public boolean hasNext() {
379              checkForComodification();
380              return nextEntry != ValueSet.this;
381            }
382    
383            @Override
384            public V next() {
385              if (!hasNext()) {
386                throw new NoSuchElementException();
387              }
388              ValueEntry<K, V> entry = (ValueEntry<K, V>) nextEntry;
389              V result = entry.getValue();
390              toRemove = entry;
391              nextEntry = entry.getSuccessorInValueSet();
392              return result;
393            }
394    
395            @Override
396            public void remove() {
397              checkForComodification();
398              Iterators.checkRemove(toRemove != null);
399              Object o = toRemove.getValue();
400              int hash = (o == null) ? 0 : o.hashCode();
401              int row = Hashing.smear(hash) & (hashTable.length - 1);
402              ValueEntry<K, V> prev = null;
403              for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
404                   prev = entry, entry = entry.nextInValueSetHashRow) {
405                if (entry == toRemove) {
406                  if (prev == null) {
407                    // first entry in row
408                    hashTable[row] = entry.nextInValueSetHashRow;
409                  } else {
410                    prev.nextInValueSetHashRow = entry.nextInValueSetHashRow;
411                  }
412                  deleteFromValueSet(toRemove);
413                  deleteFromMultimap(toRemove);
414                  size--;
415                  expectedModCount = ++modCount;
416                  break;
417                }
418              }
419              toRemove = null;
420            }
421          };
422        }
423    
424        @Override
425        public int size() {
426          return size;
427        }
428    
429        @Override
430        public boolean contains(@Nullable Object o) {
431          int hash = (o == null) ? 0 : o.hashCode();
432          int row = Hashing.smear(hash) & (hashTable.length - 1);
433    
434          for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
435              entry = entry.nextInValueSetHashRow) {
436            if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) {
437              return true;
438            }
439          }
440          return false;
441        }
442    
443        @Override
444        public boolean add(@Nullable V value) {
445          int hash = (value == null) ? 0 : value.hashCode();
446          int row = Hashing.smear(hash) & (hashTable.length - 1);
447    
448          ValueEntry<K, V> rowHead = hashTable[row];
449          for (ValueEntry<K, V> entry = rowHead; entry != null;
450              entry = entry.nextInValueSetHashRow) {
451            if (hash == entry.valueHash && Objects.equal(value, entry.getValue())) {
452              return false;
453            }
454          }
455    
456          ValueEntry<K, V> newEntry = new ValueEntry<K, V>(key, value, hash, rowHead);
457          succeedsInValueSet(lastEntry, newEntry);
458          succeedsInValueSet(newEntry, this);
459          succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry);
460          succeedsInMultimap(newEntry, multimapHeaderEntry);
461          hashTable[row] = newEntry;
462          size++;
463          modCount++;
464          rehashIfNecessary();
465          return true;
466        }
467    
468        private void rehashIfNecessary() {
469          if (size > hashTable.length && hashTable.length < MAX_VALUE_SET_TABLE_SIZE) {
470            @SuppressWarnings("unchecked")
471            ValueEntry<K, V>[] hashTable = new ValueEntry[this.hashTable.length * 2];
472            this.hashTable = hashTable;
473            int mask = hashTable.length - 1;
474            for (ValueSetLink<K, V> entry = firstEntry;
475                  entry != this; entry = entry.getSuccessorInValueSet()) {
476              ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
477              int row = Hashing.smear(valueEntry.valueHash) & mask;
478              valueEntry.nextInValueSetHashRow = hashTable[row];
479              hashTable[row] = valueEntry;
480            }
481          }
482        }
483    
484        @Override
485        public boolean remove(@Nullable Object o) {
486          int hash = (o == null) ? 0 : o.hashCode();
487          int row = Hashing.smear(hash) & (hashTable.length - 1);
488    
489          ValueEntry<K, V> prev = null;
490          for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
491               prev = entry, entry = entry.nextInValueSetHashRow) {
492            if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) {
493              if (prev == null) {
494                // first entry in the row
495                hashTable[row] = entry.nextInValueSetHashRow;
496              } else {
497                prev.nextInValueSetHashRow = entry.nextInValueSetHashRow;
498              }
499              deleteFromValueSet(entry);
500              deleteFromMultimap(entry);
501              size--;
502              modCount++;
503              return true;
504            }
505          }
506          return false;
507        }
508    
509        @Override
510        public void clear() {
511          Arrays.fill(hashTable, null);
512          size = 0;
513          for (ValueSetLink<K, V> entry = firstEntry;
514               entry != this; entry = entry.getSuccessorInValueSet()) {
515            ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
516            deleteFromMultimap(valueEntry);
517          }
518          succeedsInValueSet(this, this);
519          modCount++;
520        }
521      }
522    
523      @Override
524      Iterator<Map.Entry<K, V>> createEntryIterator() {
525        return new Iterator<Map.Entry<K, V>>() {
526          ValueEntry<K, V> nextEntry = multimapHeaderEntry.successorInMultimap;
527          ValueEntry<K, V> toRemove;
528    
529          @Override
530          public boolean hasNext() {
531            return nextEntry != multimapHeaderEntry;
532          }
533    
534          @Override
535          public Map.Entry<K, V> next() {
536            if (!hasNext()) {
537              throw new NoSuchElementException();
538            }
539            ValueEntry<K, V> result = nextEntry;
540            toRemove = result;
541            nextEntry = nextEntry.successorInMultimap;
542            return result;
543          }
544    
545          @Override
546          public void remove() {
547            Iterators.checkRemove(toRemove != null);
548            LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue());
549            toRemove = null;
550          }
551        };
552      }
553    
554      @Override
555      public void clear() {
556        super.clear();
557        succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
558      }
559    
560      /**
561       * @serialData the expected values per key, the number of distinct keys,
562       * the number of entries, and the entries in order
563       */
564      @GwtIncompatible("java.io.ObjectOutputStream")
565      private void writeObject(ObjectOutputStream stream) throws IOException {
566        stream.defaultWriteObject();
567        stream.writeInt(valueSetCapacity);
568        stream.writeInt(keySet().size());
569        for (K key : keySet()) {
570          stream.writeObject(key);
571        }
572        stream.writeInt(size());
573        for (Map.Entry<K, V> entry : entries()) {
574          stream.writeObject(entry.getKey());
575          stream.writeObject(entry.getValue());
576        }
577      }
578    
579      @GwtIncompatible("java.io.ObjectInputStream")
580      private void readObject(ObjectInputStream stream)
581          throws IOException, ClassNotFoundException {
582        stream.defaultReadObject();
583        multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null);
584        succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
585        valueSetCapacity = stream.readInt();
586        int distinctKeys = stream.readInt();
587        Map<K, Collection<V>> map =
588            new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys));
589        for (int i = 0; i < distinctKeys; i++) {
590          @SuppressWarnings("unchecked")
591          K key = (K) stream.readObject();
592          map.put(key, createCollection(key));
593        }
594        int entries = stream.readInt();
595        for (int i = 0; i < entries; i++) {
596          @SuppressWarnings("unchecked")
597          K key = (K) stream.readObject();
598          @SuppressWarnings("unchecked")
599          V value = (V) stream.readObject();
600          map.get(key).add(value);
601        }
602        setMap(map);
603      }
604    
605      @GwtIncompatible("java serialization not supported")
606      private static final long serialVersionUID = 1;
607    }