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.TernaryKey;
29  import org.dishevelled.multimap.TernaryKeyMap;
30  
31  /**
32   * Hashed implementation of TernaryKeyMap.
33   *
34   * @param <K1> first key type
35   * @param <K2> second key type
36   * @param <K3> third key type
37   * @param <V> value type
38   * @author  Michael Heuer
39   */
40  public final class HashedTernaryKeyMap<K1, K2, K3, V>
41      extends AbstractHashedMap<TernaryKey<K1, K2, K3>, V>
42      implements TernaryKeyMap<K1, K2, K3, V>
43  {
44  
45      /**
46       * Create a new empty HashedTernaryKeyMap.
47       */
48      public HashedTernaryKeyMap()
49      {
50          super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
51      }
52  
53      /**
54       * Create a new empty HashedTernaryKeyMap with the specified
55       * initial capacity, load factor, and threshold.
56       *
57       * @param initialCapacity initial capacity
58       * @param loadFactor load factor
59       * @param threshold threshold
60       */
61      public HashedTernaryKeyMap(final int initialCapacity,
62                                  final float loadFactor,
63                                  final int threshold)
64      {
65          super(initialCapacity, loadFactor, threshold);
66      }
67  
68      /**
69       * Create a new HashedTernaryKeyMap with the same mappings as
70       * the specified map (copy constructor).
71       *
72       * @param map map, must not be null
73       */
74      public HashedTernaryKeyMap(final Map<? extends TernaryKey<K1, K2, K3>, ? extends V> map)
75      {
76          super(map);
77      }
78  
79  
80      /**
81       * Hash the specified keys.
82       *
83       * @param key1 first key
84       * @param key2 second key
85       * @param key3 third key
86       * @return hash code for the specified keys
87       */
88      private int hash(final K1 key1, final K2 key2, final K3 key3)
89      {
90          int h = 0;
91          if (key1 != null)
92          {
93              h ^= key1.hashCode();
94          }
95          if (key2 != null)
96          {
97              h ^= key2.hashCode();
98          }
99          if (key3 != null)
100         {
101             h ^= key3.hashCode();
102         }
103         h += ~(h << 9);
104         h ^=  (h >>> 14);
105         h +=  (h << 4);
106         h ^=  (h >>> 10);
107         return h;
108     }
109 
110     /**
111      * Return true if the keys in the specified entry are equal to the specified keys.
112      *
113      * @param entry entry
114      * @param key1 first key
115      * @param key2 second key
116      * @param key3 third key
117      * @return true if the keys in the specified entry are equal to the specified keys
118      */
119     private boolean isEqualKey(final HashEntry<TernaryKey<K1, K2, K3>, V> entry,
120                                final K1 key1,
121                                final K2 key2,
122                                final K3 key3)
123     {
124         TernaryKey<K1, K2, K3> key = entry.getKey();
125         return (key1 == null ? key.getFirstKey() == null : key1.equals(key.getFirstKey()))
126             && (key2 == null ? key.getSecondKey() == null : key2.equals(key.getSecondKey()))
127             && (key3 == null ? key.getThirdKey() == null : key3.equals(key.getThirdKey()));
128     }
129 
130     @Override
131     public boolean containsKey(final K1 key1, final K2 key2, final K3 key3)
132     {
133         int hashCode = hash(key1, key2, key3);
134         HashEntry<TernaryKey<K1, K2, K3>, V> entry = data[hashIndex(hashCode, data.length)];
135         while (entry != null)
136         {
137             if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3))
138             {
139                 return true;
140             }
141             entry = entry.next;
142         }
143         return false;
144     }
145 
146     @Override
147     public V get(final K1 key1, final K2 key2, final K3 key3)
148     {
149         int hashCode = hash(key1, key2, key3);
150         HashEntry<TernaryKey<K1, K2, K3>, V> entry = data[hashIndex(hashCode, data.length)];
151         while (entry != null)
152         {
153             if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3))
154             {
155                 return entry.getValue();
156             }
157             entry = entry.next;
158         }
159         return null;
160     }
161 
162     @Override
163     public V put(final K1 key1, final K2 key2, final K3 key3, final V value)
164     {
165         int hashCode = hash(key1, key2, key3);
166         int index = hashIndex(hashCode, data.length);
167         HashEntry<TernaryKey<K1, K2, K3>, V> entry = data[index];
168         while (entry != null)
169         {
170             if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3))
171             {
172                 V oldValue = entry.getValue();
173                 updateEntry(entry, value);
174                 return oldValue;
175             }
176             entry = entry.next;
177         }
178         addMapping(index, hashCode, new TernaryKey<K1, K2, K3>(key1, key2, key3), value);
179         return null;
180     }
181 
182     @Override
183     public V put(final TernaryKey<K1, K2, K3> key, final V value)
184     {
185         if (key == null)
186         {
187             throw new NullPointerException("key must not be null");
188         }
189         return super.put(key, value);
190     }
191 
192     @Override
193     public V removeKey(final K1 key1, final K2 key2, final K3 key3)
194     {
195         int hashCode = hash(key1, key2, key3);
196         int index = hashIndex(hashCode, data.length);
197         HashEntry<TernaryKey<K1, K2, K3>, V> entry = data[index];
198         HashEntry<TernaryKey<K1, K2, K3>, V> previous = null;
199         while (entry != null)
200         {
201             if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3))
202             {
203                 V oldValue = entry.getValue();
204                 removeMapping(entry, index, previous);
205                 return oldValue;
206             }
207             previous = entry;
208             entry = entry.next;
209         }
210         return null;
211     }
212 }