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