1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.util.collection;
17
18 import static io.netty5.util.internal.MathUtil.safeFindNextPositivePowerOfTwo;
19
20 import java.util.AbstractCollection;
21 import java.util.AbstractSet;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.NoSuchElementException;
27 import java.util.Set;
28
29
30
31
32
33
34
35
36
37 public class LongObjectHashMap<V> implements LongObjectMap<V> {
38
39
40 public static final int DEFAULT_CAPACITY = 8;
41
42
43 public static final float DEFAULT_LOAD_FACTOR = 0.5f;
44
45
46
47
48
49 private static final Object NULL_VALUE = new Object();
50
51
52 private int maxSize;
53
54
55 private final float loadFactor;
56
57 private long[] keys;
58 private V[] values;
59 private int size;
60 private int mask;
61
62 private final Set<Long> keySet = new KeySet();
63 private final Set<Entry<Long, V>> entrySet = new EntrySet();
64 private final Iterable<PrimitiveEntry<V>> entries = new Iterable<PrimitiveEntry<V>>() {
65 @Override
66 public Iterator<PrimitiveEntry<V>> iterator() {
67 return new PrimitiveIterator();
68 }
69 };
70
71 public LongObjectHashMap() {
72 this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
73 }
74
75 public LongObjectHashMap(int initialCapacity) {
76 this(initialCapacity, DEFAULT_LOAD_FACTOR);
77 }
78
79 public LongObjectHashMap(int initialCapacity, float loadFactor) {
80 if (loadFactor <= 0.0f || loadFactor > 1.0f) {
81
82
83 throw new IllegalArgumentException("loadFactor must be > 0 and <= 1");
84 }
85
86 this.loadFactor = loadFactor;
87
88
89 int capacity = safeFindNextPositivePowerOfTwo(initialCapacity);
90 mask = capacity - 1;
91
92
93 keys = new long[capacity];
94 @SuppressWarnings({ "unchecked", "SuspiciousArrayCast" })
95 V[] temp = (V[]) new Object[capacity];
96 values = temp;
97
98
99 maxSize = calcMaxSize(capacity);
100 }
101
102 private static <T> T toExternal(T value) {
103 assert value != null : "null is not a legitimate internal value. Concurrent Modification?";
104 return value == NULL_VALUE ? null : value;
105 }
106
107 @SuppressWarnings("unchecked")
108 private static <T> T toInternal(T value) {
109 return value == null ? (T) NULL_VALUE : value;
110 }
111
112 @Override
113 public V get(long key) {
114 int index = indexOf(key);
115 return index == -1 ? null : toExternal(values[index]);
116 }
117
118 @Override
119 public V put(long key, V value) {
120 int startIndex = hashIndex(key);
121 int index = startIndex;
122
123 for (;;) {
124 if (values[index] == null) {
125
126 keys[index] = key;
127 values[index] = toInternal(value);
128 growSize();
129 return null;
130 }
131 if (keys[index] == key) {
132
133 V previousValue = values[index];
134 values[index] = toInternal(value);
135 return toExternal(previousValue);
136 }
137
138
139 if ((index = probeNext(index)) == startIndex) {
140
141 throw new IllegalStateException("Unable to insert");
142 }
143 }
144 }
145
146 @Override
147 public void putAll(Map<? extends Long, ? extends V> sourceMap) {
148 if (sourceMap instanceof LongObjectHashMap) {
149
150 @SuppressWarnings("unchecked")
151 LongObjectHashMap<V> source = (LongObjectHashMap<V>) sourceMap;
152 for (int i = 0; i < source.values.length; ++i) {
153 V sourceValue = source.values[i];
154 if (sourceValue != null) {
155 put(source.keys[i], sourceValue);
156 }
157 }
158 return;
159 }
160
161
162 for (Entry<? extends Long, ? extends V> entry : sourceMap.entrySet()) {
163 put(entry.getKey(), entry.getValue());
164 }
165 }
166
167 @Override
168 public V remove(long key) {
169 int index = indexOf(key);
170 if (index == -1) {
171 return null;
172 }
173
174 V prev = values[index];
175 removeAt(index);
176 return toExternal(prev);
177 }
178
179 @Override
180 public int size() {
181 return size;
182 }
183
184 @Override
185 public boolean isEmpty() {
186 return size == 0;
187 }
188
189 @Override
190 public void clear() {
191 Arrays.fill(keys, (long) 0);
192 Arrays.fill(values, null);
193 size = 0;
194 }
195
196 @Override
197 public boolean containsKey(long key) {
198 return indexOf(key) >= 0;
199 }
200
201 @Override
202 public boolean containsValue(Object value) {
203 @SuppressWarnings("unchecked")
204 V v1 = toInternal((V) value);
205 for (V v2 : values) {
206
207 if (v2 != null && v2.equals(v1)) {
208 return true;
209 }
210 }
211 return false;
212 }
213
214 @Override
215 public Iterable<PrimitiveEntry<V>> entries() {
216 return entries;
217 }
218
219 @Override
220 public Collection<V> values() {
221 return new AbstractCollection<V>() {
222 @Override
223 public Iterator<V> iterator() {
224 return new Iterator<V>() {
225 final PrimitiveIterator iter = new PrimitiveIterator();
226
227 @Override
228 public boolean hasNext() {
229 return iter.hasNext();
230 }
231
232 @Override
233 public V next() {
234 return iter.next().value();
235 }
236
237 @Override
238 public void remove() {
239 iter.remove();
240 }
241 };
242 }
243
244 @Override
245 public int size() {
246 return size;
247 }
248 };
249 }
250
251 @Override
252 public int hashCode() {
253
254
255
256 int hash = size;
257 for (long key : keys) {
258
259
260
261
262
263
264
265 hash ^= hashCode(key);
266 }
267 return hash;
268 }
269
270 @Override
271 public boolean equals(Object obj) {
272 if (this == obj) {
273 return true;
274 }
275 if (!(obj instanceof LongObjectMap)) {
276 return false;
277 }
278 @SuppressWarnings("rawtypes")
279 LongObjectMap other = (LongObjectMap) obj;
280 if (size != other.size()) {
281 return false;
282 }
283 for (int i = 0; i < values.length; ++i) {
284 V value = values[i];
285 if (value != null) {
286 long key = keys[i];
287 Object otherValue = other.get(key);
288 if (value == NULL_VALUE) {
289 if (otherValue != null) {
290 return false;
291 }
292 } else if (!value.equals(otherValue)) {
293 return false;
294 }
295 }
296 }
297 return true;
298 }
299
300 @Override
301 public boolean containsKey(Object key) {
302 return containsKey(objectToKey(key));
303 }
304
305 @Override
306 public V get(Object key) {
307 return get(objectToKey(key));
308 }
309
310 @Override
311 public V put(Long key, V value) {
312 return put(objectToKey(key), value);
313 }
314
315 @Override
316 public V remove(Object key) {
317 return remove(objectToKey(key));
318 }
319
320 @Override
321 public Set<Long> keySet() {
322 return keySet;
323 }
324
325 @Override
326 public Set<Entry<Long, V>> entrySet() {
327 return entrySet;
328 }
329
330 private long objectToKey(Object key) {
331 return (long) ((Long) key).longValue();
332 }
333
334
335
336
337
338
339
340 private int indexOf(long key) {
341 int startIndex = hashIndex(key);
342 int index = startIndex;
343
344 for (;;) {
345 if (values[index] == null) {
346
347 return -1;
348 }
349 if (key == keys[index]) {
350 return index;
351 }
352
353
354 if ((index = probeNext(index)) == startIndex) {
355 return -1;
356 }
357 }
358 }
359
360
361
362
363 private int hashIndex(long key) {
364
365 return hashCode(key) & mask;
366 }
367
368
369
370
371 private static int hashCode(long key) {
372 return (int) (key ^ (key >>> 32));
373 }
374
375
376
377
378 private int probeNext(int index) {
379
380 return (index + 1) & mask;
381 }
382
383
384
385
386 private void growSize() {
387 size++;
388
389 if (size > maxSize) {
390 if(keys.length == Integer.MAX_VALUE) {
391 throw new IllegalStateException("Max capacity reached at size=" + size);
392 }
393
394
395 rehash(keys.length << 1);
396 }
397 }
398
399
400
401
402
403
404
405
406 private boolean removeAt(final int index) {
407 --size;
408
409
410 keys[index] = 0;
411 values[index] = null;
412
413
414
415
416
417
418 int nextFree = index;
419 int i = probeNext(index);
420 for (V value = values[i]; value != null; value = values[i = probeNext(i)]) {
421 long key = keys[i];
422 int bucket = hashIndex(key);
423 if (i < bucket && (bucket <= nextFree || nextFree <= i) ||
424 bucket <= nextFree && nextFree <= i) {
425
426 keys[nextFree] = key;
427 values[nextFree] = value;
428
429 keys[i] = 0;
430 values[i] = null;
431 nextFree = i;
432 }
433 }
434 return nextFree != index;
435 }
436
437
438
439
440 private int calcMaxSize(int capacity) {
441
442 int upperBound = capacity - 1;
443 return Math.min(upperBound, (int) (capacity * loadFactor));
444 }
445
446
447
448
449
450
451 private void rehash(int newCapacity) {
452 long[] oldKeys = keys;
453 V[] oldVals = values;
454
455 keys = new long[newCapacity];
456 @SuppressWarnings({ "unchecked", "SuspiciousArrayCast" })
457 V[] temp = (V[]) new Object[newCapacity];
458 values = temp;
459
460 maxSize = calcMaxSize(newCapacity);
461 mask = newCapacity - 1;
462
463
464 for (int i = 0; i < oldVals.length; ++i) {
465 V oldVal = oldVals[i];
466 if (oldVal != null) {
467
468
469 long oldKey = oldKeys[i];
470 int index = hashIndex(oldKey);
471
472 for (;;) {
473 if (values[index] == null) {
474 keys[index] = oldKey;
475 values[index] = oldVal;
476 break;
477 }
478
479
480 index = probeNext(index);
481 }
482 }
483 }
484 }
485
486 @Override
487 public String toString() {
488 if (isEmpty()) {
489 return "{}";
490 }
491 StringBuilder sb = new StringBuilder(4 * size);
492 sb.append('{');
493 boolean first = true;
494 for (int i = 0; i < values.length; ++i) {
495 V value = values[i];
496 if (value != null) {
497 if (!first) {
498 sb.append(", ");
499 }
500 sb.append(keyToString(keys[i])).append('=').append(value == this ? "(this Map)" :
501 toExternal(value));
502 first = false;
503 }
504 }
505 return sb.append('}').toString();
506 }
507
508
509
510
511
512 protected String keyToString(long key) {
513 return Long.toString(key);
514 }
515
516
517
518
519 private final class EntrySet extends AbstractSet<Entry<Long, V>> {
520 @Override
521 public Iterator<Entry<Long, V>> iterator() {
522 return new MapIterator();
523 }
524
525 @Override
526 public int size() {
527 return LongObjectHashMap.this.size();
528 }
529 }
530
531
532
533
534 private final class KeySet extends AbstractSet<Long> {
535 @Override
536 public int size() {
537 return LongObjectHashMap.this.size();
538 }
539
540 @Override
541 public boolean contains(Object o) {
542 return LongObjectHashMap.this.containsKey(o);
543 }
544
545 @Override
546 public boolean remove(Object o) {
547 return LongObjectHashMap.this.remove(o) != null;
548 }
549
550 @Override
551 public boolean retainAll(Collection<?> retainedKeys) {
552 boolean changed = false;
553 for(Iterator<PrimitiveEntry<V>> iter = entries().iterator(); iter.hasNext(); ) {
554 PrimitiveEntry<V> entry = iter.next();
555 if (!retainedKeys.contains(entry.key())) {
556 changed = true;
557 iter.remove();
558 }
559 }
560 return changed;
561 }
562
563 @Override
564 public void clear() {
565 LongObjectHashMap.this.clear();
566 }
567
568 @Override
569 public Iterator<Long> iterator() {
570 return new Iterator<Long>() {
571 private final Iterator<Entry<Long, V>> iter = entrySet.iterator();
572
573 @Override
574 public boolean hasNext() {
575 return iter.hasNext();
576 }
577
578 @Override
579 public Long next() {
580 return iter.next().getKey();
581 }
582
583 @Override
584 public void remove() {
585 iter.remove();
586 }
587 };
588 }
589 }
590
591
592
593
594 private final class PrimitiveIterator implements Iterator<PrimitiveEntry<V>>, PrimitiveEntry<V> {
595 private int prevIndex = -1;
596 private int nextIndex = -1;
597 private int entryIndex = -1;
598
599 private void scanNext() {
600 while (++nextIndex != values.length && values[nextIndex] == null) {
601 }
602 }
603
604 @Override
605 public boolean hasNext() {
606 if (nextIndex == -1) {
607 scanNext();
608 }
609 return nextIndex != values.length;
610 }
611
612 @Override
613 public PrimitiveEntry<V> next() {
614 if (!hasNext()) {
615 throw new NoSuchElementException();
616 }
617
618 prevIndex = nextIndex;
619 scanNext();
620
621
622 entryIndex = prevIndex;
623 return this;
624 }
625
626 @Override
627 public void remove() {
628 if (prevIndex == -1) {
629 throw new IllegalStateException("next must be called before each remove.");
630 }
631 if (removeAt(prevIndex)) {
632
633
634
635 nextIndex = prevIndex;
636 }
637 prevIndex = -1;
638 }
639
640
641
642
643 @Override
644 public long key() {
645 return keys[entryIndex];
646 }
647
648 @Override
649 public V value() {
650 return toExternal(values[entryIndex]);
651 }
652
653 @Override
654 public void setValue(V value) {
655 values[entryIndex] = toInternal(value);
656 }
657 }
658
659
660
661
662 private final class MapIterator implements Iterator<Entry<Long, V>> {
663 private final PrimitiveIterator iter = new PrimitiveIterator();
664
665 @Override
666 public boolean hasNext() {
667 return iter.hasNext();
668 }
669
670 @Override
671 public Entry<Long, V> next() {
672 if (!hasNext()) {
673 throw new NoSuchElementException();
674 }
675
676 iter.next();
677
678 return new MapEntry(iter.entryIndex);
679 }
680
681 @Override
682 public void remove() {
683 iter.remove();
684 }
685 }
686
687
688
689
690 final class MapEntry implements Entry<Long, V> {
691 private final int entryIndex;
692
693 MapEntry(int entryIndex) {
694 this.entryIndex = entryIndex;
695 }
696
697 @Override
698 public Long getKey() {
699 verifyExists();
700 return keys[entryIndex];
701 }
702
703 @Override
704 public V getValue() {
705 verifyExists();
706 return toExternal(values[entryIndex]);
707 }
708
709 @Override
710 public V setValue(V value) {
711 verifyExists();
712 V prevValue = toExternal(values[entryIndex]);
713 values[entryIndex] = toInternal(value);
714 return prevValue;
715 }
716
717 private void verifyExists() {
718 if (values[entryIndex] == null) {
719 throw new IllegalStateException("The map entry has been removed");
720 }
721 }
722 }
723 }