1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 package org.jaxen.util;
63 import java.io.IOException;
64 import java.util.AbstractCollection;
65 import java.util.AbstractMap;
66 import java.util.AbstractSet;
67 import java.util.Collection;
68 import java.util.ConcurrentModificationException;
69 import java.util.Iterator;
70 import java.util.Map;
71 import java.util.NoSuchElementException;
72 import java.util.Set;
73
74 public class IdentityHashMap extends AbstractMap implements Map, Cloneable,
75 java.io.Serializable {
76 /***
77 * The hash table data.
78 */
79 private transient Entry table[];
80
81 /***
82 * The total number of mappings in the hash table.
83 */
84 private transient int count;
85
86 /***
87 * The table is rehashed when its size exceeds this threshold. (The
88 * value of this field is (int)(capacity * loadFactor).)
89 *
90 * @serial
91 */
92 private int threshold;
93
94 /***
95 * The load factor for the hashtable.
96 *
97 * @serial
98 */
99 private float loadFactor;
100
101 /***
102 * The number of times this HashMap has been structurally modified
103 * Structural modifications are those that change the number of mappings in
104 * the HashMap or otherwise modify its internal structure (e.g.,
105 * rehash). This field is used to make iterators on Collection-views of
106 * the HashMap fail-fast. (See ConcurrentModificationException).
107 */
108 private transient int modCount = 0;
109
110 /***
111 * Constructs a new, empty map with the specified initial
112 * capacity and the specified load factor.
113 *
114 * @param initialCapacity the initial capacity of the HashMap
115 * @param loadFactor the load factor of the HashMap
116 * @throws IllegalArgumentException if the initial capacity is less
117 * than zero, or if the load factor is non-positive
118 */
119 public IdentityHashMap(int initialCapacity, float loadFactor) {
120 if (initialCapacity < 0)
121 throw new IllegalArgumentException("Illegal Initial Capacity: "+
122 initialCapacity);
123 if (loadFactor <= 0 || Float.isNaN(loadFactor))
124 throw new IllegalArgumentException("Illegal Load factor: "+
125 loadFactor);
126 if (initialCapacity==0)
127 initialCapacity = 1;
128 this.loadFactor = loadFactor;
129 table = new Entry[initialCapacity];
130 threshold = (int)(initialCapacity * loadFactor);
131 }
132
133 /***
134 * Constructs a new, empty map with the specified initial capacity
135 * and default load factor, which is <code>0.75</code>.
136 *
137 * @param initialCapacity the initial capacity of the HashMap
138 * @throws IllegalArgumentException if the initial capacity is less
139 * than zero
140 */
141 public IdentityHashMap(int initialCapacity) {
142 this(initialCapacity, 0.75f);
143 }
144
145 /***
146 * Constructs a new, empty map with a default capacity and load
147 * factor, which is <code>0.75</code>.
148 */
149 public IdentityHashMap() {
150 this(11, 0.75f);
151 }
152
153 /***
154 * Constructs a new map with the same mappings as the given map. The
155 * map is created with a capacity of twice the number of mappings in
156 * the given map or 11 (whichever is greater), and a default load factor,
157 * which is <code>0.75</code>.
158 *
159 * @param t the map whose mappings are to be placed in this map
160 */
161 public IdentityHashMap(Map t) {
162 this(Math.max(2*t.size(), 11), 0.75f);
163 putAll(t);
164 }
165
166 /***
167 * Returns the number of key-value mappings in this map
168 *
169 * @return the number of key-value mappings in this map
170 */
171 public int size() {
172 return count;
173 }
174
175 /***
176 * Returns <code>true</code> if this map contains no key-value mappings
177 *
178 * @return <code>true</code> if this map contains no key-value mappings
179 */
180 public boolean isEmpty() {
181 return count == 0;
182 }
183
184 /***
185 * Returns <code>true</code> if this map maps one or more keys to the
186 * specified value.
187 *
188 * @param value value whose presence in this map is to be tested.
189 * @return <code>true</code> if this map maps one or more keys to the
190 * specified value
191 */
192 public boolean containsValue(Object value) {
193 Entry tab[] = table;
194
195 if (value==null) {
196 for (int i = tab.length ; i > 0 ; i--)
197 for (Entry e = tab[i] ; e != null ; e = e.next)
198 if (e.value==null)
199 return true;
200 } else {
201 for (int i = tab.length ; i > 0 ; i--)
202 for (Entry e = tab[i] ; e != null ; e = e.next)
203 if (value.equals(e.value))
204 return true;
205 }
206
207 return false;
208 }
209
210 /***
211 * Returns <code>true</code> if this map contains a mapping for the specified
212 * key.
213 *
214 * @return <code>true</code> if this map contains a mapping for the specified
215 * key
216 * @param key key whose presence in this Map is to be tested
217 */
218 public boolean containsKey(Object key) {
219 Entry tab[] = table;
220 if (key != null) {
221
222 int hash = System.identityHashCode( key );
223 int index = (hash & 0x7FFFFFFF) % tab.length;
224 for (Entry e = tab[index]; e != null; e = e.next)
225
226 if (e.hash==hash && ( key == e.key ) )
227 return true;
228 } else {
229 for (Entry e = tab[0]; e != null; e = e.next)
230 if (e.key==null) return true;
231 }
232
233 return false;
234 }
235
236 /***
237 * Returns the value to which this map maps the specified key. Returns
238 * <code>null</code> if the map contains no mapping for this key. A return
239 * value of <code>null</code> does not <i>necessarily</i> indicate that the
240 * map contains no mapping for the key; it's also possible that the map
241 * explicitly maps the key to <code>null</code>. The <code>containsKey</code>
242 * operation may be used to distinguish these two cases.
243 *
244 * @return the value to which this map maps the specified key
245 * @param key key whose associated value is to be returned
246 */
247 public Object get(Object key) {
248 Entry tab[] = table;
249
250 if (key != null) {
251
252 int hash = System.identityHashCode( key );
253 int index = (hash & 0x7FFFFFFF) % tab.length;
254 for (Entry e = tab[index]; e != null; e = e.next)
255
256 if ((e.hash == hash) && ( key == e.key ) )
257 return e.value;
258 } else {
259 for (Entry e = tab[0]; e != null; e = e.next)
260 if (e.key==null)
261 return e.value;
262 }
263
264 return null;
265 }
266
267 /***
268 * Rehashes the contents of this map into a new <code>IdentityHashMap</code> instance
269 * with a larger capacity. This method is called automatically when the
270 * number of keys in this map exceeds its capacity and load factor.
271 */
272 private void rehash() {
273 int oldCapacity = table.length;
274 Entry oldMap[] = table;
275
276 int newCapacity = oldCapacity * 2 + 1;
277 Entry newMap[] = new Entry[newCapacity];
278
279 modCount++;
280 threshold = (int)(newCapacity * loadFactor);
281 table = newMap;
282
283 for (int i = oldCapacity ; i-- > 0 ;) {
284 for (Entry old = oldMap[i] ; old != null ; ) {
285 Entry e = old;
286 old = old.next;
287
288 int index = (e.hash & 0x7FFFFFFF) % newCapacity;
289 e.next = newMap[index];
290 newMap[index] = e;
291 }
292 }
293 }
294
295 /***
296 * Associates the specified value with the specified key in this map.
297 * If the map previously contained a mapping for this key, the old
298 * value is replaced.
299 *
300 * @param key key with which the specified value is to be associated
301 * @param value value to be associated with the specified key
302 * @return previous value associated with specified key, or <code>null</code>
303 * if there was no mapping for key. A <code>null</code> return can
304 * also indicate that the IdentityHashMap previously associated
305 * <code>null</code> with the specified key.
306 */
307 public Object put(Object key, Object value) {
308
309
310
311
312
313
314 Entry tab[] = table;
315 int hash = 0;
316 int index = 0;
317
318 if (key != null) {
319 hash = System.identityHashCode( key );
320 index = (hash & 0x7FFFFFFF) % tab.length;
321 for (Entry e = tab[index] ; e != null ; e = e.next) {
322 if ((e.hash == hash) && ( key == e.key ) ) {
323 Object old = e.value;
324 e.value = value;
325 return old;
326 }
327 }
328 } else {
329 for (Entry e = tab[0] ; e != null ; e = e.next) {
330 if (e.key == null) {
331 Object old = e.value;
332 e.value = value;
333 return old;
334 }
335 }
336 }
337
338 modCount++;
339 if (count >= threshold) {
340
341 rehash();
342
343 tab = table;
344 index = (hash & 0x7FFFFFFF) % tab.length;
345 }
346
347
348 Entry e = new Entry(hash, key, value, tab[index]);
349 tab[index] = e;
350 count++;
351 return null;
352 }
353
354 /***
355 * Removes the mapping for this key from this map if present.
356 *
357 * @param key key whose mapping is to be removed from the map
358 * @return previous value associated with specified key, or <code>null</code>
359 * if there was no mapping for key. A <code>null</code> return can
360 * also indicate that the map previously associated <code>null</code>
361 * with the specified key.
362 */
363 public Object remove(Object key) {
364 Entry tab[] = table;
365
366 if (key != null) {
367
368 int hash = System.identityHashCode( key );
369 int index = (hash & 0x7FFFFFFF) % tab.length;
370
371 for (Entry e = tab[index], prev = null; e != null;
372 prev = e, e = e.next) {
373
374 if ((e.hash == hash) && ( key == e.key ) ) {
375 modCount++;
376 if (prev != null)
377 prev.next = e.next;
378 else
379 tab[index] = e.next;
380
381 count--;
382 Object oldValue = e.value;
383 e.value = null;
384 return oldValue;
385 }
386 }
387 } else {
388 for (Entry e = tab[0], prev = null; e != null;
389 prev = e, e = e.next) {
390 if (e.key == null) {
391 modCount++;
392 if (prev != null)
393 prev.next = e.next;
394 else
395 tab[0] = e.next;
396
397 count--;
398 Object oldValue = e.value;
399 e.value = null;
400 return oldValue;
401 }
402 }
403 }
404
405 return null;
406 }
407
408 /***
409 * Copies all of the mappings from the specified map to this one.
410 *
411 * These mappings replace any mappings that this map had for any of the
412 * keys currently in the specified Map.
413 *
414 * @param t mappings to be stored in this map
415 */
416 public void putAll(Map t) {
417 Iterator i = t.entrySet().iterator();
418 while (i.hasNext()) {
419 Map.Entry e = (Map.Entry) i.next();
420 put(e.getKey(), e.getValue());
421 }
422 }
423
424 /***
425 * Removes all mappings from this map.
426 */
427 public void clear() {
428 Entry tab[] = table;
429 modCount++;
430 for (int index = tab.length; --index >= 0; )
431 tab[index] = null;
432 count = 0;
433 }
434
435 /***
436 * Returns a shallow copy of this <code>IdentityHashMap</code> instance: the keys and
437 * values themselves are not cloned.
438 *
439 * @return a shallow copy of this map
440 */
441 public Object clone() {
442 try {
443 IdentityHashMap t = (IdentityHashMap)super.clone();
444 t.table = new Entry[table.length];
445 for (int i = table.length ; i-- > 0 ; ) {
446 t.table[i] = (table[i] != null)
447 ? (Entry)table[i].clone() : null;
448 }
449 t.keySet = null;
450 t.entrySet = null;
451 t.values = null;
452 t.modCount = 0;
453 return t;
454 } catch (CloneNotSupportedException e) {
455
456 throw new InternalError();
457 }
458 }
459
460
461
462 private transient Set keySet = null;
463 private transient Set entrySet = null;
464 private transient Collection values = null;
465
466 /***
467 * Returns a set view of the keys contained in this map. The set is
468 * backed by the map, so changes to the map are reflected in the set, and
469 * vice-versa. The set supports element removal, which removes the
470 * corresponding mapping from this map, via the <code>Iterator.remove</code>,
471 * <code>Set.remove</code>, <code>removeAll</code>, <code>retainAll</code>, and
472 * <code>clear</code> operations. It does not support the <code>add</code> or
473 * <code>addAll</code> operations.
474 *
475 * @return a set view of the keys contained in this map
476 */
477 public Set keySet() {
478 if (keySet == null) {
479 keySet = new AbstractSet() {
480 public Iterator iterator() {
481 return getHashIterator(KEYS);
482 }
483 public int size() {
484 return count;
485 }
486 public boolean contains(Object o) {
487 return containsKey(o);
488 }
489 public boolean remove(Object o) {
490 int oldSize = count;
491 IdentityHashMap.this.remove(o);
492 return count != oldSize;
493 }
494 public void clear() {
495 IdentityHashMap.this.clear();
496 }
497 };
498 }
499 return keySet;
500 }
501
502 /***
503 * Returns a collection view of the values contained in this map. The
504 * collection is backed by the map, so changes to the map are reflected in
505 * the collection, and vice-versa. The collection supports element
506 * removal, which removes the corresponding mapping from this map, via the
507 * <code>Iterator.remove</code>, <code>Collection.remove</code>,
508 * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code> operations.
509 * It does not support the <code>add</code> or <code>addAll</code> operations.
510 *
511 * @return a collection view of the values contained in this map
512 */
513 public Collection values() {
514 if (values==null) {
515 values = new AbstractCollection() {
516 public Iterator iterator() {
517 return getHashIterator(VALUES);
518 }
519 public int size() {
520 return count;
521 }
522 public boolean contains(Object o) {
523 return containsValue(o);
524 }
525 public void clear() {
526 IdentityHashMap.this.clear();
527 }
528 };
529 }
530 return values;
531 }
532
533 /***
534 * Returns a collection view of the mappings contained in this map. Each
535 * element in the returned collection is a <code>Map.Entry</code>. The
536 * collection is backed by the map, so changes to the map are reflected in
537 * the collection, and vice-versa. The collection supports element
538 * removal, which removes the corresponding mapping from the map, via the
539 * <code>Iterator.remove</code>, <code>Collection.remove</code>,
540 * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code> operations.
541 * It does not support the <code>add</code> or <code>addAll</code> operations.
542 *
543 * @return a collection view of the mappings contained in this map
544 * @see Map.Entry
545 */
546 public Set entrySet() {
547 if (entrySet==null) {
548 entrySet = new AbstractSet() {
549 public Iterator iterator() {
550 return getHashIterator(ENTRIES);
551 }
552
553 public boolean contains(Object o) {
554 if (!(o instanceof Map.Entry))
555 return false;
556 Map.Entry entry = (Map.Entry)o;
557 Object key = entry.getKey();
558 Entry tab[] = table;
559
560 int hash = (key==null ? 0 : System.identityHashCode( key ) );
561 int index = (hash & 0x7FFFFFFF) % tab.length;
562
563 for (Entry e = tab[index]; e != null; e = e.next)
564 if (e.hash==hash && e.equals(entry))
565 return true;
566 return false;
567 }
568
569 public boolean remove(Object o) {
570 if (!(o instanceof Map.Entry))
571 return false;
572 Map.Entry entry = (Map.Entry)o;
573 Object key = entry.getKey();
574 Entry tab[] = table;
575
576 int hash = (key==null ? 0 : System.identityHashCode( key ) );
577 int index = (hash & 0x7FFFFFFF) % tab.length;
578
579 for (Entry e = tab[index], prev = null; e != null;
580 prev = e, e = e.next) {
581 if (e.hash==hash && e.equals(entry)) {
582 modCount++;
583 if (prev != null)
584 prev.next = e.next;
585 else
586 tab[index] = e.next;
587
588 count--;
589 e.value = null;
590 return true;
591 }
592 }
593 return false;
594 }
595
596 public int size() {
597 return count;
598 }
599
600 public void clear() {
601 IdentityHashMap.this.clear();
602 }
603 };
604 }
605
606 return entrySet;
607 }
608
609 private Iterator getHashIterator(int type) {
610 if (count == 0) {
611 return emptyHashIterator;
612 } else {
613 return new HashIterator(type);
614 }
615 }
616
617 /***
618 * IdentityHashMap collision list entry.
619 */
620 private static class Entry implements Map.Entry {
621 int hash;
622 Object key;
623 Object value;
624 Entry next;
625
626 Entry(int hash, Object key, Object value, Entry next) {
627 this.hash = hash;
628 this.key = key;
629 this.value = value;
630 this.next = next;
631 }
632
633 protected Object clone() {
634 return new Entry(hash, key, value,
635 (next==null ? null : (Entry)next.clone()));
636 }
637
638
639
640 public Object getKey() {
641 return key;
642 }
643
644 public Object getValue() {
645 return value;
646 }
647
648 public Object setValue(Object value) {
649 Object oldValue = this.value;
650 this.value = value;
651 return oldValue;
652 }
653
654 public boolean equals(Object o) {
655 if (!(o instanceof Map.Entry))
656 return false;
657 Map.Entry e = (Map.Entry)o;
658
659 return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
660 (value==null ? e.getValue()==null : value.equals(e.getValue()));
661 }
662
663 public int hashCode() {
664 return hash ^ (value==null ? 0 : value.hashCode());
665 }
666
667 public String toString() {
668 return key+"="+value;
669 }
670 }
671
672
673 private static final int KEYS = 0;
674 private static final int VALUES = 1;
675 private static final int ENTRIES = 2;
676
677 private static EmptyHashIterator emptyHashIterator
678 = new EmptyHashIterator();
679
680 private static class EmptyHashIterator implements Iterator {
681
682 EmptyHashIterator() {
683
684 }
685
686 public boolean hasNext() {
687 return false;
688 }
689
690 public Object next() {
691 throw new NoSuchElementException();
692 }
693
694 public void remove() {
695 throw new IllegalStateException();
696 }
697
698 }
699
700 private class HashIterator implements Iterator {
701 Entry[] table = IdentityHashMap.this.table;
702 int index = table.length;
703 Entry entry = null;
704 Entry lastReturned = null;
705 int type;
706
707 /***
708 * The modCount value that the iterator believes that the backing
709 * List should have. If this expectation is violated, the iterator
710 * has detected concurrent modification.
711 */
712 private int expectedModCount = modCount;
713
714 HashIterator(int type) {
715 this.type = type;
716 }
717
718 public boolean hasNext() {
719 Entry e = entry;
720 int i = index;
721 Entry t[] = table;
722
723 while (e == null && i > 0)
724 e = t[--i];
725 entry = e;
726 index = i;
727 return e != null;
728 }
729
730 public Object next() {
731 if (modCount != expectedModCount)
732 throw new ConcurrentModificationException();
733
734 Entry et = entry;
735 int i = index;
736 Entry t[] = table;
737
738
739 while (et == null && i > 0)
740 et = t[--i];
741
742 entry = et;
743 index = i;
744 if (et != null) {
745 Entry e = lastReturned = entry;
746 entry = e.next;
747 return type == KEYS ? e.key : (type == VALUES ? e.value : e);
748 }
749 throw new NoSuchElementException();
750 }
751
752 public void remove() {
753 if (lastReturned == null)
754 throw new IllegalStateException();
755 if (modCount != expectedModCount)
756 throw new ConcurrentModificationException();
757
758 Entry[] tab = IdentityHashMap.this.table;
759 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
760
761 for (Entry e = tab[index], prev = null; e != null;
762 prev = e, e = e.next) {
763 if (e == lastReturned) {
764 modCount++;
765 expectedModCount++;
766 if (prev == null)
767 tab[index] = e.next;
768 else
769 prev.next = e.next;
770 count--;
771 lastReturned = null;
772 return;
773 }
774 }
775 throw new ConcurrentModificationException();
776 }
777 }
778
779 /***
780 * Save the state of the <code>IdentityHashMap</code> instance to a stream (i.e.,
781 * serialize it).
782 *
783 * @serialData the <em>capacity</em> of the IdentityHashMap (the length of the
784 * bucket array) is emitted (int), followed by the
785 * <em>size</em> of the IdentityHashMap (the number of key-value
786 * mappings), followed by the key (Object) and value (Object)
787 * for each key-value mapping represented by the IdentityHashMap.
788 * The key-value mappings are emitted in no particular order.
789 */
790 private void writeObject(java.io.ObjectOutputStream s)
791 throws IOException
792 {
793
794 s.defaultWriteObject();
795
796
797 s.writeInt(table.length);
798
799
800 s.writeInt(count);
801
802
803 for (int index = table.length-1; index >= 0; index--) {
804 Entry entry = table[index];
805
806 while (entry != null) {
807 s.writeObject(entry.key);
808 s.writeObject(entry.value);
809 entry = entry.next;
810 }
811 }
812 }
813
814 private static final long serialVersionUID = 362498820763181265L;
815
816 /***
817 * Reconstitute the <code>IdentityHashMap</code> instance from a stream (i.e.,
818 * deserialize it).
819 */
820 private void readObject(java.io.ObjectInputStream s)
821 throws IOException, ClassNotFoundException
822 {
823
824 s.defaultReadObject();
825
826
827 int numBuckets = s.readInt();
828 table = new Entry[numBuckets];
829
830
831 int size = s.readInt();
832
833
834 for (int i=0; i<size; i++) {
835 Object key = s.readObject();
836 Object value = s.readObject();
837 put(key, value);
838 }
839 }
840
841 int capacity() {
842 return table.length;
843 }
844
845 float loadFactor() {
846 return loadFactor;
847 }
848 }