View Javadoc
1   /*
2   
3       dsh-multi-map  Multi-key map interfaces and implementation.
4       Copyright (c) 2007-2016 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package org.dishevelled.multimap.impl;
25  
26  import java.util.Map;
27  
28  import org.dishevelled.multimap.BinaryKey;
29  import org.dishevelled.multimap.BinaryKeyMap;
30  
31  /**
32   * Hashed implementation of BinaryKeyMap.
33   *
34   * @param <K1> first key type
35   * @param <K2> second key type
36   * @param <V> value type
37   * @author  Michael Heuer
38   */
39  public final class HashedBinaryKeyMap<K1, K2, V>
40      extends AbstractHashedMap<BinaryKey<K1, K2>, V>
41      implements BinaryKeyMap<K1, K2, V>
42  {
43  
44      /**
45       * Create a new empty HashedBinaryKeyMap.
46       */
47      public HashedBinaryKeyMap()
48      {
49          super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
50      }
51  
52      /**
53       * Create a new empty HashedBinaryKeyMap with the specified
54       * initial capacity, load factor, and threshold.
55       *
56       * @param initialCapacity initial capacity
57       * @param loadFactor load factor
58       * @param threshold threshold
59       */
60      public HashedBinaryKeyMap(final int initialCapacity,
61                                final float loadFactor,
62                                final int threshold)
63      {
64          super(initialCapacity, loadFactor, threshold);
65      }
66  
67      /**
68       * Create a new HashedBinaryKeyMap with the same mappings as
69       * the specified map (copy constructor).
70       *
71       * @param map map, must not be null
72       */
73      public HashedBinaryKeyMap(final Map<? extends BinaryKey<K1, K2>, ? extends V> map)
74      {
75          super(map);
76      }
77  
78  
79      /**
80       * Hash the specified keys.
81       *
82       * @param key1 first key
83       * @param key2 second key
84       * @return hash code for the specified keys
85       */
86      private int hash(final K1 key1, final K2 key2)
87      {
88          int h = 0;
89          if (key1 != null)
90          {
91              h ^= key1.hashCode();
92          }
93          if (key2 != null)
94          {
95              h ^= key2.hashCode();
96          }
97          h += ~(h << 9);
98          h ^=  (h >>> 14);
99          h +=  (h << 4);
100         h ^=  (h >>> 10);
101         return h;
102     }
103 
104     /**
105      * Return true if the keys in the specified entry are equal to the specified keys.
106      *
107      * @param entry entry
108      * @param key1 first key
109      * @param key2 second key
110      * @return true if the keys in the specified entry are equal to the specified keys
111      */
112     private boolean isEqualKey(final HashEntry<BinaryKey<K1, K2>, V> entry, final K1 key1, final K2 key2)
113     {
114         BinaryKey<K1, K2> key = entry.getKey();
115         return (key1 == null ? key.getFirstKey() == null : key1.equals(key.getFirstKey()))
116             && (key2 == null ? key.getSecondKey() == null : key2.equals(key.getSecondKey()));
117     }
118 
119     @Override
120     public boolean containsKey(final K1 key1, final K2 key2)
121     {
122         int hashCode = hash(key1, key2);
123         HashEntry<BinaryKey<K1, K2>, V> entry = data[hashIndex(hashCode, data.length)];
124         while (entry != null)
125         {
126             if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2))
127             {
128                 return true;
129             }
130             entry = entry.next;
131         }
132         return false;
133     }
134 
135     @Override
136     public V get(final K1 key1, final K2 key2)
137     {
138         int hashCode = hash(key1, key2);
139         HashEntry<BinaryKey<K1, K2>, V> entry = data[hashIndex(hashCode, data.length)];
140         while (entry != null)
141         {
142             if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2))
143             {
144                 return entry.getValue();
145             }
146             entry = entry.next;
147         }
148         return null;
149     }
150 
151     @Override
152     public V put(final K1 key1, final K2 key2, final V value)
153     {
154         int hashCode = hash(key1, key2);
155         int index = hashIndex(hashCode, data.length);
156         HashEntry<BinaryKey<K1, K2>, V> entry = data[index];
157         while (entry != null)
158         {
159             if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2))
160             {
161                 V oldValue = entry.getValue();
162                 updateEntry(entry, value);
163                 return oldValue;
164             }
165             entry = entry.next;
166         }
167         addMapping(index, hashCode, new BinaryKey<K1, K2>(key1, key2), value);
168         return null;
169     }
170 
171     @Override
172     public V put(final BinaryKey<K1, K2> key, final V value)
173     {
174         if (key == null)
175         {
176             throw new NullPointerException("key must not be null");
177         }
178         return super.put(key, value);
179     }
180 
181     @Override
182     public V removeKey(final K1 key1, final K2 key2)
183     {
184         int hashCode = hash(key1, key2);
185         int index = hashIndex(hashCode, data.length);
186         HashEntry<BinaryKey<K1, K2>, V> entry = data[index];
187         HashEntry<BinaryKey<K1, K2>, V> previous = null;
188         while (entry != null)
189         {
190             if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2))
191             {
192                 V oldValue = entry.getValue();
193                 removeMapping(entry, index, previous);
194                 return oldValue;
195             }
196             previous = entry;
197             entry = entry.next;
198         }
199         return null;
200     }
201 }