001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.util.collections; 018 019import java.io.IOException; 020import java.io.Serializable; 021import java.util.AbstractCollection; 022import java.util.AbstractSet; 023import java.util.Collection; 024import java.util.ConcurrentModificationException; 025import java.util.Iterator; 026import java.util.NoSuchElementException; 027import java.util.Set; 028import java.util.concurrent.atomic.AtomicInteger; 029 030/** 031 * This is an integer hashmap that has the exact same features and interface as a normal Map except 032 * that the key is directly an integer. So no hash is calculated or key object is stored. 033 * 034 * @author jcompagner 035 * 036 * @param <V> 037 * The value in the map 038 */ 039public class IntHashMap<V> implements Cloneable, Serializable 040{ 041 transient volatile Set<Integer> keySet = null; 042 043 transient volatile Collection<V> values = null; 044 045 /** 046 * The default initial capacity - MUST be a power of two. 047 */ 048 static final int DEFAULT_INITIAL_CAPACITY = 16; 049 050 /** 051 * The maximum capacity, used if a higher value is implicitly specified by either of the 052 * constructors with arguments. MUST be a power of two <= 1<<30. 053 */ 054 static final int MAXIMUM_CAPACITY = 1 << 30; 055 056 /** 057 * The load factor used when none specified in constructor. 058 */ 059 static final float DEFAULT_LOAD_FACTOR = 0.75f; 060 061 /** 062 * The table, resized as necessary. Length MUST Always be a power of two. 063 */ 064 transient Entry<V>[] table; 065 066 /** 067 * The number of key-value mappings contained in this identity hash map. 068 */ 069 transient int size; 070 071 /** 072 * The next size value at which to resize (capacity * load factor). 073 * 074 * @serial 075 */ 076 int threshold; 077 078 /** 079 * The load factor for the hash table. 080 * 081 * @serial 082 */ 083 final float loadFactor; 084 085 /** 086 * The number of times this HashMap has been structurally modified Structural modifications are 087 * those that change the number of mappings in the HashMap or otherwise modify its internal 088 * structure (e.g., rehash). This field is used to make iterators on Collection-views of the 089 * HashMap fail-fast. (See ConcurrentModificationException). 090 */ 091 transient AtomicInteger modCount = new AtomicInteger(0); 092 093 /** 094 * Constructs an empty <tt>HashMap</tt> with the specified initial capacity and load factor. 095 * 096 * @param initialCapacity 097 * The initial capacity. 098 * @param loadFactor 099 * The load factor. 100 * @throws IllegalArgumentException 101 * if the initial capacity is negative or the load factor is nonpositive. 102 */ 103 @SuppressWarnings("unchecked") 104 public IntHashMap(int initialCapacity, final float loadFactor) 105 { 106 if (initialCapacity < 0) 107 { 108 throw new IllegalArgumentException("Illegal initial capacity: " + //$NON-NLS-1$ 109 initialCapacity); 110 } 111 if (initialCapacity > MAXIMUM_CAPACITY) 112 { 113 initialCapacity = MAXIMUM_CAPACITY; 114 } 115 if ((loadFactor <= 0) || Float.isNaN(loadFactor)) 116 { 117 throw new IllegalArgumentException("Illegal load factor: " + //$NON-NLS-1$ 118 loadFactor); 119 } 120 121 // Find a power of 2 >= initialCapacity 122 int capacity = 1; 123 while (capacity < initialCapacity) 124 { 125 capacity <<= 1; 126 } 127 128 this.loadFactor = loadFactor; 129 threshold = (int)(capacity * loadFactor); 130 table = new Entry[capacity]; 131 init(); 132 } 133 134 /** 135 * Constructs an empty <tt>HashMap</tt> with the specified initial capacity and the default load 136 * factor (0.75). 137 * 138 * @param initialCapacity 139 * the initial capacity. 140 * @throws IllegalArgumentException 141 * if the initial capacity is negative. 142 */ 143 public IntHashMap(final int initialCapacity) 144 { 145 this(initialCapacity, DEFAULT_LOAD_FACTOR); 146 } 147 148 /** 149 * Constructs an empty <tt>HashMap</tt> with the default initial capacity (16) and the default 150 * load factor (0.75). 151 */ 152 @SuppressWarnings("unchecked") 153 public IntHashMap() 154 { 155 loadFactor = DEFAULT_LOAD_FACTOR; 156 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); 157 table = new Entry[DEFAULT_INITIAL_CAPACITY]; 158 init(); 159 } 160 161 // internal utilities 162 163 /** 164 * Initialization hook for subclasses. This method is called in all constructors and 165 * pseudo-constructors (clone, readObject) after HashMap has been initialized but before any 166 * entries have been inserted. (In the absence of this method, readObject would require explicit 167 * knowledge of subclasses.) 168 */ 169 void init() 170 { 171 } 172 173 /** 174 * Returns index for hash code h. 175 * 176 * @param h 177 * @param length 178 * @return The index for the hash integer for the given length 179 */ 180 static int indexFor(final int h, final int length) 181 { 182 return h & (length - 1); 183 } 184 185 /** 186 * Returns the number of key-value mappings in this map. 187 * 188 * @return the number of key-value mappings in this map. 189 */ 190 public int size() 191 { 192 return size; 193 } 194 195 /** 196 * Returns <tt>true</tt> if this map contains no key-value mappings. 197 * 198 * @return <tt>true</tt> if this map contains no key-value mappings. 199 */ 200 public boolean isEmpty() 201 { 202 return size == 0; 203 } 204 205 /** 206 * Returns the value to which the specified key is mapped in this identity hash map, or 207 * <tt>null</tt> if the map contains no mapping for this key. A return value of <tt>null</tt> 208 * does not <i>necessarily</i> indicate that the map contains no mapping for the key; it is also 209 * possible that the map explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt> 210 * method may be used to distinguish these two cases. 211 * 212 * @param key 213 * the key whose associated value is to be returned. 214 * @return the value to which this map maps the specified key, or <tt>null</tt> if the map 215 * contains no mapping for this key. 216 * @see #put(int, Object) 217 */ 218 public V get(final int key) 219 { 220 int i = indexFor(key, table.length); 221 Entry<V> e = table[i]; 222 while (true) 223 { 224 if (e == null) 225 { 226 return null; 227 } 228 if (key == e.key) 229 { 230 return e.value; 231 } 232 e = e.next; 233 } 234 } 235 236 /** 237 * Returns <tt>true</tt> if this map contains a mapping for the specified key. 238 * 239 * @param key 240 * The key whose presence in this map is to be tested 241 * @return <tt>true</tt> if this map contains a mapping for the specified key. 242 */ 243 public boolean containsKey(final int key) 244 { 245 int i = indexFor(key, table.length); 246 Entry<V> e = table[i]; 247 while (e != null) 248 { 249 if (key == e.key) 250 { 251 return true; 252 } 253 e = e.next; 254 } 255 return false; 256 } 257 258 /** 259 * Returns the entry associated with the specified key in the HashMap. Returns null if the 260 * HashMap contains no mapping for this key. 261 * 262 * @param key 263 * @return The Entry object for the given hash key 264 */ 265 Entry<V> getEntry(final int key) 266 { 267 int i = indexFor(key, table.length); 268 Entry<V> e = table[i]; 269 while ((e != null) && !(key == e.key)) 270 { 271 e = e.next; 272 } 273 return e; 274 } 275 276 /** 277 * Associates the specified value with the specified key in this map. If the map previously 278 * contained a mapping for this key, the old value is replaced. 279 * 280 * @param key 281 * key with which the specified value is to be associated. 282 * @param value 283 * value to be associated with the specified key. 284 * @return previous value associated with specified key, or <tt>null</tt> if there was no 285 * mapping for key. A <tt>null</tt> return can also indicate that the HashMap previously 286 * associated <tt>null</tt> with the specified key. 287 */ 288 public V put(final int key, final V value) 289 { 290 int i = indexFor(key, table.length); 291 292 for (Entry<V> e = table[i]; e != null; e = e.next) 293 { 294 if (key == e.key) 295 { 296 V oldValue = e.value; 297 e.value = value; 298 return oldValue; 299 } 300 } 301 302 modCount.incrementAndGet(); 303 addEntry(key, value, i); 304 return null; 305 } 306 307 /** 308 * This method is used instead of put by constructors and pseudoconstructors (clone, 309 * readObject). It does not resize the table, check for comodification, etc. It calls 310 * createEntry rather than addEntry. 311 * 312 * @param key 313 * @param value 314 */ 315 private void putForCreate(final int key, final V value) 316 { 317 int i = indexFor(key, table.length); 318 319 /** 320 * Look for preexisting entry for key. This will never happen for clone or deserialize. It 321 * will only happen for construction if the input Map is a sorted map whose ordering is 322 * inconsistent w/ equals. 323 */ 324 for (Entry<V> e = table[i]; e != null; e = e.next) 325 { 326 if (key == e.key) 327 { 328 e.value = value; 329 return; 330 } 331 } 332 333 createEntry(key, value, i); 334 } 335 336 void putAllForCreate(final IntHashMap<V> m) 337 { 338 for (Entry<V> entry : m.entrySet()) 339 { 340 putForCreate(entry.getKey(), entry.getValue()); 341 } 342 } 343 344 /** 345 * Rehashes the contents of this map into a new array with a larger capacity. This method is 346 * called automatically when the number of keys in this map reaches its threshold. 347 * 348 * If current capacity is MAXIMUM_CAPACITY, this method does not resize the map, but but sets 349 * threshold to Integer.MAX_VALUE. This has the effect of preventing future calls. 350 * 351 * @param newCapacity 352 * the new capacity, MUST be a power of two; must be greater than current capacity 353 * unless current capacity is MAXIMUM_CAPACITY (in which case value is irrelevant). 354 */ 355 @SuppressWarnings("unchecked") 356 void resize(final int newCapacity) 357 { 358 Entry<V>[] oldTable = table; 359 int oldCapacity = oldTable.length; 360 if (oldCapacity == MAXIMUM_CAPACITY) 361 { 362 threshold = Integer.MAX_VALUE; 363 return; 364 } 365 366 Entry<V>[] newTable = new Entry[newCapacity]; 367 transfer(newTable); 368 table = newTable; 369 threshold = (int)(newCapacity * loadFactor); 370 } 371 372 /** 373 * Transfer all entries from current table to newTable. 374 * 375 * @param newTable 376 */ 377 void transfer(final Entry<V>[] newTable) 378 { 379 Entry<V>[] src = table; 380 int newCapacity = newTable.length; 381 for (int j = 0; j < src.length; j++) 382 { 383 Entry<V> e = src[j]; 384 if (e != null) 385 { 386 src[j] = null; 387 do 388 { 389 Entry<V> next = e.next; 390 int i = indexFor(e.key, newCapacity); 391 e.next = newTable[i]; 392 newTable[i] = e; 393 e = next; 394 } 395 while (e != null); 396 } 397 } 398 } 399 400 /** 401 * Copies all of the mappings from the specified map to this map These mappings will replace any 402 * mappings that this map had for any of the keys currently in the specified map. 403 * 404 * @param m 405 * mappings to be stored in this map. 406 * @throws NullPointerException 407 * if the specified map is null. 408 */ 409 public void putAll(final IntHashMap<V> m) 410 { 411 int numKeysToBeAdded = m.size(); 412 if (numKeysToBeAdded == 0) 413 { 414 return; 415 } 416 417 /* 418 * Expand the map if the map if the number of mappings to be added is greater than or equal 419 * to threshold. This is conservative; the obvious condition is (m.size() + size) >= 420 * threshold, but this condition could result in a map with twice the appropriate capacity, 421 * if the keys to be added overlap with the keys already in this map. By using the 422 * conservative calculation, we subject ourself to at most one extra resize. 423 */ 424 if (numKeysToBeAdded > threshold) 425 { 426 int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); 427 if (targetCapacity > MAXIMUM_CAPACITY) 428 { 429 targetCapacity = MAXIMUM_CAPACITY; 430 } 431 int newCapacity = table.length; 432 while (newCapacity < targetCapacity) 433 { 434 newCapacity <<= 1; 435 } 436 if (newCapacity > table.length) 437 { 438 resize(newCapacity); 439 } 440 } 441 442 for (Entry<V> entry : m.entrySet()) 443 { 444 put(entry.getKey(), entry.getValue()); 445 } 446 } 447 448 /** 449 * Removes the mapping for this key from this map if present. 450 * 451 * @param key 452 * key whose mapping is to be removed from the map. 453 * @return previous value associated with specified key, or <tt>null</tt> if there was no 454 * mapping for key. A <tt>null</tt> return can also indicate that the map previously 455 * associated <tt>null</tt> with the specified key. 456 */ 457 public V remove(final int key) 458 { 459 Entry<V> e = removeEntryForKey(key); 460 return (e == null ? null : e.value); 461 } 462 463 /** 464 * Removes and returns the entry associated with the specified key in the HashMap. Returns null 465 * if the HashMap contains no mapping for this key. 466 * 467 * @param key 468 * @return The Entry object that was removed 469 */ 470 Entry<V> removeEntryForKey(final int key) 471 { 472 int i = indexFor(key, table.length); 473 Entry<V> prev = table[i]; 474 Entry<V> e = prev; 475 476 while (e != null) 477 { 478 Entry<V> next = e.next; 479 if (key == e.key) 480 { 481 modCount.incrementAndGet(); 482 size--; 483 if (prev == e) 484 { 485 table[i] = next; 486 } 487 else 488 { 489 prev.next = next; 490 } 491 return e; 492 } 493 prev = e; 494 e = next; 495 } 496 497 return e; 498 } 499 500 /** 501 * Special version of remove for EntrySet. 502 * 503 * @param o 504 * @return The entry that was removed 505 */ 506 @SuppressWarnings("unchecked") 507 Entry<V> removeMapping(final Object o) 508 { 509 if (!(o instanceof Entry)) 510 { 511 return null; 512 } 513 514 Entry<V> entry = (Entry<V>)o; 515 int key = entry.getKey(); 516 int i = indexFor(key, table.length); 517 Entry<V> prev = table[i]; 518 Entry<V> e = prev; 519 520 while (e != null) 521 { 522 Entry<V> next = e.next; 523 if ((e.key == key) && e.equals(entry)) 524 { 525 modCount.incrementAndGet(); 526 size--; 527 if (prev == e) 528 { 529 table[i] = next; 530 } 531 else 532 { 533 prev.next = next; 534 } 535 return e; 536 } 537 prev = e; 538 e = next; 539 } 540 541 return e; 542 } 543 544 /** 545 * Removes all mappings from this map. 546 */ 547 public void clear() 548 { 549 modCount.incrementAndGet(); 550 Entry<V> tab[] = table; 551 for (int i = 0; i < tab.length; i++) 552 { 553 tab[i] = null; 554 } 555 size = 0; 556 } 557 558 /** 559 * Returns <tt>true</tt> if this map maps one or more keys to the specified value. 560 * 561 * @param value 562 * value whose presence in this map is to be tested. 563 * @return <tt>true</tt> if this map maps one or more keys to the specified value. 564 */ 565 public boolean containsValue(final Object value) 566 { 567 if (value == null) 568 { 569 return containsNullValue(); 570 } 571 572 for (Entry<V> entry : table) 573 { 574 for (Entry<V> e = entry; e != null; e = e.next) 575 { 576 if (value.equals(e.value)) 577 { 578 return true; 579 } 580 } 581 } 582 return false; 583 } 584 585 /** 586 * Special-case code for containsValue with null argument 587 * 588 * @return boolean true if there is a null value in this map 589 */ 590 private boolean containsNullValue() 591 { 592 Entry<V> tab[] = table; 593 for (Entry<V> tabEntry : tab) 594 { 595 for (Entry<V> e = tabEntry; e != null; e = e.next) 596 { 597 if (e.value == null) 598 { 599 return true; 600 } 601 } 602 } 603 return false; 604 } 605 606 /** 607 * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and values themselves are 608 * not cloned. 609 * 610 * @return a shallow copy of this map. 611 */ 612 @SuppressWarnings("unchecked") 613 @Override 614 public Object clone() throws CloneNotSupportedException 615 { 616 IntHashMap<V> result = null; 617 try 618 { 619 result = (IntHashMap<V>)super.clone(); 620 result.table = new Entry[table.length]; 621 result.entrySet = null; 622 result.modCount.set(0); 623 result.size = 0; 624 result.init(); 625 result.putAllForCreate(this); 626 } 627 catch (CloneNotSupportedException e) 628 { 629 // assert false; 630 } 631 return result; 632 } 633 634 /** 635 * @author jcompagner 636 * @param <V> 637 * type of value object 638 */ 639 public static class Entry<V> 640 { 641 final int key; 642 V value; 643 Entry<V> next; 644 645 /** 646 * Create new entry. 647 * 648 * @param k 649 * @param v 650 * @param n 651 */ 652 Entry(final int k, final V v, final Entry<V> n) 653 { 654 value = v; 655 next = n; 656 key = k; 657 } 658 659 /** 660 * @return The int key of this entry 661 */ 662 public int getKey() 663 { 664 return key; 665 } 666 667 /** 668 * @return Gets the value object of this entry 669 */ 670 public V getValue() 671 { 672 return value; 673 } 674 675 /** 676 * @param newValue 677 * @return The previous value 678 */ 679 public V setValue(final V newValue) 680 { 681 V oldValue = value; 682 value = newValue; 683 return oldValue; 684 } 685 686 /** 687 * @see java.lang.Object#equals(java.lang.Object) 688 */ 689 @SuppressWarnings("unchecked") 690 @Override 691 public boolean equals(final Object o) 692 { 693 if (!(o instanceof Entry)) 694 { 695 return false; 696 } 697 Entry<V> e = (Entry<V>)o; 698 int k1 = getKey(); 699 int k2 = e.getKey(); 700 if (k1 == k2) 701 { 702 Object v1 = getValue(); 703 Object v2 = e.getValue(); 704 if ((v1 == v2) || ((v1 != null) && v1.equals(v2))) 705 { 706 return true; 707 } 708 } 709 return false; 710 } 711 712 /** 713 * @see java.lang.Object#hashCode() 714 */ 715 @Override 716 public int hashCode() 717 { 718 return key ^ (value == null ? 0 : value.hashCode()); 719 } 720 721 /** 722 * @see java.lang.Object#toString() 723 */ 724 @Override 725 public String toString() 726 { 727 return getKey() + "=" + getValue(); //$NON-NLS-1$ 728 } 729 } 730 731 /** 732 * Add a new entry with the specified key, value and hash code to the specified bucket. It is 733 * the responsibility of this method to resize the table if appropriate. 734 * 735 * Subclass overrides this to alter the behavior of put method. 736 * 737 * @param key 738 * @param value 739 * @param bucketIndex 740 */ 741 void addEntry(final int key, final V value, final int bucketIndex) 742 { 743 table[bucketIndex] = new Entry<>(key, value, table[bucketIndex]); 744 if (size++ >= threshold) 745 { 746 resize(2 * table.length); 747 } 748 } 749 750 /** 751 * Like addEntry except that this version is used when creating entries as part of Map 752 * construction or "pseudo-construction" (cloning, deserialization). This version needn't worry 753 * about resizing the table. 754 * 755 * Subclass overrides this to alter the behavior of HashMap(Map), clone, and readObject. 756 * 757 * @param key 758 * @param value 759 * @param bucketIndex 760 */ 761 void createEntry(final int key, final V value, final int bucketIndex) 762 { 763 table[bucketIndex] = new Entry<>(key, value, table[bucketIndex]); 764 size++; 765 } 766 767 private abstract class HashIterator<H> implements Iterator<H> 768 { 769 Entry<V> next; // next entry to return 770 int expectedModCount; // For fast-fail 771 int index; // current slot 772 Entry<V> current; // current entry 773 774 HashIterator() 775 { 776 expectedModCount = modCount.get(); 777 Entry<V>[] t = table; 778 int i = t.length; 779 Entry<V> n = null; 780 if (size != 0) 781 { // advance to first entry 782 while ((i > 0) && ((n = t[--i]) == null)) 783 { 784 /* NoOp */ 785 } 786 } 787 next = n; 788 index = i; 789 } 790 791 /** 792 * @see java.util.Iterator#hasNext() 793 */ 794 @Override 795 public boolean hasNext() 796 { 797 return next != null; 798 } 799 800 Entry<V> nextEntry() 801 { 802 if (!modCount.compareAndSet(expectedModCount, expectedModCount)) 803 { 804 throw new ConcurrentModificationException(); 805 } 806 Entry<V> e = next; 807 if (e == null) 808 { 809 throw new NoSuchElementException(); 810 } 811 812 Entry<V> n = e.next; 813 Entry<V>[] t = table; 814 int i = index; 815 while ((n == null) && (i > 0)) 816 { 817 n = t[--i]; 818 } 819 index = i; 820 next = n; 821 return current = e; 822 } 823 824 /** 825 * @see java.util.Iterator#remove() 826 */ 827 @Override 828 public void remove() 829 { 830 if (current == null) 831 { 832 throw new IllegalStateException(); 833 } 834 if (!modCount.compareAndSet(expectedModCount, expectedModCount)) 835 { 836 throw new ConcurrentModificationException(); 837 } 838 int k = current.key; 839 current = null; 840 removeEntryForKey(k); 841 expectedModCount = modCount.get(); 842 } 843 844 } 845 846 private class ValueIterator extends HashIterator<V> 847 { 848 /** 849 * @see java.util.Iterator#next() 850 */ 851 @Override 852 public V next() 853 { 854 return nextEntry().value; 855 } 856 } 857 858 private class KeyIterator extends HashIterator<Integer> 859 { 860 /** 861 * @see java.util.Iterator#next() 862 */ 863 @Override 864 public Integer next() 865 { 866 return nextEntry().getKey(); 867 } 868 } 869 870 private class EntryIterator extends HashIterator<Entry<V>> 871 { 872 /** 873 * @see java.util.Iterator#next() 874 */ 875 @Override 876 public Entry<V> next() 877 { 878 return nextEntry(); 879 } 880 } 881 882 // Subclass overrides these to alter behavior of views' iterator() method 883 Iterator<Integer> newKeyIterator() 884 { 885 return new KeyIterator(); 886 } 887 888 Iterator<V> newValueIterator() 889 { 890 return new ValueIterator(); 891 } 892 893 Iterator<Entry<V>> newEntryIterator() 894 { 895 return new EntryIterator(); 896 } 897 898 // Views 899 900 private transient Set<Entry<V>> entrySet = null; 901 902 /** 903 * Returns a set view of the keys contained in this map. The set is backed by the map, so 904 * changes to the map are reflected in the set, and vice-versa. The set supports element 905 * removal, which removes the corresponding mapping from this map, via the 906 * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and 907 * <tt>clear</tt> operations. It does not support the <tt>add</tt> or <tt>addAll</tt> 908 * operations. 909 * 910 * @return a set view of the keys contained in this map. 911 */ 912 public Set<Integer> keySet() 913 { 914 Set<Integer> ks = keySet; 915 return (ks != null ? ks : (keySet = new KeySet())); 916 } 917 918 private class KeySet extends AbstractSet<Integer> 919 { 920 /** 921 * @see java.util.AbstractCollection#iterator() 922 */ 923 @Override 924 public Iterator<Integer> iterator() 925 { 926 return newKeyIterator(); 927 } 928 929 /** 930 * @see java.util.AbstractCollection#size() 931 */ 932 @Override 933 public int size() 934 { 935 return size; 936 } 937 938 /** 939 * @see java.util.AbstractCollection#contains(java.lang.Object) 940 */ 941 @Override 942 public boolean contains(final Object o) 943 { 944 if (o instanceof Number) 945 { 946 return containsKey(((Number)o).intValue()); 947 } 948 return false; 949 } 950 951 /** 952 * @see java.util.AbstractCollection#remove(java.lang.Object) 953 */ 954 @Override 955 public boolean remove(final Object o) 956 { 957 if (o instanceof Number) 958 { 959 return removeEntryForKey(((Number)o).intValue()) != null; 960 } 961 return false; 962 } 963 964 /** 965 * @see java.util.AbstractCollection#clear() 966 */ 967 @Override 968 public void clear() 969 { 970 IntHashMap.this.clear(); 971 } 972 } 973 974 /** 975 * Returns a collection view of the values contained in this map. The collection is backed by 976 * the map, so changes to the map are reflected in the collection, and vice-versa. The 977 * collection supports element removal, which removes the corresponding mapping from this map, 978 * via the <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, <tt>removeAll</tt>, 979 * <tt>retainAll</tt>, and <tt>clear</tt> operations. It does not support the <tt>add</tt> or 980 * <tt>addAll</tt> operations. 981 * 982 * @return a collection view of the values contained in this map. 983 */ 984 public Collection<V> values() 985 { 986 Collection<V> vs = values; 987 return (vs != null ? vs : (values = new Values())); 988 } 989 990 private class Values extends AbstractCollection<V> 991 { 992 /** 993 * @see java.util.AbstractCollection#iterator() 994 */ 995 @Override 996 public Iterator<V> iterator() 997 { 998 return newValueIterator(); 999 } 1000 1001 /** 1002 * @see java.util.AbstractCollection#size() 1003 */ 1004 @Override 1005 public int size() 1006 { 1007 return size; 1008 } 1009 1010 /** 1011 * @see java.util.AbstractCollection#contains(java.lang.Object) 1012 */ 1013 @Override 1014 public boolean contains(final Object o) 1015 { 1016 return containsValue(o); 1017 } 1018 1019 /** 1020 * @see java.util.AbstractCollection#clear() 1021 */ 1022 @Override 1023 public void clear() 1024 { 1025 IntHashMap.this.clear(); 1026 } 1027 } 1028 1029 /** 1030 * Returns a collection view of the mappings contained in this map. Each element in the returned 1031 * collection is a <tt>Map.Entry</tt>. The collection is backed by the map, so changes to the 1032 * map are reflected in the collection, and vice-versa. The collection supports element removal, 1033 * which removes the corresponding mapping from the map, via the <tt>Iterator.remove</tt>, 1034 * <tt>Collection.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> 1035 * operations. It does not support the <tt>add</tt> or <tt>addAll</tt> operations. 1036 * 1037 * @return a collection view of the mappings contained in this map. 1038 * @see java.util.Map.Entry 1039 */ 1040 public Set<Entry<V>> entrySet() 1041 { 1042 Set<Entry<V>> es = entrySet; 1043 return (es != null ? es : (entrySet = new EntrySet())); 1044 } 1045 1046 private class EntrySet extends AbstractSet<Entry<V>> 1047 { 1048 /** 1049 * @see java.util.AbstractCollection#iterator() 1050 */ 1051 @Override 1052 public Iterator<Entry<V>> iterator() 1053 { 1054 return newEntryIterator(); 1055 } 1056 1057 /** 1058 * @see java.util.AbstractCollection#contains(java.lang.Object) 1059 */ 1060 @SuppressWarnings("unchecked") 1061 @Override 1062 public boolean contains(final Object o) 1063 { 1064 if (!(o instanceof Entry)) 1065 { 1066 return false; 1067 } 1068 Entry<V> e = (Entry<V>)o; 1069 Entry<V> candidate = getEntry(e.getKey()); 1070 return (candidate != null) && candidate.equals(e); 1071 } 1072 1073 /** 1074 * @see java.util.AbstractCollection#remove(java.lang.Object) 1075 */ 1076 @Override 1077 public boolean remove(final Object o) 1078 { 1079 return removeMapping(o) != null; 1080 } 1081 1082 /** 1083 * @see java.util.AbstractCollection#size() 1084 */ 1085 @Override 1086 public int size() 1087 { 1088 return size; 1089 } 1090 1091 /** 1092 * @see java.util.AbstractCollection#clear() 1093 */ 1094 @Override 1095 public void clear() 1096 { 1097 IntHashMap.this.clear(); 1098 } 1099 } 1100 1101 /** 1102 * Save the state of the <tt>HashMap</tt> instance to a stream (i.e., serialize it). 1103 * 1104 * @param s 1105 * The ObjectOutputStream 1106 * @throws IOException 1107 * 1108 * @serialData The <i>capacity</i> of the HashMap (the length of the bucket array) is emitted 1109 * (int), followed by the <i>size</i> of the HashMap (the number of key-value 1110 * mappings), followed by the key (Object) and value (Object) for each key-value 1111 * mapping represented by the HashMap The key-value mappings are emitted in the 1112 * order that they are returned by <tt>entrySet().iterator()</tt>. 1113 * 1114 */ 1115 private void writeObject(final java.io.ObjectOutputStream s) throws IOException 1116 { 1117 // Write out the threshold, loadfactor, and any hidden stuff 1118 s.defaultWriteObject(); 1119 1120 // Write out number of buckets 1121 s.writeInt(table.length); 1122 1123 // Write out size (number of Mappings) 1124 s.writeInt(size); 1125 1126 // Write out keys and values (alternating) 1127 for (Entry<V> entry : entrySet()) 1128 { 1129 s.writeInt(entry.getKey()); 1130 s.writeObject(entry.getValue()); 1131 } 1132 } 1133 1134 private static final long serialVersionUID = 362498820763181265L; 1135 1136 /** 1137 * Reconstitute the <tt>HashMap</tt> instance from a stream (i.e., deserialize it). 1138 * 1139 * @param s 1140 * @throws IOException 1141 * @throws ClassNotFoundException 1142 */ 1143 @SuppressWarnings("unchecked") 1144 private void readObject(final java.io.ObjectInputStream s) throws IOException, 1145 ClassNotFoundException 1146 { 1147 modCount = new AtomicInteger(0); 1148 1149 // Read in the threshold, loadfactor, and any hidden stuff 1150 s.defaultReadObject(); 1151 1152 // Read in number of buckets and allocate the bucket array; 1153 int numBuckets = s.readInt(); 1154 table = new Entry[numBuckets]; 1155 1156 init(); // Give subclass a chance to do its thing. 1157 1158 // Read in size (number of Mappings) 1159 int size = s.readInt(); 1160 1161 // Read the keys and values, and put the mappings in the HashMap 1162 for (int i = 0; i < size; i++) 1163 { 1164 int key = s.readInt(); 1165 V value = (V)s.readObject(); 1166 putForCreate(key, value); 1167 } 1168 } 1169 1170 // These methods are used when serializing HashSets 1171 int capacity() 1172 { 1173 return table.length; 1174 } 1175 1176 float loadFactor() 1177 { 1178 return loadFactor; 1179 } 1180}