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