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