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