4 package net
.kezvh
.collections
.Map2
;
6 import java
.io
.IOException
;
7 import java
.lang
.reflect
.Array
;
8 import java
.util
.AbstractCollection
;
9 import java
.util
.AbstractMap
;
10 import java
.util
.AbstractSet
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Collection
;
13 import java
.util
.ConcurrentModificationException
;
14 import java
.util
.HashMap
;
15 import java
.util
.Iterator
;
17 import java
.util
.NoSuchElementException
;
20 import net
.kezvh
.collections
.AbstractIterator
;
21 import net
.kezvh
.collections
.MapEntryImpl
;
22 import net
.kezvh
.collections
.Pair
;
23 import net
.kezvh
.collections
.views
.SetView
;
24 import net
.kezvh
.functional
.lambda
.L1
;
27 * Sometimes you want a map with 2 keys. While one could just use java's maps but with
28 * something like Couple<K1, K2> as the key, there's an additional opperation I often
29 * want to do that you just can't do with something like that. In particular, this
30 * operation is incredibly useful if you use this collection to map the edges of a graph,
31 * and you want to iterate through all the edges into or out of a particular node.
33 * That operation is to slice the map by 1 key or the other.
35 * This class grepped a lot of its guts out of java's hashmap implementation (is that
36 * an ip issue?). However, each entry in the table also maintains connections to
37 * other entries which share it's first or second key. This way, you've got a linked
38 * list of entries for each key along each dimension.
42 * this is operationally identical to getting on a hashmap, so O(1)
44 * hashtable put + 2 linked list head inserts, so also O(1) amortized
46 * hashtable remove + 2 doubly linked list remove, so O(1)
48 * returns a list of elements backed by the map in O(1) time
49 * remove(k1) || remove(k2)
50 * O(n), where n is the number of elements in get(k1) or get(k2) respectively.
57 public class HashMap2
<K1
, K2
, V
> implements Map2
<K1
, K2
, V
> {
59 * The default initial capacity - MUST be a power of two.
61 static final int DEFAULT_INITIAL_CAPACITY
= 16;
64 * The maximum capacity, used if a higher value is implicitly specified by
65 * either of the constructors with arguments. MUST be a power of two <=
68 static final int MAXIMUM_CAPACITY
= 1 << 30;
71 * The load factor used when none specified in constructor.
73 static final float DEFAULT_LOAD_FACTOR
= 0.75f
;
75 private static final <K1
, K2
, V
> Entry
<K1
, K2
, V
>[] createEntries(final Entry
<K1
, K2
, V
>[] other
, final int capacity
) {
76 return HashMap2
.createEntries(other
.getClass().getComponentType(), capacity
);
79 @SuppressWarnings("unchecked")
80 private static final <K1
, K2
, V
> Entry
<K1
, K2
, V
>[] createEntries(final Class
<?
> clazz
, final int capacity
) {
81 return (Entry
<K1
, K2
, V
>[]) Array
.newInstance(clazz
, capacity
);
84 private static final <K1
, K2
> int hash(final K1 k1
, final K2 k2
) {
92 else if (k2
== null || k1
.equals(k2
))
95 h
= k1
.hashCode() ^ k2
.hashCode();
97 h ^
= (h
>>> 20) ^
(h
>>> 12);
98 return h ^
(h
>>> 7) ^
(h
>>> 4);
101 private static final <K1
, K2
> int hash(final Pair
<K1
, K2
> entry
) {
102 return HashMap2
.hash(entry
.get1(), entry
.get2());
105 private static final int indexFor(final int hash
, final int length
) {
106 return hash
& (length
- 1);
110 * TODO make this a libary method
117 private static final <T
> boolean objectsEqual(final T a
, final T b
) {
124 * so the complicated thing is this resides in 3 lists: 1 is the list of
125 * collisions 1 is the list of all entries matching key 1 1 is the list of
126 * all entries matching key 2
133 private static final class Entry
<K1
, K2
, V
> implements Map2
.Entry
<K1
, K2
, V
>, Pair
<K1
, K2
> {
137 private final int hash
;
138 private Entry
<K1
, K2
, V
> nextCollision
;
139 private Entry
<K1
, K2
, V
> next1
; // next in loop w/ fixed k1
140 private Entry
<K1
, K2
, V
> next2
; // next in loop w/ fixed k2
141 private Entry
<K1
, K2
, V
> prev1
; // prev in loop w/ fixed k1
142 private Entry
<K1
, K2
, V
> prev2
; // prev in loop w/ fixed k2
144 public Entry(final int hash
, final K1 k1
, final K2 k2
, final V v
, final Entry
<K1
, K2
, V
> nextCollision
) {
148 this.nextCollision
= nextCollision
;
153 * i kind of love this function
155 * @param bimap COMMENT
157 public void removeSelf(final HashMap2
<K1
, K2
, V
> bimap
) {
158 if (this.next1
!= null)
159 this.next1
.setPrev1(this.prev1
);
160 if (this.next2
!= null)
161 this.next2
.setPrev2(this.prev2
);
162 if (this.prev1
!= null)
163 this.prev1
.setNext1(this.next1
);
164 if (this.prev2
!= null)
165 this.prev2
.setNext2(this.next2
);
166 bimap
.k1Heads
.get(this.k1
).removing(this);
167 if (bimap
.k1Heads
.get(this.k1
).isEmpty())
168 bimap
.k1Heads
.remove(this.k1
);
169 bimap
.k2Heads
.get(this.k2
).removing(this);
170 if (bimap
.k2Heads
.get(this.k2
).isEmpty())
171 bimap
.k2Heads
.remove(this.k2
);
177 public Entry
<K1
, K2
, V
> getNext1() {
182 * @param next1 the next1 to set
185 public Entry
<K1
, K2
, V
> setNext1(final Entry
<K1
, K2
, V
> next1
) {
196 public Entry
<K1
, K2
, V
> getNext2() {
201 * @param next2 the next2 to set
204 public Entry
<K1
, K2
, V
> setNext2(final Entry
<K1
, K2
, V
> next2
) {
215 public Entry
<K1
, K2
, V
> getPrev1() {
220 * @param prev1 the prev1 to set
223 public Entry
<K1
, K2
, V
> setPrev1(final Entry
<K1
, K2
, V
> prev1
) {
234 public Entry
<K1
, K2
, V
> getPrev2() {
239 * @param prev2 the prev2 to set
242 public Entry
<K1
, K2
, V
> setPrev2(final Entry
<K1
, K2
, V
> prev2
) {
250 public boolean matchesKeys(final K1 key1
, final K2 key2
) {
251 return HashMap2
.objectsEqual(this.k1
, key1
) && HashMap2
.objectsEqual(this.k2
, key2
);
255 * @return the nextCollision
257 public Entry
<K1
, K2
, V
> getNextCollision() {
258 return this.nextCollision
;
262 * @param nextCollision the nextCollision to set
264 public void setNextCollision(final Entry
<K1
, K2
, V
> nextCollision
) {
265 this.nextCollision
= nextCollision
;
269 * @see java.lang.Object#hashCode()
273 public int hashCode() {
278 * @see java.lang.Object#equals(java.lang.Object)
283 public boolean equals(final Object obj
) {
288 if (this.getClass() != obj
.getClass())
290 final Entry
<?
, ?
, ?
> other
= (Entry
<?
, ?
, ?
>) obj
;
291 if (this.k1
== null) {
292 if (other
.k1
!= null)
294 } else if (!this.k1
.equals(other
.k1
))
296 if (this.k2
== null) {
297 if (other
.k2
!= null)
299 } else if (!this.k2
.equals(other
.k2
))
305 * @see net.kezvh.collections.Map2.Map2.Entry#getKey1()
309 public K1
getKey1() {
314 * @see net.kezvh.collections.Map2.Map2.Entry#getKey2()
318 public K2
getKey2() {
323 * @see net.kezvh.collections.Map2.Map2.Entry#getValue()
327 public V
getValue() {
332 * @see net.kezvh.collections.Map2.Map2.Entry#setValue(java.lang.Object)
333 * @param value COMMENT
337 public V
setValue(final V value
) {
346 * @see java.util.Map.Entry#getKey()
350 public Pair
<K1
, K2
> getKey() {
355 * @see net.kezvh.collections.Pair#get1()
364 * @see net.kezvh.collections.Pair#get2()
373 private abstract class HeadSet
extends AbstractSet
<Entry
<K1
, K2
, V
>> {
374 private int headsetSize
;
376 private Entry
<K1
, K2
, V
> head
;
378 private final class HeadSetIterator
extends AbstractIterator
<Entry
<K1
, K2
, V
>> {
379 private Entry
<K1
, K2
, V
> current
= HeadSet
.this.head
;
382 * @see net.kezvh.collections.AbstractIterator#findNext()
384 * @throws NoSuchElementException COMMENT
387 protected Entry
<K1
, K2
, V
> findNext() throws NoSuchElementException
{
388 if (this.current
== null)
389 throw new NoSuchElementException();
393 this.current
= HeadSet
.this.getNext(this.current
);
398 * @see net.kezvh.collections.AbstractIterator#remove()
401 public void remove() {
402 HashMap2
.this.remove(HeadSet
.this.getPrev(this.current
));
406 public final void insertNewHead(final Entry
<K1
, K2
, V
> newHead
) {
407 this.setNext(newHead
, this.head
);
408 if (this.head
!= null)
409 this.setPrev(this.head
, newHead
);
414 public final void removing(final Entry
<K1
, K2
, V
> removedEntry
) {
415 if (this.head
== removedEntry
)
416 this.head
= this.getNext(removedEntry
);
420 protected abstract Entry
<K1
, K2
, V
> getNext(Entry
<K1
, K2
, V
> entry
);
422 protected abstract Entry
<K1
, K2
, V
> getPrev(Entry
<K1
, K2
, V
> entry
);
424 protected abstract Entry
<K1
, K2
, V
> setNext(Entry
<K1
, K2
, V
> entry
, Entry
<K1
, K2
, V
> next
);
426 protected abstract Entry
<K1
, K2
, V
> setPrev(Entry
<K1
, K2
, V
> entry
, Entry
<K1
, K2
, V
> prev
);
429 * @see java.util.AbstractCollection#iterator()
433 public Iterator
<Entry
<K1
, K2
, V
>> iterator() {
434 return new HeadSetIterator();
438 * @see java.util.AbstractCollection#size()
443 return this.headsetSize
;
447 * @see java.util.AbstractCollection#add(java.lang.Object)
452 public boolean add(final Entry
<K1
, K2
, V
> o
) {
453 return HashMap2
.this.put(o
) != null;
457 * @see java.util.AbstractCollection#addAll(java.util.Collection)
462 public boolean addAll(final Collection
<?
extends Entry
<K1
, K2
, V
>> c
) {
463 return HashMap2
.this.putAll(c
);
467 * @see java.util.AbstractCollection#clear()
470 public void clear() {
471 for (final Entry
<K1
, K2
, V
> entry
: this)
472 HashMap2
.this.remove(entry
);
476 * @see java.util.AbstractCollection#contains(java.lang.Object)
480 @SuppressWarnings("unchecked")
482 public boolean contains(final Object o
) {
483 if (!(o
instanceof Map2
.Entry
))
486 final Map2
.Entry
<K1
, K2
, V
> other
= (Map2
.Entry
<K1
, K2
, V
>) o
;
487 return HashMap2
.this.containsKey(other
.getKey1(), other
.getKey2());
491 * @see java.util.AbstractCollection#containsAll(java.util.Collection)
496 public boolean containsAll(final Collection
<?
> c
) {
497 for (final Object entry
: c
)
498 if (!this.contains(entry
))
504 * @see java.util.AbstractCollection#isEmpty()
508 public boolean isEmpty() {
509 return this.head
== null;
513 * @see java.util.AbstractCollection#remove(java.lang.Object)
518 public boolean remove(final Object o
) {
519 return HashMap2
.this.remove(o
) != null;
523 * @see java.util.AbstractCollection#retainAll(java.util.Collection)
528 public boolean retainAll(final Collection
<?
> c
) {
529 boolean modified
= false;
530 for (final Entry
<K1
, K2
, V
> entry
: this)
531 if (!c
.contains(entry
))
532 modified
|= HashMap2
.this.remove(entry
) != null;
538 * @see java.util.AbstractCollection#toArray()
539 * @return TOOD COMMENT
542 public Object
[] toArray() {
543 final Entry
<K1
, K2
, V
>[] array
= HashMap2
.createEntries(this.head
.getClass(), HashMap2
.this.size
);
544 return this.toArray(array
);
547 @SuppressWarnings("unchecked")
549 public <T
extends Object
> T
[] toArray(final T
[] a
) {
550 final T
[] b
= a
.length
>= HashMap2
.this.size ? a
: (T
[]) HashMap2
.createEntries(a
.getClass().getComponentType(), HashMap2
.this.size
);
552 for (final Entry
<K1
, K2
, V
> entry
: this)
558 private final class HeadSet1
extends HeadSet
{
560 protected Entry
<K1
, K2
, V
> getPrev(final Entry
<K1
, K2
, V
> arg0
) {
561 return arg0
.getNext1();
565 protected Entry
<K1
, K2
, V
> getNext(final Entry
<K1
, K2
, V
> arg0
) {
566 return arg0
.getPrev1();
570 protected Entry
<K1
, K2
, V
> setNext(final Entry
<K1
, K2
, V
> entry
, final Entry
<K1
, K2
, V
> next
) {
571 return entry
.setNext1(next
);
575 protected Entry
<K1
, K2
, V
> setPrev(final Entry
<K1
, K2
, V
> entry
, final Entry
<K1
, K2
, V
> prev
) {
576 return entry
.setPrev1(prev
);
580 private final class HeadSet2
extends HeadSet
{
582 protected Entry
<K1
, K2
, V
> getPrev(final Entry
<K1
, K2
, V
> arg0
) {
583 return arg0
.getNext2();
587 protected Entry
<K1
, K2
, V
> getNext(final Entry
<K1
, K2
, V
> arg0
) {
588 return arg0
.getPrev2();
592 protected Entry
<K1
, K2
, V
> setNext(final Entry
<K1
, K2
, V
> entry
, final Entry
<K1
, K2
, V
> next
) {
593 return entry
.setNext2(next
);
597 protected Entry
<K1
, K2
, V
> setPrev(final Entry
<K1
, K2
, V
> entry
, final Entry
<K1
, K2
, V
> prev
) {
598 return entry
.setPrev2(prev
);
602 private final Map
<K1
, HeadSet1
> k1Heads
= new HashMap
<K1
, HeadSet1
>();
603 private final Map
<K2
, HeadSet2
> k2Heads
= new HashMap
<K2
, HeadSet2
>();
604 private Entry
<K1
, K2
, V
>[] entries
;
606 * The number of key-value mappings contained in this identity hash map.
608 private transient int size
;
611 * The next size value at which to resize (capacity * load factor).
615 private int threshold
;
618 * The load factor for the hash table.
622 private final float loadFactor
;
625 * The number of times this HashMap has been structurally modified
626 * Structural modifications are those that change the number of mappings in
627 * the HashMap or otherwise modify its internal structure (e.g., rehash).
628 * This field is used to make iterators on Collection-views of the HashMap
629 * fail-fast. (See ConcurrentModificationException).
631 private transient volatile int modCount
;
634 * TODO refactor this so it uses AbstractIterator
639 private abstract class HashIterator
<E
> implements Iterator
<E
> {
640 Entry
<K1
, K2
, V
> next
; // next entry to return
641 int expectedModCount
; // For fast-fail
642 int index
; // current slot
643 Entry
<K1
, K2
, V
> current
; // current entry
645 public HashIterator() {
646 this.expectedModCount
= HashMap2
.this.modCount
;
647 final Entry
<K1
, K2
, V
>[] t
= HashMap2
.this.entries
;
649 Entry
<K1
, K2
, V
> n
= null;
650 if (HashMap2
.this.size
!= 0)
651 while (i
> 0 && (n
= t
[--i
]) == null) {
652 // nothing TODO maybe refactor this so it's.. not so ugly
658 public boolean hasNext() {
659 return this.next
!= null;
662 protected Entry
<K1
, K2
, V
> nextEntry() {
663 if (HashMap2
.this.modCount
!= this.expectedModCount
)
664 throw new ConcurrentModificationException();
665 final Entry
<K1
, K2
, V
> e
= this.next
;
667 throw new NoSuchElementException();
669 Entry
<K1
, K2
, V
> n
= e
.getNextCollision();
670 final Entry
<K1
, K2
, V
>[] t
= HashMap2
.this.entries
;
672 while (n
== null && i
> 0)
676 return this.current
= e
;
679 public void remove() {
680 if (this.current
== null)
681 throw new IllegalStateException();
682 if (HashMap2
.this.modCount
!= this.expectedModCount
)
683 throw new ConcurrentModificationException();
684 final Pair
<K1
, K2
> k
= this.current
.getKey();
686 HashMap2
.this.removeEntryForKey(k
.get1(), k
.get2());
687 this.expectedModCount
= HashMap2
.this.modCount
;
692 private class ValueIterator
extends HashIterator
<V
> {
694 return this.nextEntry().getValue();
698 private class KeyIterator
extends HashIterator
<Pair
<K1
, K2
>> {
699 public Pair
<K1
, K2
> next() {
700 return this.nextEntry().getKey();
704 private class EntryIterator
extends HashIterator
<Map
.Entry
<Pair
<K1
, K2
>, V
>> {
705 public Map
.Entry
<Pair
<K1
, K2
>, V
> next() {
706 return this.nextEntry();
710 // Subclass overrides these to alter behavior of views' iterator() method
711 Iterator
<Pair
<K1
, K2
>> newKeyIterator() {
712 return new KeyIterator();
715 Iterator
<V
> newValueIterator() {
716 return new ValueIterator();
719 Iterator
<Map
.Entry
<Pair
<K1
, K2
>, V
>> newEntryIterator() {
720 return new EntryIterator();
725 * Each of these fields are initialized to contain an instance of the
726 * appropriate view the first time this view is requested. The views are
727 * stateless, so there's no reason to create more than one of each.
729 private transient volatile Set
<Pair
<K1
, K2
>> keySet
= null;
730 private transient volatile Collection
<V
> values
= null;
731 private transient volatile Set
<Map
.Entry
<Pair
<K1
, K2
>, V
>> entrySet
= null;
734 * Returns a set view of the keys contained in this map. The set is backed
735 * by the map, so changes to the map are reflected in the set, and
736 * vice-versa. The set supports element removal, which removes the
737 * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
738 * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
739 * <tt>clear</tt> operations. It does not support the <tt>add</tt> or
740 * <tt>addAll</tt> operations.
742 * @return a set view of the keys contained in this map.
744 public Set
<Pair
<K1
, K2
>> keySet() {
745 final Set
<Pair
<K1
, K2
>> ks
= this.keySet
;
746 return (ks
!= null ? ks
: (this.keySet
= new KeySet()));
749 private class KeySet
extends AbstractSet
<Pair
<K1
, K2
>> {
751 public Iterator
<Pair
<K1
, K2
>> iterator() {
752 return HashMap2
.this.newKeyIterator();
757 return HashMap2
.this.size
;
761 public boolean contains(final Object o
) {
762 return HashMap2
.this.containsKey(o
);
765 @SuppressWarnings("unchecked")
767 public boolean remove(final Object o
) {
768 final Pair
<K1
, K2
> other
= (Pair
<K1
, K2
>) o
;
769 return HashMap2
.this.removeEntryForKey(other
.get1(), other
.get2()) != null;
773 public void clear() {
774 HashMap2
.this.clear();
779 * Returns a collection view of the values contained in this map. The
780 * collection is backed by the map, so changes to the map are reflected in
781 * the collection, and vice-versa. The collection supports element removal,
782 * which removes the corresponding mapping from this map, via the
783 * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, <tt>removeAll</tt>,
784 * <tt>retainAll</tt>, and <tt>clear</tt> operations. It does not support
785 * the <tt>add</tt> or <tt>addAll</tt> operations.
787 * @return a collection view of the values contained in this map.
789 public Collection
<V
> values() {
790 final Collection
<V
> vs
= this.values
;
791 return (vs
!= null ? vs
: (this.values
= new Values()));
794 private class Values
extends AbstractCollection
<V
> {
796 public Iterator
<V
> iterator() {
797 return HashMap2
.this.newValueIterator();
802 return HashMap2
.this.size
;
806 public boolean contains(final Object o
) {
807 return HashMap2
.this.containsValue(o
);
811 public void clear() {
812 HashMap2
.this.clear();
817 * Returns a collection view of the mappings contained in this map. Each
818 * element in the returned collection is a <tt>Map.Entry</tt>. The
819 * collection is backed by the map, so changes to the map are reflected in
820 * the collection, and vice-versa. The collection supports element removal,
821 * which removes the corresponding mapping from the map, via the
822 * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, <tt>removeAll</tt>,
823 * <tt>retainAll</tt>, and <tt>clear</tt> operations. It does not support
824 * the <tt>add</tt> or <tt>addAll</tt> operations.
826 * @return a collection view of the mappings contained in this map.
829 public Set
<Map
.Entry
<Pair
<K1
, K2
>, V
>> entrySet() {
830 final Set
<Map
.Entry
<Pair
<K1
, K2
>, V
>> es
= this.entrySet
;
831 return (es
!= null ? es
: (this.entrySet
= new EntrySet()));
834 private Entry
<K1
, K2
, V
> getEntry(final K1 key1
, final K2 key2
) {
835 for (Entry
<K1
, K2
, V
> entry
= this.entries
[HashMap2
.indexFor(HashMap2
.hash(key1
, key2
), this.entries
.length
)]; entry
!= null; entry
= entry
.getNextCollision())
836 if (entry
.matchesKeys(key1
, key2
))
841 private class EntrySet
extends AbstractSet
<Map
.Entry
<Pair
<K1
, K2
>, V
>> {
843 public Iterator
<Map
.Entry
<Pair
<K1
, K2
>, V
>> iterator() {
844 return HashMap2
.this.newEntryIterator();
847 @SuppressWarnings("unchecked")
849 public boolean contains(final Object o
) {
850 if (!(o
instanceof Map2
.Entry
))
852 final Map2
.Entry
<K1
, K2
, V
> e
= (Map2
.Entry
<K1
, K2
, V
>) o
;
853 final Entry
<K1
, K2
, V
> candidate
= HashMap2
.this.getEntry(e
.getKey1(), e
.getKey2());
854 return candidate
!= null && candidate
.equals(e
);
857 @SuppressWarnings("unchecked")
859 public boolean remove(final Object o
) {
860 return HashMap2
.this.removeMapping((Map
.Entry
<Pair
<K1
, K2
>, V
>) o
) != null;
865 return HashMap2
.this.size
;
869 public void clear() {
870 HashMap2
.this.clear();
875 * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
878 * @serialData The <i>capacity</i> of the HashMap (the length of the bucket
879 * array) is emitted (int), followed by the <i>size</i> of the
880 * HashMap (the number of key-value mappings), followed by the
881 * key (Object) and value (Object) for each key-value mapping
882 * represented by the HashMap The key-value mappings are emitted
883 * in the order that they are returned by
884 * <tt>entrySet().iterator()</tt>.
886 private void writeObject(final java
.io
.ObjectOutputStream s
) throws IOException
{
887 final Iterator
<Map
.Entry
<Pair
<K1
, K2
>, V
>> i
= this.entrySet().iterator();
889 // Write out the threshold, loadfactor, and any hidden stuff
890 s
.defaultWriteObject();
892 // Write out number of buckets
893 s
.writeInt(this.entries
.length
);
895 // Write out size (number of Mappings)
896 s
.writeInt(this.size
);
898 // Write out keys and values (alternating)
899 while (i
.hasNext()) {
900 final Map
.Entry
<Pair
<K1
, K2
>, V
> e
= i
.next();
901 s
.writeObject(e
.getKey());
902 s
.writeObject(e
.getValue());
907 * allow subclasses to do initialization after deserialization
909 protected void init() {
913 private static final long serialVersionUID
= 362498820763181265L;
916 * Reconstitute the <tt>HashMap</tt> instance from a stream (i.e.,
919 @SuppressWarnings("unchecked")
920 private void readObject(final java
.io
.ObjectInputStream s
) throws IOException
, ClassNotFoundException
{
921 // Read in the threshold, loadfactor, and any hidden stuff
922 s
.defaultReadObject();
924 // Read in number of buckets and allocate the bucket array;
925 final int numBuckets
= s
.readInt();
926 this.entries
= HashMap2
.createEntries(new Entry
<K1
, K2
, V
>(0, null, null, null, null).getClass(), numBuckets
);
928 this.init(); // Give subclass a chance to do its thing.
930 // Read in size (number of Mappings)
931 final int readSize
= s
.readInt();
933 // Read the keys and values, and put the mappings in the HashMap
934 for (int i
= 0; i
< readSize
; i
++) {
935 final K1 key1
= (K1
) s
.readObject();
936 final K2 key2
= (K2
) s
.readObject();
937 final V value
= (V
) s
.readObject();
938 this.putForCreate(key1
, key2
, value
);
942 // These methods are used when serializing HashSets
944 return this.entries
.length
;
948 return this.loadFactor
;
955 this.entries
= HashMap2
.createEntries(new Entry
<K1
, K2
, V
>(0, null, null, null, null).getClass(), HashMap2
.DEFAULT_INITIAL_CAPACITY
);
956 this.loadFactor
= HashMap2
.DEFAULT_LOAD_FACTOR
;
960 * @see net.kezvh.collections.Map2.Map2#clear()
963 public void clear() {
964 this.entries
= HashMap2
.createEntries(this.entries
, HashMap2
.DEFAULT_INITIAL_CAPACITY
);
965 this.k1Heads
.clear();
966 this.k2Heads
.clear();
970 * Special version of remove for EntrySet.
972 private Entry
<K1
, K2
, V
> removeMapping(final Map
.Entry
<Pair
<K1
, K2
>, V
> o
) {
973 if (!(o
instanceof Map2
.Entry
))
976 final Map
.Entry
<Pair
<K1
, K2
>, V
> entry
= o
;
977 final Pair
<K1
, K2
> k
= entry
.getKey();
978 final int hash
= k
.hashCode();
979 final int i
= HashMap2
.indexFor(hash
, this.entries
.length
);
980 Entry
<K1
, K2
, V
> prev
= this.entries
[i
];
981 Entry
<K1
, K2
, V
> e
= prev
;
984 final Entry
<K1
, K2
, V
> next
= e
.getNextCollision();
985 if (e
.hash
== hash
&& e
.equals(entry
)) {
989 this.entries
[i
] = next
;
991 prev
.setNextCollision(next
);
1003 * @see net.kezvh.collections.Map2.Map2#containsKey(java.lang.Object,
1005 * @param key1 COMMENT
1006 * @param key2 COMMENT
1010 public boolean containsKey(final K1 key1
, final K2 key2
) {
1011 return this.getEntry(key1
, key2
) != null;
1015 * This method is used instead of put by constructors and pseudoconstructors
1016 * (clone, readObject). It does not resize the table, check for
1017 * comodification, etc. It calls createEntry rather than addEntry.
1019 private void putForCreate(final K1 key1
, final K2 key2
, final V value
) {
1020 final int hash
= HashMap2
.hash(key1
, key2
);
1021 final int i
= HashMap2
.indexFor(hash
, this.entries
.length
);
1024 * Look for preexisting entry for key. This will never happen for clone
1025 * or deserialize. It will only happen for construction if the input Map
1026 * is a sorted map whose ordering is inconsistent w/ equals.
1028 for (Entry
<K1
, K2
, V
> e
= this.entries
[i
]; e
!= null; e
= e
.nextCollision
)
1029 if (e
.hash
== hash
&& HashMap2
.objectsEqual(key1
, e
.get1()) && HashMap2
.objectsEqual(key2
, e
.get2())) {
1034 this.createEntry(hash
, key1
, key2
, value
, i
);
1038 * Like addEntry except that this version is used when creating entries as
1039 * part of Map construction or "pseudo-construction" (cloning,
1040 * deserialization). This version needn't worry about resizing the table.
1041 * Subclass overrides this to alter the behavior of HashMap(Map), clone, and
1044 private void createEntry(final int hash
, final K1 key1
, final K2 key2
, final V value
, final int bucketIndex
) {
1045 final Entry
<K1
, K2
, V
> e
= this.entries
[bucketIndex
];
1046 this.entries
[bucketIndex
] = new Entry
<K1
, K2
, V
>(hash
, key1
, key2
, value
, e
);
1051 * @see net.kezvh.collections.Map2.Map2#entrySetOnKey1(java.lang.Object)
1052 * @param key1 COMMENT
1056 public Set
<Map2
.Entry
<K1
, K2
, V
>> entrySetOnKey1(final K1 key1
) {
1057 return new SetView
<Entry
<K1
, K2
, V
>, Map2
.Entry
<K1
, K2
, V
>>(this.k1Heads
.get(key1
), this.getBimapEntry
);
1060 private final L1
<Map2
.Entry
<K1
, K2
, V
>, Entry
<K1
, K2
, V
>> getBimapEntry
= new L1
<Map2
.Entry
<K1
, K2
, V
>, Entry
<K1
, K2
, V
>>() {
1062 * @see net.kezvh.functional.lambda.L1#op(java.lang.Object)
1063 * @param param COMMENT
1067 public Map2
.Entry
<K1
, K2
, V
> op(final Entry
<K1
, K2
, V
> param
) {
1072 private final L1
<K1
, Entry
<K1
, K2
, V
>> getKey1
= new L1
<K1
, Entry
<K1
, K2
, V
>>() {
1074 * @see net.kezvh.functional.lambda.L1#op(java.lang.Object)
1075 * @param param COMMENT
1079 public K1
op(final Entry
<K1
, K2
, V
> param
) {
1080 return param
.getKey1();
1084 private final L1
<K2
, Entry
<K1
, K2
, V
>> getKey2
= new L1
<K2
, Entry
<K1
, K2
, V
>>() {
1086 * @see net.kezvh.functional.lambda.L1#op(java.lang.Object)
1087 * @param param COMMENT
1091 public K2
op(final Entry
<K1
, K2
, V
> param
) {
1092 return param
.getKey2();
1096 private final L1
<V
, Entry
<K1
, K2
, V
>> getValue
= new L1
<V
, Entry
<K1
, K2
, V
>>() {
1098 * @see net.kezvh.functional.lambda.L1#op(java.lang.Object)
1099 * @param param COMMENT
1103 public V
op(final Entry
<K1
, K2
, V
> param
) {
1104 return param
.getValue();
1109 * @see net.kezvh.collections.Map2.Map2#entrySetOnKey2(java.lang.Object)
1110 * @param key2 COMMENT
1114 public Set
<Map2
.Entry
<K1
, K2
, V
>> entrySetOnKey2(final K2 key2
) {
1115 return new SetView
<Entry
<K1
, K2
, V
>, Map2
.Entry
<K1
, K2
, V
>>(this.k2Heads
.get(key2
), this.getBimapEntry
);
1119 * @see net.kezvh.collections.Map2.Map2#get(java.lang.Object, java.lang.Object)
1120 * @param key1 COMMENT
1121 * @param key2 COMMENT
1125 public V
get(final K1 key1
, final K2 key2
) {
1126 return this.getEntry(key1
, key2
).value
;
1130 * @see net.kezvh.collections.Map2.Map2#isEmpty()
1134 public boolean isEmpty() {
1135 return this.size
== 0;
1139 * @see net.kezvh.collections.Map2.Map2#keySetOnKey1(java.lang.Object)
1140 * @param key1 COMMENT
1144 public Set
<K2
> keySetOnKey1(final K1 key1
) {
1145 return new SetView
<Entry
<K1
, K2
, V
>, K2
>(this.k1Heads
.get(key1
), this.getKey2
);
1149 * @see net.kezvh.collections.Map2.Map2#keySetOnKey2(java.lang.Object)
1150 * @param key2 COMMENT
1154 public Set
<K1
> keySetOnKey2(final K2 key2
) {
1155 return new SetView
<Entry
<K1
, K2
, V
>, K1
>(this.k2Heads
.get(key2
), this.getKey1
);
1159 * @see net.kezvh.collections.Map2.Map2#put(java.lang.Object, java.lang.Object,
1161 * @param key1 COMMENT
1162 * @param key2 COMMENT
1163 * @param value COMMENT
1167 public V
put(final K1 key1
, final K2 key2
, final V value
) {
1168 final Entry
<K1
, K2
, V
> entry
= this.getEntry(key1
, key2
);
1170 return entry
.setValue(value
);
1172 this.addEntry(key1
, key2
, value
);
1176 private void addEntry(final K1 key1
, final K2 key2
, final V value
) {
1177 if (this.size
++ >= this.threshold
)
1178 this.resize(this.entries
.length
<< 1);
1179 final int hash
= HashMap2
.hash(key1
, key2
);
1180 final int index
= HashMap2
.indexFor(hash
, this.entries
.length
);
1181 final Entry
<K1
, K2
, V
> e
= this.entries
[index
];
1182 final Entry
<K1
, K2
, V
> newEntry
= new Entry
<K1
, K2
, V
>(hash
, key1
, key2
, value
, e
);
1183 this.entries
[index
] = newEntry
;
1184 HeadSet1 key1Head
= this.k1Heads
.get(key1
);
1185 if (key1Head
== null) {
1186 key1Head
= new HeadSet1();
1187 this.k1Heads
.put(key1
, key1Head
);
1190 HeadSet2 key2Head
= this.k2Heads
.get(key2
);
1191 if (key2Head
== null) {
1192 key2Head
= new HeadSet2();
1193 this.k2Heads
.put(key2
, key2Head
);
1196 key1Head
.insertNewHead(newEntry
);
1197 key2Head
.insertNewHead(newEntry
);
1200 private void resize(final int newSize
) {
1201 final Entry
<K1
, K2
, V
>[] newTable
= HashMap2
.createEntries(this.entries
.getClass().getComponentType(), newSize
);
1202 for (final Entry
<K1
, K2
, V
> entry
: this.entries
)
1204 newTable
[HashMap2
.indexFor(HashMap2
.hash(entry
), newSize
)] = entry
;
1208 * @see net.kezvh.collections.Map2.Map2#putAll(net.kezvh.collections.Map2.Map2)
1212 public void putAll(final Map2
<?
extends K1
, ?
extends K2
, ?
extends V
> m
) {
1213 if (this.size
+ m
.size() > this.threshold
) {
1214 final int newSize
= Integer
.highestOneBit(this.size
+ m
.size()) << 1;
1215 this.resize(newSize
);
1217 for (final Map
.Entry
<?
extends Pair
<?
extends K1
, ?
extends K2
>, ?
extends V
> o
: m
.entrySet())
1218 this.put(o
.getKey().get1(), o
.getKey().get2(), o
.getValue());
1222 * @see net.kezvh.collections.Map2.Map2#remove(java.lang.Object,
1224 * @param key1 COMMENT
1225 * @param key2 COMMENT
1229 public V
remove(final K1 key1
, final K2 key2
) {
1230 final Entry
<K1
, K2
, V
> e
= this.removeEntryForKey(key1
, key2
);
1231 return (e
== null ?
null : e
.value
);
1234 private Entry
<K1
, K2
, V
> removeEntryForKey(final K1 key1
, final K2 key2
) {
1235 final int hash
= HashMap2
.hash(key1
, key2
);
1236 final int index
= HashMap2
.indexFor(hash
, this.entries
.length
);
1238 Entry
<K1
, K2
, V
> prev
= this.entries
[index
];
1239 Entry
<K1
, K2
, V
> e
= prev
;
1242 final Entry
<K1
, K2
, V
> next
= e
.getNextCollision();
1243 if (e
.hash
== hash
&& e
.matchesKeys(key1
, key2
)) {
1247 this.entries
[index
] = next
;
1249 prev
.setNextCollision(next
);
1261 * @see net.kezvh.collections.Map2.Map2#size()
1270 * @see net.kezvh.collections.Map2.Map2#valuesOnKey1(java.lang.Object)
1271 * @param key1 COMMENT
1275 public Collection
<V
> valuesOnKey1(final K1 key1
) {
1276 return new SetView
<Entry
<K1
, K2
, V
>, V
>(this.k1Heads
.get(key1
), this.getValue
);
1280 * @see net.kezvh.collections.Map2.Map2#valuesOnKey2(java.lang.Object)
1281 * @param key2 COMMENT
1285 public Collection
<V
> valuesOnKey2(final K2 key2
) {
1286 return new SetView
<Entry
<K1
, K2
, V
>, V
>(this.k2Heads
.get(key2
), this.getValue
);
1290 * @see java.util.Map#containsKey(java.lang.Object)
1291 * @param key COMMENT
1294 @SuppressWarnings("unchecked")
1296 public boolean containsKey(final Object key
) {
1297 if (!(key
instanceof Pair
))
1300 final Pair
<K1
, K2
> keys
= (Pair
<K1
, K2
>) key
;
1301 return this.containsKey(keys
.get1(), keys
.get2());
1305 * @see java.util.Map#containsValue(java.lang.Object)
1306 * @param value COMMENT
1310 public boolean containsValue(final Object value
) {
1311 for (final Entry
<K1
, K2
, V
> entry
: this.entries
)
1312 if (entry
!= null && HashMap2
.objectsEqual(entry
.getValue(), value
))
1319 * @see java.util.Map#get(java.lang.Object)
1320 * @param key COMMENT
1323 @SuppressWarnings("unchecked")
1325 public V
get(final Object key
) {
1326 if (!(key
instanceof Pair
))
1329 final Pair
<K1
, K2
> keys
= (Pair
<K1
, K2
>) key
;
1330 return this.get(keys
.get1(), keys
.get2());
1334 * @see java.util.Map#put(java.lang.Object, java.lang.Object)
1335 * @param key COMMENT
1336 * @param value COMMENT
1340 public V
put(final Pair
<K1
, K2
> key
, final V value
) {
1341 return this.put(key
.get1(), key
.get2(), value
);
1345 * @param entry COMMENT
1348 public V
put(final Map2
.Entry
<K1
, K2
, V
> entry
) {
1349 return this.put(entry
.getKey1(), entry
.getKey2(), entry
.getValue());
1353 * @see java.util.Map#putAll(java.util.Map)
1357 public boolean putAll(final Collection
<?
extends Map2
.Entry
<K1
, K2
, V
>> c
) {
1358 if (this.size
+ c
.size() > this.threshold
) {
1359 final int newSize
= Integer
.highestOneBit(this.size
+ c
.size()) << 1;
1360 this.resize(newSize
);
1362 boolean changed
= false;
1363 for (final Map2
.Entry
<K1
, K2
, V
> o
: c
)
1364 changed
|= this.put(o
.getKey().get1(), o
.getKey().get2(), o
.getValue()) != null;
1369 * @see java.util.Map#putAll(java.util.Map)
1373 public void putAll(final Map
<?
extends Pair
<K1
, K2
>, ?
extends V
> t
) {
1374 if (this.size
+ t
.size() > this.threshold
) {
1375 final int newSize
= Integer
.highestOneBit(this.size
+ t
.size()) << 1;
1376 this.resize(newSize
);
1378 for (final Map
.Entry
<?
extends Pair
<?
extends K1
, ?
extends K2
>, ?
extends V
> o
: t
.entrySet())
1379 this.put(o
.getKey().get1(), o
.getKey().get2(), o
.getValue());
1383 * @see java.util.Map#remove(java.lang.Object)
1384 * @param key COMMENT
1387 @SuppressWarnings("unchecked")
1389 public V
remove(final Object key
) {
1390 if (!(key
instanceof Pair
))
1393 final Pair
<K1
, K2
> keys
= (Pair
<K1
, K2
>) key
;
1394 return this.remove(keys
.get1(), keys
.get2());
1397 private class EntryMap1
extends AbstractMap
<K2
, V
> {
1398 private final K1 key1
;
1400 private final Set
<Map
.Entry
<K2
, V
>> entryMapSet
;
1402 private final L1
<Map
.Entry
<K2
, V
>, HashMap2
.Entry
<K1
, K2
, V
>> forwardMap
= new L1
<Map
.Entry
<K2
, V
>, HashMap2
.Entry
<K1
, K2
, V
>>() {
1404 * @see net.kezvh.functional.lambda.L1#op(java.lang.Object)
1405 * @param param COMMENT
1409 public Map
.Entry
<K2
, V
> op(final HashMap2
.Entry
<K1
, K2
, V
> param
) {
1410 return new Map
.Entry
<K2
, V
>() {
1412 * @see java.util.Map.Entry#getKey()
1416 public K2
getKey() {
1417 return param
.get2();
1421 * @see java.util.Map.Entry#getValue()
1425 public V
getValue() {
1426 return param
.getValue();
1430 public V
setValue(final V value
) {
1431 return param
.setValue(value
);
1438 * @see java.util.AbstractMap#get(java.lang.Object)
1439 * @param key COMMENT
1442 @SuppressWarnings("unchecked")
1444 public V
get(final Object key
) {
1445 return HashMap2
.this.get(this.key1
, (K2
) key
);
1449 public V
put(final K2 key
, final V value
) {
1450 return HashMap2
.this.put(this.key1
, key
, value
);
1454 * @see java.util.AbstractMap#remove(java.lang.Object)
1455 * @param key COMMENT
1458 @SuppressWarnings("unchecked")
1460 public V
remove(final Object key
) {
1461 return HashMap2
.this.remove(this.key1
, (K2
) key
);
1464 public EntryMap1(final K1 key1
) {
1466 this.entryMapSet
= new SetView
<HashMap2
.Entry
<K1
, K2
, V
>, Map
.Entry
<K2
, V
>>(HashMap2
.this.k1Heads
.get(key1
), this.forwardMap
) {
1468 * @see java.util.AbstractCollection#add(java.lang.Object)
1473 public boolean add(final java
.util
.Map
.Entry
<K2
, V
> o
) {
1474 return HashMap2
.this.put(key1
, o
.getKey(), o
.getValue()) != null;
1478 * @see java.util.AbstractCollection#clear()
1481 public void clear() {
1482 HashMap2
.this.k1Heads
.get(key1
).clear();
1486 * @see java.util.AbstractCollection#contains(java.lang.Object)
1490 @SuppressWarnings("unchecked")
1492 public boolean contains(final Object o
) {
1493 if (!(o
instanceof Map
.Entry
))
1496 final Map
.Entry
<K2
, V
> other
= (Map
.Entry
<K2
, V
>) o
;
1497 return HashMap2
.this.containsKey(key1
, other
.getKey());
1501 * @see java.util.AbstractCollection#remove(java.lang.Object)
1505 @SuppressWarnings("unchecked")
1507 public boolean remove(final Object o
) {
1508 final Map
.Entry
<K2
, V
> other
= (Map
.Entry
<K2
, V
>) o
;
1509 return HashMap2
.this.remove(key1
, other
.getKey()) != null;
1515 * @see java.util.AbstractMap#entrySet()
1519 public Set
<java
.util
.Map
.Entry
<K2
, V
>> entrySet() {
1520 return this.entryMapSet
;
1525 * @see net.kezvh.collections.Map2.Map2#entryMapOnKey1(java.lang.Object)
1526 * @param key1 COMMENT
1530 public Map
<K2
, V
> entryMapOnKey1(final K1 key1
) {
1531 return new EntryMap1(key1
);
1534 private class EntryMap2
extends AbstractMap
<K1
, V
> {
1535 private final K2 key2
;
1536 private final Set
<Map
.Entry
<K1
, V
>> entryMapSet
;
1538 private final L1
<Map
.Entry
<K1
, V
>, HashMap2
.Entry
<K1
, K2
, V
>> forwardMap
= new L1
<Map
.Entry
<K1
, V
>, HashMap2
.Entry
<K1
, K2
, V
>>() {
1540 * @see net.kezvh.functional.lambda.L1#op(java.lang.Object)
1541 * @param param COMMENT
1545 public Map
.Entry
<K1
, V
> op(final HashMap2
.Entry
<K1
, K2
, V
> param
) {
1546 return new Map
.Entry
<K1
, V
>() {
1548 * @see java.util.Map.Entry#getKey()
1552 public K1
getKey() {
1553 return param
.get1();
1557 * @see java.util.Map.Entry#getValue()
1561 public V
getValue() {
1562 return param
.getValue();
1566 public V
setValue(final V value
) {
1567 return param
.setValue(value
);
1573 public EntryMap2(final K2 key2
) {
1575 this.entryMapSet
= new SetView
<HashMap2
.Entry
<K1
, K2
, V
>, Map
.Entry
<K1
, V
>>(HashMap2
.this.k1Heads
.get(key2
), this.forwardMap
) {
1577 * @see java.util.AbstractCollection#add(java.lang.Object)
1582 public boolean add(final java
.util
.Map
.Entry
<K1
, V
> o
) {
1583 return HashMap2
.this.put(o
.getKey(), key2
, o
.getValue()) != null;
1587 * @see java.util.AbstractCollection#clear()
1590 public void clear() {
1591 HashMap2
.this.k2Heads
.get(key2
).clear();
1595 * @see java.util.AbstractCollection#contains(java.lang.Object)
1599 @SuppressWarnings("unchecked")
1601 public boolean contains(final Object o
) {
1602 if (!(o
instanceof Map
.Entry
))
1605 final Map
.Entry
<K1
, V
> other
= (Map
.Entry
<K1
, V
>) o
;
1606 return HashMap2
.this.containsKey(other
.getKey(), key2
);
1610 * @see java.util.AbstractCollection#remove(java.lang.Object)
1614 @SuppressWarnings("unchecked")
1616 public boolean remove(final Object o
) {
1617 final Map
.Entry
<K1
, V
> other
= (Map
.Entry
<K1
, V
>) o
;
1618 return HashMap2
.this.remove(other
.getKey(), key2
) != null;
1624 * @see java.util.AbstractMap#entrySet()
1628 public Set
<java
.util
.Map
.Entry
<K1
, V
>> entrySet() {
1629 return this.entryMapSet
;
1633 * @see java.util.AbstractMap#get(java.lang.Object)
1634 * @param key COMMENT
1637 @SuppressWarnings("unchecked")
1639 public V
get(final Object key
) {
1640 return HashMap2
.this.get((K1
) key
, this.key2
);
1644 public V
put(final K1 key
, final V value
) {
1645 return HashMap2
.this.put(key
, this.key2
, value
);
1649 * @see java.util.AbstractMap#remove(java.lang.Object)
1650 * @param key COMMENT
1653 @SuppressWarnings("unchecked")
1655 public V
remove(final Object key
) {
1656 return HashMap2
.this.remove((K1
) key
, this.key2
);
1661 * @see net.kezvh.collections.Map2.Map2#entryMapOnKey2(java.lang.Object)
1662 * @param key2 COMMENT
1666 public Map
<K1
, V
> entryMapOnKey2(final K2 key2
) {
1667 return new EntryMap2(key2
);
1671 public Collection
<Map
.Entry
<K2
, V
>> get1(final K1 key1
) {
1672 return this.entryMapOnKey1(key1
).entrySet();
1676 public Collection
<Map
.Entry
<K1
, V
>> get2(final K2 key2
) {
1677 return this.entryMapOnKey2(key2
).entrySet();
1681 public Collection
<Map
.Entry
<K2
, V
>> remove1(final K1 key1
) {
1682 final Collection
<Map
.Entry
<K2
, V
>> removedEntries
= new ArrayList
<Map
.Entry
<K2
, V
>>(this.k1Heads
.get(key1
).size());
1684 for (final Entry
<K1
, K2
, V
> entry
: this.k1Heads
.get(key1
))
1685 removedEntries
.add(new MapEntryImpl
<K2
, V
>(entry
.get2(), entry
.getValue()));
1687 for (final Map
.Entry
<K2
, V
> removedEntry
: removedEntries
)
1688 this.remove(key1
, removedEntry
.getKey());
1690 return removedEntries
;
1694 public Collection
<Map
.Entry
<K1
, V
>> remove2(final K2 key2
) {
1695 final Collection
<Map
.Entry
<K1
, V
>> removedEntries
= new ArrayList
<Map
.Entry
<K1
, V
>>(this.k2Heads
.get(key2
).size());
1697 for (final Entry
<K1
, K2
, V
> entry
: this.k1Heads
.get(key2
))
1698 removedEntries
.add(new MapEntryImpl
<K1
, V
>(entry
.get1(), entry
.getValue()));
1700 for (final Map
.Entry
<K1
, V
> removedEntry
: removedEntries
)
1701 this.remove(removedEntry
.getKey(), key2
);
1703 return removedEntries
;