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