1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.util.collection;
17
18 import java.lang.reflect.Array;
19 import java.util.AbstractCollection;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Iterator;
23 import java.util.NoSuchElementException;
24
25
26
27
28
29
30
31
32
33 public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectMap.Entry<V>> {
34
35
36 private static final int DEFAULT_CAPACITY = 11;
37
38
39 private static final float DEFAULT_LOAD_FACTOR = 0.5f;
40
41
42
43
44
45 private static final Object NULL_VALUE = new Object();
46
47
48 private int maxSize;
49
50
51 private final float loadFactor;
52
53 private int[] keys;
54 private V[] values;
55 private Collection<V> valueCollection;
56 private int size;
57
58 public IntObjectHashMap() {
59 this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
60 }
61
62 public IntObjectHashMap(int initialCapacity) {
63 this(initialCapacity, DEFAULT_LOAD_FACTOR);
64 }
65
66 public IntObjectHashMap(int initialCapacity, float loadFactor) {
67 if (initialCapacity < 1) {
68 throw new IllegalArgumentException("initialCapacity must be >= 1");
69 }
70
71 if (loadFactor <= 0.0f || loadFactor > 1.0f) {
72
73
74 throw new IllegalArgumentException("loadFactor must be > 0 and <= 1");
75 }
76
77 this.loadFactor = loadFactor;
78
79
80 int capacity = adjustCapacity(initialCapacity);
81
82
83 keys = new int[capacity];
84 @SuppressWarnings({ "unchecked", "SuspiciousArrayCast" })
85 V[] temp = (V[]) new Object[capacity];
86 values = temp;
87
88
89 maxSize = calcMaxSize(capacity);
90 }
91
92 private static <T> T toExternal(T value) {
93 return value == NULL_VALUE ? null : value;
94 }
95
96 @SuppressWarnings("unchecked")
97 private static <T> T toInternal(T value) {
98 return value == null ? (T) NULL_VALUE : value;
99 }
100
101 @Override
102 public V get(int key) {
103 int index = indexOf(key);
104 return index == -1 ? null : toExternal(values[index]);
105 }
106
107 @Override
108 public V put(int key, V value) {
109 int startIndex = hashIndex(key);
110 int index = startIndex;
111
112 for (;;) {
113 if (values[index] == null) {
114
115 keys[index] = key;
116 values[index] = toInternal(value);
117 growSize();
118 return null;
119 }
120 if (keys[index] == key) {
121
122 V previousValue = values[index];
123 values[index] = toInternal(value);
124 return toExternal(previousValue);
125 }
126
127
128 if ((index = probeNext(index)) == startIndex) {
129
130 throw new IllegalStateException("Unable to insert");
131 }
132 }
133 }
134
135 private int probeNext(int index) {
136 return index == values.length - 1 ? 0 : index + 1;
137 }
138
139 @Override
140 public void putAll(IntObjectMap<V> sourceMap) {
141 if (sourceMap instanceof IntObjectHashMap) {
142
143 IntObjectHashMap<V> source = (IntObjectHashMap<V>) sourceMap;
144 for (int i = 0; i < source.values.length; ++i) {
145 V sourceValue = source.values[i];
146 if (sourceValue != null) {
147 put(source.keys[i], sourceValue);
148 }
149 }
150 return;
151 }
152
153
154 for (Entry<V> entry : sourceMap.entries()) {
155 put(entry.key(), entry.value());
156 }
157 }
158
159 @Override
160 public V remove(int key) {
161 int index = indexOf(key);
162 if (index == -1) {
163 return null;
164 }
165
166 V prev = values[index];
167 removeAt(index);
168 return toExternal(prev);
169 }
170
171 @Override
172 public int size() {
173 return size;
174 }
175
176 @Override
177 public boolean isEmpty() {
178 return size == 0;
179 }
180
181 @Override
182 public void clear() {
183 Arrays.fill(keys, 0);
184 Arrays.fill(values, null);
185 size = 0;
186 }
187
188 @Override
189 public boolean containsKey(int key) {
190 return indexOf(key) >= 0;
191 }
192
193 @Override
194 public boolean containsValue(V value) {
195 V v1 = toInternal(value);
196 for (V v2 : values) {
197
198 if (v2 != null && v2.equals(v1)) {
199 return true;
200 }
201 }
202 return false;
203 }
204
205 @Override
206 public Iterable<Entry<V>> entries() {
207 return this;
208 }
209
210 @Override
211 public Iterator<Entry<V>> iterator() {
212 return new IteratorImpl();
213 }
214
215 @Override
216 public int[] keys() {
217 int[] outKeys = new int[size()];
218 int targetIx = 0;
219 for (int i = 0; i < values.length; ++i) {
220 if (values[i] != null) {
221 outKeys[targetIx++] = keys[i];
222 }
223 }
224 return outKeys;
225 }
226
227 @Override
228 public V[] values(Class<V> clazz) {
229 @SuppressWarnings("unchecked")
230 V[] outValues = (V[]) Array.newInstance(clazz, size());
231 int targetIx = 0;
232 for (V value : values) {
233 if (value != null) {
234 outValues[targetIx++] = value;
235 }
236 }
237 return outValues;
238 }
239
240 @Override
241 public Collection<V> values() {
242 Collection<V> valueCollection = this.valueCollection;
243 if (valueCollection == null) {
244 this.valueCollection = valueCollection = new AbstractCollection<V>() {
245 @Override
246 public Iterator<V> iterator() {
247 return new Iterator<V>() {
248 final Iterator<Entry<V>> iter = IntObjectHashMap.this.iterator();
249 @Override
250 public boolean hasNext() {
251 return iter.hasNext();
252 }
253
254 @Override
255 public V next() {
256 return iter.next().value();
257 }
258
259 @Override
260 public void remove() {
261 throw new UnsupportedOperationException();
262 }
263 };
264 }
265
266 @Override
267 public int size() {
268 return size;
269 }
270 };
271 }
272
273 return valueCollection;
274 }
275
276 @Override
277 public int hashCode() {
278
279
280
281 int hash = size;
282 for (int key : keys) {
283
284
285
286
287
288
289
290 hash ^= key;
291 }
292 return hash;
293 }
294
295 @Override
296 public boolean equals(Object obj) {
297 if (this == obj) {
298 return true;
299 }
300 if (!(obj instanceof IntObjectMap)) {
301 return false;
302 }
303 @SuppressWarnings("rawtypes")
304 IntObjectMap other = (IntObjectMap) obj;
305 if (size != other.size()) {
306 return false;
307 }
308 for (int i = 0; i < values.length; ++i) {
309 V value = values[i];
310 if (value != null) {
311 int key = keys[i];
312 Object otherValue = other.get(key);
313 if (value == NULL_VALUE) {
314 if (otherValue != null) {
315 return false;
316 }
317 } else if (!value.equals(otherValue)) {
318 return false;
319 }
320 }
321 }
322 return true;
323 }
324
325
326
327
328
329
330
331 private int indexOf(int key) {
332 int startIndex = hashIndex(key);
333 int index = startIndex;
334
335 for (;;) {
336 if (values[index] == null) {
337
338 return -1;
339 }
340 if (key == keys[index]) {
341 return index;
342 }
343
344
345 if ((index = probeNext(index)) == startIndex) {
346 return -1;
347 }
348 }
349 }
350
351
352
353
354 private int hashIndex(int key) {
355
356 return (key % keys.length + keys.length) % keys.length;
357 }
358
359
360
361
362 private void growSize() {
363 size++;
364
365 if (size > maxSize) {
366
367
368 rehash(adjustCapacity((int) Math.min(keys.length * 2.0, Integer.MAX_VALUE - 8)));
369 } else if (size == keys.length) {
370
371
372 rehash(keys.length);
373 }
374 }
375
376
377
378
379 private static int adjustCapacity(int capacity) {
380 return capacity | 1;
381 }
382
383
384
385
386
387
388
389 private void removeAt(int index) {
390 --size;
391
392
393 keys[index] = 0;
394 values[index] = null;
395
396
397
398
399
400
401 int nextFree = index;
402 for (int i = probeNext(index); values[i] != null; i = probeNext(i)) {
403 int bucket = hashIndex(keys[i]);
404 if (i < bucket && (bucket <= nextFree || nextFree <= i) ||
405 bucket <= nextFree && nextFree <= i) {
406
407 keys[nextFree] = keys[i];
408 values[nextFree] = values[i];
409
410 keys[i] = 0;
411 values[i] = null;
412 nextFree = i;
413 }
414 }
415 }
416
417
418
419
420 private int calcMaxSize(int capacity) {
421
422 int upperBound = capacity - 1;
423 return Math.min(upperBound, (int) (capacity * loadFactor));
424 }
425
426
427
428
429
430
431 private void rehash(int newCapacity) {
432 int[] oldKeys = keys;
433 V[] oldVals = values;
434
435 keys = new int[newCapacity];
436 @SuppressWarnings({ "unchecked", "SuspiciousArrayCast" })
437 V[] temp = (V[]) new Object[newCapacity];
438 values = temp;
439
440 maxSize = calcMaxSize(newCapacity);
441
442
443 for (int i = 0; i < oldVals.length; ++i) {
444 V oldVal = oldVals[i];
445 if (oldVal != null) {
446
447
448 int oldKey = oldKeys[i];
449 int index = hashIndex(oldKey);
450
451 for (;;) {
452 if (values[index] == null) {
453 keys[index] = oldKey;
454 values[index] = toInternal(oldVal);
455 break;
456 }
457
458
459 index = probeNext(index);
460 }
461 }
462 }
463 }
464
465
466
467
468 private final class IteratorImpl implements Iterator<Entry<V>>, Entry<V> {
469 private int prevIndex = -1;
470 private int nextIndex = -1;
471 private int entryIndex = -1;
472
473 private void scanNext() {
474 for (;;) {
475 if (++nextIndex == values.length || values[nextIndex] != null) {
476 break;
477 }
478 }
479 }
480
481 @Override
482 public boolean hasNext() {
483 if (nextIndex == -1) {
484 scanNext();
485 }
486 return nextIndex < keys.length;
487 }
488
489 @Override
490 public Entry<V> next() {
491 if (!hasNext()) {
492 throw new NoSuchElementException();
493 }
494
495 prevIndex = nextIndex;
496 scanNext();
497
498
499 entryIndex = prevIndex;
500 return this;
501 }
502
503 @Override
504 public void remove() {
505 if (prevIndex < 0) {
506 throw new IllegalStateException("next must be called before each remove.");
507 }
508 removeAt(prevIndex);
509 prevIndex = -1;
510 }
511
512
513
514
515 @Override
516 public int key() {
517 return keys[entryIndex];
518 }
519
520 @Override
521 public V value() {
522 return toExternal(values[entryIndex]);
523 }
524
525 @Override
526 public void setValue(V value) {
527 values[entryIndex] = toInternal(value);
528 }
529 }
530
531 @Override
532 public String toString() {
533 if (size == 0) {
534 return "{}";
535 }
536 StringBuilder sb = new StringBuilder(4 * size);
537 for (int i = 0; i < values.length; ++i) {
538 V value = values[i];
539 if (value != null) {
540 sb.append(sb.length() == 0 ? "{" : ", ");
541 sb.append(keyToString(keys[i])).append('=').append(value == this ? "(this Map)" : value);
542 }
543 }
544 return sb.append('}').toString();
545 }
546
547
548
549
550 protected String keyToString(int key) {
551 return Integer.toString(key);
552 }
553 }