Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2007 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect;
     18 
     19 import static com.google.common.collect.CollectPreconditions.checkNonnegative;
     20 import static com.google.common.collect.CollectPreconditions.checkRemove;
     21 
     22 import com.google.common.annotations.GwtCompatible;
     23 import com.google.common.annotations.GwtIncompatible;
     24 import com.google.common.annotations.VisibleForTesting;
     25 import com.google.common.base.Objects;
     26 
     27 import java.io.IOException;
     28 import java.io.ObjectInputStream;
     29 import java.io.ObjectOutputStream;
     30 import java.util.Arrays;
     31 import java.util.Collection;
     32 import java.util.ConcurrentModificationException;
     33 import java.util.Iterator;
     34 import java.util.LinkedHashMap;
     35 import java.util.LinkedHashSet;
     36 import java.util.Map;
     37 import java.util.NoSuchElementException;
     38 import java.util.Set;
     39 
     40 import javax.annotation.Nullable;
     41 
     42 /**
     43  * Implementation of {@code Multimap} that does not allow duplicate key-value
     44  * entries and that returns collections whose iterators follow the ordering in
     45  * which the data was added to the multimap.
     46  *
     47  * <p>The collections returned by {@code keySet}, {@code keys}, and {@code
     48  * asMap} iterate through the keys in the order they were first added to the
     49  * multimap. Similarly, {@code get}, {@code removeAll}, and {@code
     50  * replaceValues} return collections that iterate through the values in the
     51  * order they were added. The collections generated by {@code entries} and
     52  * {@code values} iterate across the key-value mappings in the order they were
     53  * added to the multimap.
     54  *
     55  * <p>The iteration ordering of the collections generated by {@code keySet},
     56  * {@code keys}, and {@code asMap} has a few subtleties. As long as the set of
     57  * keys remains unchanged, adding or removing mappings does not affect the key
     58  * iteration order. However, if you remove all values associated with a key and
     59  * then add the key back to the multimap, that key will come last in the key
     60  * iteration order.
     61  *
     62  * <p>The multimap does not store duplicate key-value pairs. Adding a new
     63  * key-value pair equal to an existing key-value pair has no effect.
     64  *
     65  * <p>Keys and values may be null. All optional multimap methods are supported,
     66  * and all returned views are modifiable.
     67  *
     68  * <p>This class is not threadsafe when any concurrent operations update the
     69  * multimap. Concurrent read operations will work correctly. To allow concurrent
     70  * update operations, wrap your multimap with a call to {@link
     71  * Multimaps#synchronizedSetMultimap}.
     72  *
     73  * <p>See the Guava User Guide article on <a href=
     74  * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap">
     75  * {@code Multimap}</a>.
     76  *
     77  * @author Jared Levy
     78  * @author Louis Wasserman
     79  * @since 2.0 (imported from Google Collections Library)
     80  */
     81 @GwtCompatible(serializable = true, emulated = true)
     82 public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> {
     83 
     84   /**
     85    * Creates a new, empty {@code LinkedHashMultimap} with the default initial
     86    * capacities.
     87    */
     88   public static <K, V> LinkedHashMultimap<K, V> create() {
     89     return new LinkedHashMultimap<K, V>(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY);
     90   }
     91 
     92   /**
     93    * Constructs an empty {@code LinkedHashMultimap} with enough capacity to hold
     94    * the specified numbers of keys and values without rehashing.
     95    *
     96    * @param expectedKeys the expected number of distinct keys
     97    * @param expectedValuesPerKey the expected average number of values per key
     98    * @throws IllegalArgumentException if {@code expectedKeys} or {@code
     99    *      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 bucket 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   @VisibleForTesting
    158   static final class ValueEntry<K, V> extends ImmutableEntry<K, V>
    159       implements ValueSetLink<K, V> {
    160     final int smearedValueHash;
    161 
    162     @Nullable ValueEntry<K, V> nextInValueBucket;
    163 
    164     ValueSetLink<K, V> predecessorInValueSet;
    165     ValueSetLink<K, V> successorInValueSet;
    166 
    167     ValueEntry<K, V> predecessorInMultimap;
    168     ValueEntry<K, V> successorInMultimap;
    169 
    170     ValueEntry(@Nullable K key, @Nullable V value, int smearedValueHash,
    171         @Nullable ValueEntry<K, V> nextInValueBucket) {
    172       super(key, value);
    173       this.smearedValueHash = smearedValueHash;
    174       this.nextInValueBucket = nextInValueBucket;
    175     }
    176 
    177     boolean matchesValue(@Nullable Object v, int smearedVHash) {
    178       return smearedValueHash == smearedVHash && Objects.equal(getValue(), v);
    179     }
    180 
    181     @Override
    182     public ValueSetLink<K, V> getPredecessorInValueSet() {
    183       return predecessorInValueSet;
    184     }
    185 
    186     @Override
    187     public ValueSetLink<K, V> getSuccessorInValueSet() {
    188       return successorInValueSet;
    189     }
    190 
    191     @Override
    192     public void setPredecessorInValueSet(ValueSetLink<K, V> entry) {
    193       predecessorInValueSet = entry;
    194     }
    195 
    196     @Override
    197     public void setSuccessorInValueSet(ValueSetLink<K, V> entry) {
    198       successorInValueSet = entry;
    199     }
    200 
    201     public ValueEntry<K, V> getPredecessorInMultimap() {
    202       return predecessorInMultimap;
    203     }
    204 
    205     public ValueEntry<K, V> getSuccessorInMultimap() {
    206       return successorInMultimap;
    207     }
    208 
    209     public void setSuccessorInMultimap(ValueEntry<K, V> multimapSuccessor) {
    210       this.successorInMultimap = multimapSuccessor;
    211     }
    212 
    213     public void setPredecessorInMultimap(ValueEntry<K, V> multimapPredecessor) {
    214       this.predecessorInMultimap = multimapPredecessor;
    215     }
    216   }
    217 
    218   private static final int DEFAULT_KEY_CAPACITY = 16;
    219   private static final int DEFAULT_VALUE_SET_CAPACITY = 2;
    220   @VisibleForTesting static final double VALUE_SET_LOAD_FACTOR = 1.0;
    221 
    222   @VisibleForTesting transient int valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY;
    223   private transient ValueEntry<K, V> multimapHeaderEntry;
    224 
    225   private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) {
    226     super(new LinkedHashMap<K, Collection<V>>(keyCapacity));
    227     checkNonnegative(valueSetCapacity, "expectedValuesPerKey");
    228 
    229     this.valueSetCapacity = valueSetCapacity;
    230     this.multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null);
    231     succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
    232   }
    233 
    234   /**
    235    * {@inheritDoc}
    236    *
    237    * <p>Creates an empty {@code LinkedHashSet} for a collection of values for
    238    * one key.
    239    *
    240    * @return a new {@code LinkedHashSet} containing a collection of values for
    241    *     one key
    242    */
    243   @Override
    244   Set<V> createCollection() {
    245     return new LinkedHashSet<V>(valueSetCapacity);
    246   }
    247 
    248   /**
    249    * {@inheritDoc}
    250    *
    251    * <p>Creates a decorated insertion-ordered set that also keeps track of the
    252    * order in which key-value pairs are added to the multimap.
    253    *
    254    * @param key key to associate with values in the collection
    255    * @return a new decorated set containing a collection of values for one key
    256    */
    257   @Override
    258   Collection<V> createCollection(K key) {
    259     return new ValueSet(key, valueSetCapacity);
    260   }
    261 
    262   /**
    263    * {@inheritDoc}
    264    *
    265    * <p>If {@code values} is not empty and the multimap already contains a
    266    * mapping for {@code key}, the {@code keySet()} ordering is unchanged.
    267    * However, the provided values always come last in the {@link #entries()} and
    268    * {@link #values()} iteration orderings.
    269    */
    270   @Override
    271   public Set<V> replaceValues(@Nullable K key, Iterable<? extends V> values) {
    272     return super.replaceValues(key, values);
    273   }
    274 
    275   /**
    276    * Returns a set of all key-value pairs. Changes to the returned set will
    277    * update the underlying multimap, and vice versa. The entries set does not
    278    * support the {@code add} or {@code addAll} operations.
    279    *
    280    * <p>The iterator generated by the returned set traverses the entries in the
    281    * order they were added to the multimap.
    282    *
    283    * <p>Each entry is an immutable snapshot of a key-value mapping in the
    284    * multimap, taken at the time the entry is returned by a method call to the
    285    * collection or its iterator.
    286    */
    287   @Override public Set<Map.Entry<K, V>> entries() {
    288     return super.entries();
    289   }
    290 
    291   /**
    292    * Returns a collection of all values in the multimap. Changes to the returned
    293    * collection will update the underlying multimap, and vice versa.
    294    *
    295    * <p>The iterator generated by the returned collection traverses the values
    296    * in the order they were added to the multimap.
    297    */
    298   @Override public Collection<V> values() {
    299     return super.values();
    300   }
    301 
    302   @VisibleForTesting
    303   final class ValueSet extends Sets.ImprovedAbstractSet<V> implements ValueSetLink<K, V> {
    304     /*
    305      * We currently use a fixed load factor of 1.0, a bit higher than normal to reduce memory
    306      * consumption.
    307      */
    308 
    309     private final K key;
    310     @VisibleForTesting ValueEntry<K, V>[] hashTable;
    311     private int size = 0;
    312     private int modCount = 0;
    313 
    314     // We use the set object itself as the end of the linked list, avoiding an unnecessary
    315     // entry object per key.
    316     private ValueSetLink<K, V> firstEntry;
    317     private ValueSetLink<K, V> lastEntry;
    318 
    319     ValueSet(K key, int expectedValues) {
    320       this.key = key;
    321       this.firstEntry = this;
    322       this.lastEntry = this;
    323       // Round expected values up to a power of 2 to get the table size.
    324       int tableSize = Hashing.closedTableSize(expectedValues, VALUE_SET_LOAD_FACTOR);
    325 
    326       @SuppressWarnings("unchecked")
    327       ValueEntry<K, V>[] hashTable = new ValueEntry[tableSize];
    328       this.hashTable = hashTable;
    329     }
    330 
    331     private int mask() {
    332       return hashTable.length - 1;
    333     }
    334 
    335     @Override
    336     public ValueSetLink<K, V> getPredecessorInValueSet() {
    337       return lastEntry;
    338     }
    339 
    340     @Override
    341     public ValueSetLink<K, V> getSuccessorInValueSet() {
    342       return firstEntry;
    343     }
    344 
    345     @Override
    346     public void setPredecessorInValueSet(ValueSetLink<K, V> entry) {
    347       lastEntry = entry;
    348     }
    349 
    350     @Override
    351     public void setSuccessorInValueSet(ValueSetLink<K, V> entry) {
    352       firstEntry = entry;
    353     }
    354 
    355     @Override
    356     public Iterator<V> iterator() {
    357       return new Iterator<V>() {
    358         ValueSetLink<K, V> nextEntry = firstEntry;
    359         ValueEntry<K, V> toRemove;
    360         int expectedModCount = modCount;
    361 
    362         private void checkForComodification() {
    363           if (modCount != expectedModCount) {
    364             throw new ConcurrentModificationException();
    365           }
    366         }
    367 
    368         @Override
    369         public boolean hasNext() {
    370           checkForComodification();
    371           return nextEntry != ValueSet.this;
    372         }
    373 
    374         @Override
    375         public V next() {
    376           if (!hasNext()) {
    377             throw new NoSuchElementException();
    378           }
    379           ValueEntry<K, V> entry = (ValueEntry<K, V>) nextEntry;
    380           V result = entry.getValue();
    381           toRemove = entry;
    382           nextEntry = entry.getSuccessorInValueSet();
    383           return result;
    384         }
    385 
    386         @Override
    387         public void remove() {
    388           checkForComodification();
    389           checkRemove(toRemove != null);
    390           ValueSet.this.remove(toRemove.getValue());
    391           expectedModCount = modCount;
    392           toRemove = null;
    393         }
    394       };
    395     }
    396 
    397     @Override
    398     public int size() {
    399       return size;
    400     }
    401 
    402     @Override
    403     public boolean contains(@Nullable Object o) {
    404       int smearedHash = Hashing.smearedHash(o);
    405       for (ValueEntry<K, V> entry = hashTable[smearedHash & mask()]; entry != null;
    406           entry = entry.nextInValueBucket) {
    407         if (entry.matchesValue(o, smearedHash)) {
    408           return true;
    409         }
    410       }
    411       return false;
    412     }
    413 
    414     @Override
    415     public boolean add(@Nullable V value) {
    416       int smearedHash = Hashing.smearedHash(value);
    417       int bucket = smearedHash & mask();
    418       ValueEntry<K, V> rowHead = hashTable[bucket];
    419       for (ValueEntry<K, V> entry = rowHead; entry != null;
    420           entry = entry.nextInValueBucket) {
    421         if (entry.matchesValue(value, smearedHash)) {
    422           return false;
    423         }
    424       }
    425 
    426       ValueEntry<K, V> newEntry = new ValueEntry<K, V>(key, value, smearedHash, rowHead);
    427       succeedsInValueSet(lastEntry, newEntry);
    428       succeedsInValueSet(newEntry, this);
    429       succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry);
    430       succeedsInMultimap(newEntry, multimapHeaderEntry);
    431       hashTable[bucket] = newEntry;
    432       size++;
    433       modCount++;
    434       rehashIfNecessary();
    435       return true;
    436     }
    437 
    438     private void rehashIfNecessary() {
    439       if (Hashing.needsResizing(size, hashTable.length, VALUE_SET_LOAD_FACTOR)) {
    440         @SuppressWarnings("unchecked")
    441         ValueEntry<K, V>[] hashTable = new ValueEntry[this.hashTable.length * 2];
    442         this.hashTable = hashTable;
    443         int mask = hashTable.length - 1;
    444         for (ValueSetLink<K, V> entry = firstEntry;
    445             entry != this; entry = entry.getSuccessorInValueSet()) {
    446           ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
    447           int bucket = valueEntry.smearedValueHash & mask;
    448           valueEntry.nextInValueBucket = hashTable[bucket];
    449           hashTable[bucket] = valueEntry;
    450         }
    451       }
    452     }
    453 
    454     @Override
    455     public boolean remove(@Nullable Object o) {
    456       int smearedHash = Hashing.smearedHash(o);
    457       int bucket = smearedHash & mask();
    458       ValueEntry<K, V> prev = null;
    459       for (ValueEntry<K, V> entry = hashTable[bucket]; entry != null;
    460            prev = entry, entry = entry.nextInValueBucket) {
    461         if (entry.matchesValue(o, smearedHash)) {
    462           if (prev == null) {
    463             // first entry in the bucket
    464             hashTable[bucket] = entry.nextInValueBucket;
    465           } else {
    466             prev.nextInValueBucket = entry.nextInValueBucket;
    467           }
    468           deleteFromValueSet(entry);
    469           deleteFromMultimap(entry);
    470           size--;
    471           modCount++;
    472           return true;
    473         }
    474       }
    475       return false;
    476     }
    477 
    478     @Override
    479     public void clear() {
    480       Arrays.fill(hashTable, null);
    481       size = 0;
    482       for (ValueSetLink<K, V> entry = firstEntry;
    483            entry != this; entry = entry.getSuccessorInValueSet()) {
    484         ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
    485         deleteFromMultimap(valueEntry);
    486       }
    487       succeedsInValueSet(this, this);
    488       modCount++;
    489     }
    490   }
    491 
    492   @Override
    493   Iterator<Map.Entry<K, V>> entryIterator() {
    494     return new Iterator<Map.Entry<K, V>>() {
    495       ValueEntry<K, V> nextEntry = multimapHeaderEntry.successorInMultimap;
    496       ValueEntry<K, V> toRemove;
    497 
    498       @Override
    499       public boolean hasNext() {
    500         return nextEntry != multimapHeaderEntry;
    501       }
    502 
    503       @Override
    504       public Map.Entry<K, V> next() {
    505         if (!hasNext()) {
    506           throw new NoSuchElementException();
    507         }
    508         ValueEntry<K, V> result = nextEntry;
    509         toRemove = result;
    510         nextEntry = nextEntry.successorInMultimap;
    511         return result;
    512       }
    513 
    514       @Override
    515       public void remove() {
    516         checkRemove(toRemove != null);
    517         LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue());
    518         toRemove = null;
    519       }
    520     };
    521   }
    522 
    523   @Override
    524   Iterator<V> valueIterator() {
    525     return Maps.valueIterator(entryIterator());
    526   }
    527 
    528   @Override
    529   public void clear() {
    530     super.clear();
    531     succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
    532   }
    533 
    534   /**
    535    * @serialData the expected values per key, the number of distinct keys,
    536    * the number of entries, and the entries in order
    537    */
    538   @GwtIncompatible("java.io.ObjectOutputStream")
    539   private void writeObject(ObjectOutputStream stream) throws IOException {
    540     stream.defaultWriteObject();
    541     stream.writeInt(valueSetCapacity);
    542     stream.writeInt(keySet().size());
    543     for (K key : keySet()) {
    544       stream.writeObject(key);
    545     }
    546     stream.writeInt(size());
    547     for (Map.Entry<K, V> entry : entries()) {
    548       stream.writeObject(entry.getKey());
    549       stream.writeObject(entry.getValue());
    550     }
    551   }
    552 
    553   @GwtIncompatible("java.io.ObjectInputStream")
    554   private void readObject(ObjectInputStream stream)
    555       throws IOException, ClassNotFoundException {
    556     stream.defaultReadObject();
    557     multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null);
    558     succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
    559     valueSetCapacity = stream.readInt();
    560     int distinctKeys = stream.readInt();
    561     Map<K, Collection<V>> map =
    562         new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys));
    563     for (int i = 0; i < distinctKeys; i++) {
    564       @SuppressWarnings("unchecked")
    565       K key = (K) stream.readObject();
    566       map.put(key, createCollection(key));
    567     }
    568     int entries = stream.readInt();
    569     for (int i = 0; i < entries; i++) {
    570       @SuppressWarnings("unchecked")
    571       K key = (K) stream.readObject();
    572       @SuppressWarnings("unchecked")
    573       V value = (V) stream.readObject();
    574       map.get(key).add(value);
    575     }
    576     setMap(map);
    577   }
    578 
    579   @GwtIncompatible("java serialization not supported")
    580   private static final long serialVersionUID = 1;
    581 }
    582