1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.util; 19 20 import java.lang.ref.ReferenceQueue; 21 import java.lang.ref.WeakReference; 22 23 /** 24 * WeakHashMap is an implementation of Map with keys which are WeakReferences. A 25 * key/value mapping is removed when the key is no longer referenced. All 26 * optional operations (adding and removing) are supported. Keys and values can 27 * be any objects. Note that the garbage collector acts similar to a second 28 * thread on this collection, possibly removing keys. 29 * 30 * @since 1.2 31 * @see HashMap 32 * @see WeakReference 33 */ 34 public class WeakHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> { 35 36 private static final int DEFAULT_SIZE = 16; 37 38 private final ReferenceQueue<K> referenceQueue; 39 40 int elementCount; 41 42 Entry<K, V>[] elementData; 43 44 private final int loadFactor; 45 46 private int threshold; 47 48 volatile int modCount; 49 50 // Simple utility method to isolate unchecked cast for array creation 51 @SuppressWarnings("unchecked") 52 private static <K, V> Entry<K, V>[] newEntryArray(int size) { 53 return new Entry[size]; 54 } 55 56 private static final class Entry<K, V> extends WeakReference<K> implements 57 Map.Entry<K, V> { 58 int hash; 59 60 boolean isNull; 61 62 V value; 63 64 Entry<K, V> next; 65 66 interface Type<R, K, V> { 67 R get(Map.Entry<K, V> entry); 68 } 69 70 Entry(K key, V object, ReferenceQueue<K> queue) { 71 super(key, queue); 72 isNull = key == null; 73 hash = isNull ? 0 : key.hashCode(); 74 value = object; 75 } 76 77 public K getKey() { 78 return super.get(); 79 } 80 81 public V getValue() { 82 return value; 83 } 84 85 public V setValue(V object) { 86 V result = value; 87 value = object; 88 return result; 89 } 90 91 @Override 92 public boolean equals(Object other) { 93 if (!(other instanceof Map.Entry)) { 94 return false; 95 } 96 Map.Entry<?, ?> entry = (Map.Entry<?, ?>) other; 97 Object key = super.get(); 98 return (key == null ? key == entry.getKey() : key.equals(entry 99 .getKey())) 100 && (value == null ? value == entry.getValue() : value 101 .equals(entry.getValue())); 102 } 103 104 @Override 105 public int hashCode() { 106 return hash + (value == null ? 0 : value.hashCode()); 107 } 108 109 @Override 110 public String toString() { 111 return super.get() + "=" + value; 112 } 113 } 114 115 class HashIterator<R> implements Iterator<R> { 116 private int position = 0, expectedModCount; 117 118 private Entry<K, V> currentEntry, nextEntry; 119 120 private K nextKey; 121 122 final Entry.Type<R, K, V> type; 123 124 HashIterator(Entry.Type<R, K, V> type) { 125 this.type = type; 126 expectedModCount = modCount; 127 } 128 129 public boolean hasNext() { 130 if (nextEntry != null && (nextKey != null || nextEntry.isNull)) { 131 return true; 132 } 133 while (true) { 134 if (nextEntry == null) { 135 while (position < elementData.length) { 136 if ((nextEntry = elementData[position++]) != null) { 137 break; 138 } 139 } 140 if (nextEntry == null) { 141 return false; 142 } 143 } 144 // ensure key of next entry is not gc'ed 145 nextKey = nextEntry.get(); 146 if (nextKey != null || nextEntry.isNull) { 147 return true; 148 } 149 nextEntry = nextEntry.next; 150 } 151 } 152 153 public R next() { 154 if (expectedModCount == modCount) { 155 if (hasNext()) { 156 currentEntry = nextEntry; 157 nextEntry = currentEntry.next; 158 R result = type.get(currentEntry); 159 // free the key 160 nextKey = null; 161 return result; 162 } 163 throw new NoSuchElementException(); 164 } 165 throw new ConcurrentModificationException(); 166 } 167 168 public void remove() { 169 if (expectedModCount == modCount) { 170 if (currentEntry != null) { 171 removeEntry(currentEntry); 172 currentEntry = null; 173 expectedModCount++; 174 // cannot poll() as that would change the expectedModCount 175 } else { 176 throw new IllegalStateException(); 177 } 178 } else { 179 throw new ConcurrentModificationException(); 180 } 181 } 182 } 183 184 /** 185 * Constructs a new empty {@code WeakHashMap} instance. 186 */ 187 public WeakHashMap() { 188 this(DEFAULT_SIZE); 189 } 190 191 /** 192 * Constructs a new {@code WeakHashMap} instance with the specified 193 * capacity. 194 * 195 * @param capacity 196 * the initial capacity of this map. 197 * @throws IllegalArgumentException 198 * if the capacity is less than zero. 199 */ 200 public WeakHashMap(int capacity) { 201 if (capacity >= 0) { 202 elementCount = 0; 203 elementData = newEntryArray(capacity == 0 ? 1 : capacity); 204 loadFactor = 7500; // Default load factor of 0.75 205 computeMaxSize(); 206 referenceQueue = new ReferenceQueue<K>(); 207 } else { 208 throw new IllegalArgumentException(); 209 } 210 } 211 212 /** 213 * Constructs a new {@code WeakHashMap} instance with the specified capacity 214 * and load factor. 215 * 216 * @param capacity 217 * the initial capacity of this map. 218 * @param loadFactor 219 * the initial load factor. 220 * @throws IllegalArgumentException 221 * if the capacity is less than zero or the load factor is less 222 * or equal to zero. 223 */ 224 public WeakHashMap(int capacity, float loadFactor) { 225 if (capacity >= 0 && loadFactor > 0) { 226 elementCount = 0; 227 elementData = newEntryArray(capacity == 0 ? 1 : capacity); 228 this.loadFactor = (int) (loadFactor * 10000); 229 computeMaxSize(); 230 referenceQueue = new ReferenceQueue<K>(); 231 } else { 232 throw new IllegalArgumentException(); 233 } 234 } 235 236 /** 237 * Constructs a new {@code WeakHashMap} instance containing the mappings 238 * from the specified map. 239 * 240 * @param map 241 * the mappings to add. 242 */ 243 public WeakHashMap(Map<? extends K, ? extends V> map) { 244 this(map.size() < 6 ? 11 : map.size() * 2); 245 putAllImpl(map); 246 } 247 248 /** 249 * Removes all mappings from this map, leaving it empty. 250 * 251 * @see #isEmpty() 252 * @see #size() 253 */ 254 @Override 255 public void clear() { 256 if (elementCount > 0) { 257 elementCount = 0; 258 Arrays.fill(elementData, null); 259 modCount++; 260 while (referenceQueue.poll() != null) { 261 // do nothing 262 } 263 } 264 } 265 266 private void computeMaxSize() { 267 threshold = (int) ((long) elementData.length * loadFactor / 10000); 268 } 269 270 /** 271 * Returns whether this map contains the specified key. 272 * 273 * @param key 274 * the key to search for. 275 * @return {@code true} if this map contains the specified key, 276 * {@code false} otherwise. 277 */ 278 @Override 279 public boolean containsKey(Object key) { 280 return getEntry(key) != null; 281 } 282 283 /** 284 * Returns a set containing all of the mappings in this map. Each mapping is 285 * an instance of {@link Map.Entry}. As the set is backed by this map, 286 * changes in one will be reflected in the other. It does not support adding 287 * operations. 288 * 289 * @return a set of the mappings. 290 */ 291 @Override 292 public Set<Map.Entry<K, V>> entrySet() { 293 poll(); 294 return new AbstractSet<Map.Entry<K, V>>() { 295 @Override 296 public int size() { 297 return WeakHashMap.this.size(); 298 } 299 300 @Override 301 public void clear() { 302 WeakHashMap.this.clear(); 303 } 304 305 @Override 306 public boolean remove(Object object) { 307 if (contains(object)) { 308 WeakHashMap.this 309 .remove(((Map.Entry<?, ?>) object).getKey()); 310 return true; 311 } 312 return false; 313 } 314 315 @Override 316 public boolean contains(Object object) { 317 if (object instanceof Map.Entry) { 318 Entry<?, ?> entry = getEntry(((Map.Entry<?, ?>) object) 319 .getKey()); 320 if (entry != null) { 321 Object key = entry.get(); 322 if (key != null || entry.isNull) { 323 return object.equals(entry); 324 } 325 } 326 } 327 return false; 328 } 329 330 @Override 331 public Iterator<Map.Entry<K, V>> iterator() { 332 return new HashIterator<Map.Entry<K, V>>( 333 new Entry.Type<Map.Entry<K, V>, K, V>() { 334 public Map.Entry<K, V> get(Map.Entry<K, V> entry) { 335 return entry; 336 } 337 }); 338 } 339 }; 340 } 341 342 /** 343 * Returns a set of the keys contained in this map. The set is backed by 344 * this map so changes to one are reflected by the other. The set does not 345 * support adding. 346 * 347 * @return a set of the keys. 348 */ 349 @Override 350 public Set<K> keySet() { 351 poll(); 352 if (keySet == null) { 353 keySet = new AbstractSet<K>() { 354 @Override 355 public boolean contains(Object object) { 356 return containsKey(object); 357 } 358 359 @Override 360 public int size() { 361 return WeakHashMap.this.size(); 362 } 363 364 @Override 365 public void clear() { 366 WeakHashMap.this.clear(); 367 } 368 369 @Override 370 public boolean remove(Object key) { 371 if (containsKey(key)) { 372 WeakHashMap.this.remove(key); 373 return true; 374 } 375 return false; 376 } 377 378 @Override 379 public Iterator<K> iterator() { 380 return new HashIterator<K>(new Entry.Type<K, K, V>() { 381 public K get(Map.Entry<K, V> entry) { 382 return entry.getKey(); 383 } 384 }); 385 } 386 387 @Override 388 public Object[] toArray() { 389 Collection<K> coll = new ArrayList<K>(size()); 390 391 for (Iterator<K> iter = iterator(); iter.hasNext();) { 392 coll.add(iter.next()); 393 } 394 return coll.toArray(); 395 } 396 397 @Override 398 public <T> T[] toArray(T[] contents) { 399 Collection<K> coll = new ArrayList<K>(size()); 400 401 for (Iterator<K> iter = iterator(); iter.hasNext();) { 402 coll.add(iter.next()); 403 } 404 return coll.toArray(contents); 405 } 406 }; 407 } 408 return keySet; 409 } 410 411 /** 412 * Returns a collection of the values contained in this map. The collection 413 * is backed by this map so changes to one are reflected by the other. The 414 * collection supports remove, removeAll, retainAll and clear operations, 415 * and it does not support add or addAll operations. 416 * <p> 417 * This method returns a collection which is the subclass of 418 * AbstractCollection. The iterator method of this subclass returns a 419 * "wrapper object" over the iterator of map's entrySet(). The size method 420 * wraps the map's size method and the contains method wraps the map's 421 * containsValue method. 422 * <p> 423 * The collection is created when this method is called at first time and 424 * returned in response to all subsequent calls. This method may return 425 * different Collection when multiple calls to this method, since it has no 426 * synchronization performed. 427 * 428 * @return a collection of the values contained in this map. 429 */ 430 @Override 431 public Collection<V> values() { 432 poll(); 433 if (valuesCollection == null) { 434 valuesCollection = new AbstractCollection<V>() { 435 @Override 436 public int size() { 437 return WeakHashMap.this.size(); 438 } 439 440 @Override 441 public void clear() { 442 WeakHashMap.this.clear(); 443 } 444 445 @Override 446 public boolean contains(Object object) { 447 return containsValue(object); 448 } 449 450 @Override 451 public Iterator<V> iterator() { 452 return new HashIterator<V>(new Entry.Type<V, K, V>() { 453 public V get(Map.Entry<K, V> entry) { 454 return entry.getValue(); 455 } 456 }); 457 } 458 }; 459 } 460 return valuesCollection; 461 } 462 463 /** 464 * Returns the value of the mapping with the specified key. 465 * 466 * @param key 467 * the key. 468 * @return the value of the mapping with the specified key, or {@code null} 469 * if no mapping for the specified key is found. 470 */ 471 @Override 472 public V get(Object key) { 473 poll(); 474 if (key != null) { 475 int index = (key.hashCode() & 0x7FFFFFFF) % elementData.length; 476 Entry<K, V> entry = elementData[index]; 477 while (entry != null) { 478 if (key.equals(entry.get())) { 479 return entry.value; 480 } 481 entry = entry.next; 482 } 483 return null; 484 } 485 Entry<K, V> entry = elementData[0]; 486 while (entry != null) { 487 if (entry.isNull) { 488 return entry.value; 489 } 490 entry = entry.next; 491 } 492 return null; 493 } 494 495 Entry<K, V> getEntry(Object key) { 496 poll(); 497 if (key != null) { 498 int index = (key.hashCode() & 0x7FFFFFFF) % elementData.length; 499 Entry<K, V> entry = elementData[index]; 500 while (entry != null) { 501 if (key.equals(entry.get())) { 502 return entry; 503 } 504 entry = entry.next; 505 } 506 return null; 507 } 508 Entry<K, V> entry = elementData[0]; 509 while (entry != null) { 510 if (entry.isNull) { 511 return entry; 512 } 513 entry = entry.next; 514 } 515 return null; 516 } 517 518 /** 519 * Returns whether this map contains the specified value. 520 * 521 * @param value 522 * the value to search for. 523 * @return {@code true} if this map contains the specified value, 524 * {@code false} otherwise. 525 */ 526 @Override 527 public boolean containsValue(Object value) { 528 poll(); 529 if (value != null) { 530 for (int i = elementData.length; --i >= 0;) { 531 Entry<K, V> entry = elementData[i]; 532 while (entry != null) { 533 K key = entry.get(); 534 if ((key != null || entry.isNull) 535 && value.equals(entry.value)) { 536 return true; 537 } 538 entry = entry.next; 539 } 540 } 541 } else { 542 for (int i = elementData.length; --i >= 0;) { 543 Entry<K, V> entry = elementData[i]; 544 while (entry != null) { 545 K key = entry.get(); 546 if ((key != null || entry.isNull) && entry.value == null) { 547 return true; 548 } 549 entry = entry.next; 550 } 551 } 552 } 553 return false; 554 } 555 556 /** 557 * Returns the number of elements in this map. 558 * 559 * @return the number of elements in this map. 560 */ 561 @Override 562 public boolean isEmpty() { 563 return size() == 0; 564 } 565 566 @SuppressWarnings("unchecked") 567 void poll() { 568 Entry<K, V> toRemove; 569 while ((toRemove = (Entry<K, V>) referenceQueue.poll()) != null) { 570 removeEntry(toRemove); 571 } 572 } 573 574 void removeEntry(Entry<K, V> toRemove) { 575 Entry<K, V> entry, last = null; 576 int index = (toRemove.hash & 0x7FFFFFFF) % elementData.length; 577 entry = elementData[index]; 578 // Ignore queued entries which cannot be found, the user could 579 // have removed them before they were queued, i.e. using clear() 580 while (entry != null) { 581 if (toRemove == entry) { 582 modCount++; 583 if (last == null) { 584 elementData[index] = entry.next; 585 } else { 586 last.next = entry.next; 587 } 588 elementCount--; 589 break; 590 } 591 last = entry; 592 entry = entry.next; 593 } 594 } 595 596 /** 597 * Maps the specified key to the specified value. 598 * 599 * @param key 600 * the key. 601 * @param value 602 * the value. 603 * @return the value of any previous mapping with the specified key or 604 * {@code null} if there was no mapping. 605 */ 606 @Override 607 public V put(K key, V value) { 608 poll(); 609 int index = 0; 610 Entry<K, V> entry; 611 if (key != null) { 612 index = (key.hashCode() & 0x7FFFFFFF) % elementData.length; 613 entry = elementData[index]; 614 while (entry != null && !key.equals(entry.get())) { 615 entry = entry.next; 616 } 617 } else { 618 entry = elementData[0]; 619 while (entry != null && !entry.isNull) { 620 entry = entry.next; 621 } 622 } 623 if (entry == null) { 624 modCount++; 625 if (++elementCount > threshold) { 626 rehash(); 627 index = key == null ? 0 : (key.hashCode() & 0x7FFFFFFF) 628 % elementData.length; 629 } 630 entry = new Entry<K, V>(key, value, referenceQueue); 631 entry.next = elementData[index]; 632 elementData[index] = entry; 633 return null; 634 } 635 V result = entry.value; 636 entry.value = value; 637 return result; 638 } 639 640 private void rehash() { 641 int length = elementData.length * 2; 642 if (length == 0) { 643 length = 1; 644 } 645 Entry<K, V>[] newData = newEntryArray(length); 646 for (int i = 0; i < elementData.length; i++) { 647 Entry<K, V> entry = elementData[i]; 648 while (entry != null) { 649 int index = entry.isNull ? 0 : (entry.hash & 0x7FFFFFFF) 650 % length; 651 Entry<K, V> next = entry.next; 652 entry.next = newData[index]; 653 newData[index] = entry; 654 entry = next; 655 } 656 } 657 elementData = newData; 658 computeMaxSize(); 659 } 660 661 /** 662 * Copies all the mappings in the given map to this map. These mappings will 663 * replace all mappings that this map had for any of the keys currently in 664 * the given map. 665 * 666 * @param map 667 * the map to copy mappings from. 668 * @throws NullPointerException 669 * if {@code map} is {@code null}. 670 */ 671 @Override 672 public void putAll(Map<? extends K, ? extends V> map) { 673 putAllImpl(map); 674 } 675 676 /** 677 * Removes the mapping with the specified key from this map. 678 * 679 * @param key 680 * the key of the mapping to remove. 681 * @return the value of the removed mapping or {@code null} if no mapping 682 * for the specified key was found. 683 */ 684 @Override 685 public V remove(Object key) { 686 poll(); 687 int index = 0; 688 Entry<K, V> entry, last = null; 689 if (key != null) { 690 index = (key.hashCode() & 0x7FFFFFFF) % elementData.length; 691 entry = elementData[index]; 692 while (entry != null && !key.equals(entry.get())) { 693 last = entry; 694 entry = entry.next; 695 } 696 } else { 697 entry = elementData[0]; 698 while (entry != null && !entry.isNull) { 699 last = entry; 700 entry = entry.next; 701 } 702 } 703 if (entry != null) { 704 modCount++; 705 if (last == null) { 706 elementData[index] = entry.next; 707 } else { 708 last.next = entry.next; 709 } 710 elementCount--; 711 return entry.value; 712 } 713 return null; 714 } 715 716 /** 717 * Returns the number of elements in this map. 718 * 719 * @return the number of elements in this map. 720 */ 721 @Override 722 public int size() { 723 poll(); 724 return elementCount; 725 } 726 727 private void putAllImpl(Map<? extends K, ? extends V> map) { 728 if (map.entrySet() != null) { 729 super.putAll(map); 730 } 731 } 732 } 733