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.checkArgument; 020 021import com.google.common.annotations.GwtCompatible; 022import com.google.common.annotations.GwtIncompatible; 023import com.google.common.annotations.VisibleForTesting; 024import com.google.common.base.Objects; 025 026import java.io.IOException; 027import java.io.ObjectInputStream; 028import java.io.ObjectOutputStream; 029import java.util.Arrays; 030import java.util.Collection; 031import java.util.ConcurrentModificationException; 032import java.util.Iterator; 033import java.util.LinkedHashMap; 034import java.util.LinkedHashSet; 035import java.util.Map; 036import java.util.NoSuchElementException; 037import java.util.Set; 038 039import javax.annotation.Nullable; 040 041/** 042 * Implementation of {@code Multimap} that does not allow duplicate key-value 043 * entries and that returns collections whose iterators follow the ordering in 044 * which the data was added to the multimap. 045 * 046 * <p>The collections returned by {@code keySet}, {@code keys}, and {@code 047 * asMap} iterate through the keys in the order they were first added to the 048 * multimap. Similarly, {@code get}, {@code removeAll}, and {@code 049 * replaceValues} return collections that iterate through the values in the 050 * order they were added. The collections generated by {@code entries} and 051 * {@code values} iterate across the key-value mappings in the order they were 052 * added to the multimap. 053 * 054 * <p>The iteration ordering of the collections generated by {@code keySet}, 055 * {@code keys}, and {@code asMap} has a few subtleties. As long as the set of 056 * keys remains unchanged, adding or removing mappings does not affect the key 057 * iteration order. However, if you remove all values associated with a key and 058 * then add the key back to the multimap, that key will come last in the key 059 * iteration order. 060 * 061 * <p>The multimap does not store duplicate key-value pairs. Adding a new 062 * key-value pair equal to an existing key-value pair has no effect. 063 * 064 * <p>Keys and values may be null. All optional multimap methods are supported, 065 * and all returned views are modifiable. 066 * 067 * <p>This class is not threadsafe when any concurrent operations update the 068 * multimap. Concurrent read operations will work correctly. To allow concurrent 069 * update operations, wrap your multimap with a call to {@link 070 * Multimaps#synchronizedSetMultimap}. 071 * 072 * <p>See the Guava User Guide article on <a href= 073 * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> 074 * {@code Multimap}</a>. 075 * 076 * @author Jared Levy 077 * @author Louis Wasserman 078 * @since 2.0 (imported from Google Collections Library) 079 */ 080@GwtCompatible(serializable = true, emulated = true) 081public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { 082 083 /** 084 * Creates a new, empty {@code LinkedHashMultimap} with the default initial 085 * capacities. 086 */ 087 public static <K, V> LinkedHashMultimap<K, V> create() { 088 return new LinkedHashMultimap<K, V>(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY); 089 } 090 091 /** 092 * Constructs an empty {@code LinkedHashMultimap} with enough capacity to hold 093 * the specified 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 098 * expectedValuesPerKey} is negative 099 */ 100 public static <K, V> LinkedHashMultimap<K, V> create( 101 int expectedKeys, int expectedValuesPerKey) { 102 return new LinkedHashMultimap<K, V>( 103 Maps.capacity(expectedKeys), 104 Maps.capacity(expectedValuesPerKey)); 105 } 106 107 /** 108 * Constructs a {@code LinkedHashMultimap} with the same mappings as the 109 * specified multimap. If a key-value mapping appears multiple times in the 110 * input multimap, it only appears once in the constructed multimap. The new 111 * multimap has the same {@link Multimap#entries()} iteration order as the 112 * input multimap, except for excluding duplicate mappings. 113 * 114 * @param multimap the multimap whose contents are copied to this multimap 115 */ 116 public static <K, V> LinkedHashMultimap<K, V> create( 117 Multimap<? extends K, ? extends V> multimap) { 118 LinkedHashMultimap<K, V> result = create(multimap.keySet().size(), DEFAULT_VALUE_SET_CAPACITY); 119 result.putAll(multimap); 120 return result; 121 } 122 123 private interface ValueSetLink<K, V> { 124 ValueSetLink<K, V> getPredecessorInValueSet(); 125 ValueSetLink<K, V> getSuccessorInValueSet(); 126 127 void setPredecessorInValueSet(ValueSetLink<K, V> entry); 128 void setSuccessorInValueSet(ValueSetLink<K, V> entry); 129 } 130 131 private static <K, V> void succeedsInValueSet(ValueSetLink<K, V> pred, ValueSetLink<K, V> succ) { 132 pred.setSuccessorInValueSet(succ); 133 succ.setPredecessorInValueSet(pred); 134 } 135 136 private static <K, V> void succeedsInMultimap( 137 ValueEntry<K, V> pred, ValueEntry<K, V> succ) { 138 pred.setSuccessorInMultimap(succ); 139 succ.setPredecessorInMultimap(pred); 140 } 141 142 private static <K, V> void deleteFromValueSet(ValueSetLink<K, V> entry) { 143 succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet()); 144 } 145 146 private static <K, V> void deleteFromMultimap(ValueEntry<K, V> entry) { 147 succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap()); 148 } 149 150 /** 151 * LinkedHashMultimap entries are in no less than three coexisting linked lists: 152 * a row in the hash table for a Set<V> associated with a key, the linked list 153 * of insertion-ordered entries in that Set<V>, and the linked list of entries 154 * in the LinkedHashMultimap as a whole. 155 */ 156 @VisibleForTesting 157 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 @VisibleForTesting static final double VALUE_SET_LOAD_FACTOR = 1.0; 229 230 @VisibleForTesting transient int valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY; 231 private transient ValueEntry<K, V> multimapHeaderEntry; 232 233 private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) { 234 super(new LinkedHashMap<K, Collection<V>>(keyCapacity)); 235 236 checkArgument(valueSetCapacity >= 0, 237 "expectedValuesPerKey must be >= 0 but was %s", valueSetCapacity); 238 239 this.valueSetCapacity = valueSetCapacity; 240 this.multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null); 241 succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); 242 } 243 244 /** 245 * {@inheritDoc} 246 * 247 * <p>Creates an empty {@code LinkedHashSet} for a collection of values for 248 * one key. 249 * 250 * @return a new {@code LinkedHashSet} containing a collection of values for 251 * one key 252 */ 253 @Override 254 Set<V> createCollection() { 255 return new LinkedHashSet<V>(valueSetCapacity); 256 } 257 258 /** 259 * {@inheritDoc} 260 * 261 * <p>Creates a decorated insertion-ordered set that also keeps track of the 262 * order in which key-value pairs are added to the multimap. 263 * 264 * @param key key to associate with values in the collection 265 * @return a new decorated set containing a collection of values for one key 266 */ 267 @Override 268 Collection<V> createCollection(K key) { 269 return new ValueSet(key, valueSetCapacity); 270 } 271 272 /** 273 * {@inheritDoc} 274 * 275 * <p>If {@code values} is not empty and the multimap already contains a 276 * mapping for {@code key}, the {@code keySet()} ordering is unchanged. 277 * However, the provided values always come last in the {@link #entries()} and 278 * {@link #values()} iteration orderings. 279 */ 280 @Override 281 public Set<V> replaceValues(@Nullable K key, Iterable<? extends V> values) { 282 return super.replaceValues(key, values); 283 } 284 285 /** 286 * Returns a set of all key-value pairs. Changes to the returned set will 287 * update the underlying multimap, and vice versa. The entries set does not 288 * support the {@code add} or {@code addAll} operations. 289 * 290 * <p>The iterator generated by the returned set traverses the entries in the 291 * order they were added to the multimap. 292 * 293 * <p>Each entry is an immutable snapshot of a key-value mapping in the 294 * multimap, taken at the time the entry is returned by a method call to the 295 * collection or its iterator. 296 */ 297 @Override public Set<Map.Entry<K, V>> entries() { 298 return super.entries(); 299 } 300 301 /** 302 * Returns a collection of all values in the multimap. Changes to the returned 303 * collection will update the underlying multimap, and vice versa. 304 * 305 * <p>The iterator generated by the returned collection traverses the values 306 * in the order they were added to the multimap. 307 */ 308 @Override public Collection<V> values() { 309 return super.values(); 310 } 311 312 @VisibleForTesting 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 @Override 342 public ValueSetLink<K, V> getPredecessorInValueSet() { 343 return lastEntry; 344 } 345 346 @Override 347 public ValueSetLink<K, V> getSuccessorInValueSet() { 348 return firstEntry; 349 } 350 351 @Override 352 public void setPredecessorInValueSet(ValueSetLink<K, V> entry) { 353 lastEntry = entry; 354 } 355 356 @Override 357 public void setSuccessorInValueSet(ValueSetLink<K, V> entry) { 358 firstEntry = entry; 359 } 360 361 @Override 362 public Iterator<V> iterator() { 363 return new Iterator<V>() { 364 ValueSetLink<K, V> nextEntry = firstEntry; 365 ValueEntry<K, V> toRemove; 366 int expectedModCount = modCount; 367 368 private void checkForComodification() { 369 if (modCount != expectedModCount) { 370 throw new ConcurrentModificationException(); 371 } 372 } 373 374 @Override 375 public boolean hasNext() { 376 checkForComodification(); 377 return nextEntry != ValueSet.this; 378 } 379 380 @Override 381 public V next() { 382 if (!hasNext()) { 383 throw new NoSuchElementException(); 384 } 385 ValueEntry<K, V> entry = (ValueEntry<K, V>) nextEntry; 386 V result = entry.getValue(); 387 toRemove = entry; 388 nextEntry = entry.getSuccessorInValueSet(); 389 return result; 390 } 391 392 @Override 393 public void remove() { 394 checkForComodification(); 395 Iterators.checkRemove(toRemove != null); 396 Object o = toRemove.getValue(); 397 int hash = (o == null) ? 0 : o.hashCode(); 398 int row = Hashing.smear(hash) & (hashTable.length - 1); 399 ValueEntry<K, V> prev = null; 400 for (ValueEntry<K, V> entry = hashTable[row]; entry != null; 401 prev = entry, entry = entry.nextInValueSetHashRow) { 402 if (entry == toRemove) { 403 if (prev == null) { 404 // first entry in row 405 hashTable[row] = entry.nextInValueSetHashRow; 406 } else { 407 prev.nextInValueSetHashRow = entry.nextInValueSetHashRow; 408 } 409 deleteFromValueSet(toRemove); 410 deleteFromMultimap(toRemove); 411 size--; 412 expectedModCount = ++modCount; 413 break; 414 } 415 } 416 toRemove = null; 417 } 418 }; 419 } 420 421 @Override 422 public int size() { 423 return size; 424 } 425 426 @Override 427 public boolean contains(@Nullable Object o) { 428 int hash = (o == null) ? 0 : o.hashCode(); 429 int row = Hashing.smear(hash) & (hashTable.length - 1); 430 431 for (ValueEntry<K, V> entry = hashTable[row]; entry != null; 432 entry = entry.nextInValueSetHashRow) { 433 if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) { 434 return true; 435 } 436 } 437 return false; 438 } 439 440 @Override 441 public boolean add(@Nullable V value) { 442 int hash = (value == null) ? 0 : value.hashCode(); 443 int row = Hashing.smear(hash) & (hashTable.length - 1); 444 445 ValueEntry<K, V> rowHead = hashTable[row]; 446 for (ValueEntry<K, V> entry = rowHead; entry != null; 447 entry = entry.nextInValueSetHashRow) { 448 if (hash == entry.valueHash && Objects.equal(value, entry.getValue())) { 449 return false; 450 } 451 } 452 453 ValueEntry<K, V> newEntry = new ValueEntry<K, V>(key, value, hash, rowHead); 454 succeedsInValueSet(lastEntry, newEntry); 455 succeedsInValueSet(newEntry, this); 456 succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry); 457 succeedsInMultimap(newEntry, multimapHeaderEntry); 458 hashTable[row] = newEntry; 459 size++; 460 modCount++; 461 rehashIfNecessary(); 462 return true; 463 } 464 465 private void rehashIfNecessary() { 466 if (Hashing.needsResizing(size, hashTable.length, VALUE_SET_LOAD_FACTOR)) { 467 @SuppressWarnings("unchecked") 468 ValueEntry<K, V>[] hashTable = new ValueEntry[this.hashTable.length * 2]; 469 this.hashTable = hashTable; 470 int mask = hashTable.length - 1; 471 for (ValueSetLink<K, V> entry = firstEntry; 472 entry != this; entry = entry.getSuccessorInValueSet()) { 473 ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry; 474 int row = Hashing.smear(valueEntry.valueHash) & mask; 475 valueEntry.nextInValueSetHashRow = hashTable[row]; 476 hashTable[row] = valueEntry; 477 } 478 } 479 } 480 481 @Override 482 public boolean remove(@Nullable Object o) { 483 int hash = (o == null) ? 0 : o.hashCode(); 484 int row = Hashing.smear(hash) & (hashTable.length - 1); 485 486 ValueEntry<K, V> prev = null; 487 for (ValueEntry<K, V> entry = hashTable[row]; entry != null; 488 prev = entry, entry = entry.nextInValueSetHashRow) { 489 if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) { 490 if (prev == null) { 491 // first entry in the row 492 hashTable[row] = entry.nextInValueSetHashRow; 493 } else { 494 prev.nextInValueSetHashRow = entry.nextInValueSetHashRow; 495 } 496 deleteFromValueSet(entry); 497 deleteFromMultimap(entry); 498 size--; 499 modCount++; 500 return true; 501 } 502 } 503 return false; 504 } 505 506 @Override 507 public void clear() { 508 Arrays.fill(hashTable, null); 509 size = 0; 510 for (ValueSetLink<K, V> entry = firstEntry; 511 entry != this; 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<Map.Entry<K, V>> entryIterator() { 522 return new Iterator<Map.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 Map.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 Iterators.checkRemove(toRemove != null); 545 LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue()); 546 toRemove = null; 547 } 548 }; 549 } 550 551 @Override 552 public void clear() { 553 super.clear(); 554 succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); 555 } 556 557 /** 558 * @serialData the expected values per key, the number of distinct keys, 559 * the number of entries, and the entries in order 560 */ 561 @GwtIncompatible("java.io.ObjectOutputStream") 562 private void writeObject(ObjectOutputStream stream) throws IOException { 563 stream.defaultWriteObject(); 564 stream.writeInt(valueSetCapacity); 565 stream.writeInt(keySet().size()); 566 for (K key : keySet()) { 567 stream.writeObject(key); 568 } 569 stream.writeInt(size()); 570 for (Map.Entry<K, V> entry : entries()) { 571 stream.writeObject(entry.getKey()); 572 stream.writeObject(entry.getValue()); 573 } 574 } 575 576 @GwtIncompatible("java.io.ObjectInputStream") 577 private void readObject(ObjectInputStream stream) 578 throws IOException, ClassNotFoundException { 579 stream.defaultReadObject(); 580 multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null); 581 succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); 582 valueSetCapacity = stream.readInt(); 583 int distinctKeys = stream.readInt(); 584 Map<K, Collection<V>> map = 585 new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys)); 586 for (int i = 0; i < distinctKeys; i++) { 587 @SuppressWarnings("unchecked") 588 K key = (K) stream.readObject(); 589 map.put(key, createCollection(key)); 590 } 591 int entries = stream.readInt(); 592 for (int i = 0; i < entries; i++) { 593 @SuppressWarnings("unchecked") 594 K key = (K) stream.readObject(); 595 @SuppressWarnings("unchecked") 596 V value = (V) stream.readObject(); 597 map.get(key).add(value); 598 } 599 setMap(map); 600 } 601 602 @GwtIncompatible("java serialization not supported") 603 private static final long serialVersionUID = 1; 604}