1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.collections4.map;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.io.Serializable;
23 import java.util.Map;
24 import java.util.Objects;
25
26 import org.apache.commons.collections4.MapIterator;
27 import org.apache.commons.collections4.keyvalue.MultiKey;
28 import org.apache.commons.collections4.map.AbstractHashedMap.HashEntry;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 public class MultiKeyMap<K, V> extends AbstractMapDecorator<MultiKey<? extends K>, V>
89 implements Serializable, Cloneable {
90
91
92 private static final long serialVersionUID = -1788199231038721040L;
93
94
95
96
97
98
99
100
101
102
103
104
105
106 public static <K, V> MultiKeyMap<K, V> multiKeyMap(final AbstractHashedMap<MultiKey<? extends K>, V> map) {
107 Objects.requireNonNull(map, "map");
108 if (map.isEmpty()) {
109 return new MultiKeyMap<>(map);
110 }
111 throw new IllegalArgumentException("Map must be empty");
112 }
113
114
115
116
117 public MultiKeyMap() {
118 this(new HashedMap<>());
119 }
120
121
122
123
124
125
126
127
128
129 protected MultiKeyMap(final AbstractHashedMap<MultiKey<? extends K>, V> map) {
130 super(map);
131 this.map = map;
132 }
133
134
135
136
137
138
139 protected void checkKey(final MultiKey<?> key) {
140 Objects.requireNonNull(key, "key");
141 }
142
143
144
145
146
147
148 @SuppressWarnings("unchecked")
149 @Override
150 public MultiKeyMap<K, V> clone() {
151 try {
152 return (MultiKeyMap<K, V>) super.clone();
153 } catch (final CloneNotSupportedException e) {
154 throw new UnsupportedOperationException(e);
155 }
156 }
157
158
159
160
161
162
163
164
165 public boolean containsKey(final Object key1, final Object key2) {
166 final int hashCode = hash(key1, key2);
167 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decoratedHashEntry(hashCode);
168 while (entry != null) {
169 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
170 return true;
171 }
172 entry = entry.next;
173 }
174 return false;
175 }
176
177
178
179
180
181
182
183
184
185 public boolean containsKey(final Object key1, final Object key2, final Object key3) {
186 final int hashCode = hash(key1, key2, key3);
187 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decoratedHashEntry(hashCode);
188 while (entry != null) {
189 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
190 return true;
191 }
192 entry = entry.next;
193 }
194 return false;
195 }
196
197
198
199
200
201
202
203
204
205
206 public boolean containsKey(final Object key1, final Object key2, final Object key3, final Object key4) {
207 final int hashCode = hash(key1, key2, key3, key4);
208 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decoratedHashEntry(hashCode);
209 while (entry != null) {
210 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
211 return true;
212 }
213 entry = entry.next;
214 }
215 return false;
216 }
217
218
219
220
221
222
223
224
225
226
227
228 public boolean containsKey(final Object key1, final Object key2, final Object key3, final Object key4, final Object key5) {
229 final int hashCode = hash(key1, key2, key3, key4, key5);
230 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decoratedHashEntry(hashCode);
231 while (entry != null) {
232 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
233 return true;
234 }
235 entry = entry.next;
236 }
237 return false;
238 }
239
240
241
242
243 @Override
244 protected AbstractHashedMap<MultiKey<? extends K>, V> decorated() {
245 return (AbstractHashedMap<MultiKey<? extends K>, V>) super.decorated();
246 }
247
248 HashEntry<MultiKey<? extends K>, V> decoratedHashEntry(final int hashCode) {
249 return decorated().data[decoratedHashIndex(hashCode)];
250 }
251
252 int decoratedHashIndex(final int hashCode) {
253 return decorated().hashIndex(hashCode, decorated().data.length);
254 }
255
256
257
258
259
260
261
262
263 public V get(final Object key1, final Object key2) {
264 final int hashCode = hash(key1, key2);
265 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decoratedHashEntry(hashCode);
266 while (entry != null) {
267 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
268 return entry.getValue();
269 }
270 entry = entry.next;
271 }
272 return null;
273 }
274
275
276
277
278
279
280
281
282
283 public V get(final Object key1, final Object key2, final Object key3) {
284 final int hashCode = hash(key1, key2, key3);
285 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decoratedHashEntry(hashCode);
286 while (entry != null) {
287 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
288 return entry.getValue();
289 }
290 entry = entry.next;
291 }
292 return null;
293 }
294
295
296
297
298
299
300
301
302
303
304 public V get(final Object key1, final Object key2, final Object key3, final Object key4) {
305 final int hashCode = hash(key1, key2, key3, key4);
306 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decoratedHashEntry(hashCode);
307 while (entry != null) {
308 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
309 return entry.getValue();
310 }
311 entry = entry.next;
312 }
313 return null;
314 }
315
316
317
318
319
320
321
322
323
324
325
326 public V get(final Object key1, final Object key2, final Object key3, final Object key4, final Object key5) {
327 final int hashCode = hash(key1, key2, key3, key4, key5);
328 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decoratedHashEntry(hashCode);
329 while (entry != null) {
330 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
331 return entry.getValue();
332 }
333 entry = entry.next;
334 }
335 return null;
336 }
337
338
339
340
341
342
343
344
345 protected int hash(final Object key1, final Object key2) {
346 int h = 0;
347 if (key1 != null) {
348 h ^= key1.hashCode();
349 }
350 if (key2 != null) {
351 h ^= key2.hashCode();
352 }
353 h += ~(h << 9);
354 h ^= h >>> 14;
355 h += h << 4;
356 h ^= h >>> 10;
357 return h;
358 }
359
360
361
362
363
364
365
366
367
368 protected int hash(final Object key1, final Object key2, final Object key3) {
369 int h = 0;
370 if (key1 != null) {
371 h ^= key1.hashCode();
372 }
373 if (key2 != null) {
374 h ^= key2.hashCode();
375 }
376 if (key3 != null) {
377 h ^= key3.hashCode();
378 }
379 h += ~(h << 9);
380 h ^= h >>> 14;
381 h += h << 4;
382 h ^= h >>> 10;
383 return h;
384 }
385
386
387
388
389
390
391
392
393
394
395 protected int hash(final Object key1, final Object key2, final Object key3, final Object key4) {
396 int h = 0;
397 if (key1 != null) {
398 h ^= key1.hashCode();
399 }
400 if (key2 != null) {
401 h ^= key2.hashCode();
402 }
403 if (key3 != null) {
404 h ^= key3.hashCode();
405 }
406 if (key4 != null) {
407 h ^= key4.hashCode();
408 }
409 h += ~(h << 9);
410 h ^= h >>> 14;
411 h += h << 4;
412 h ^= h >>> 10;
413 return h;
414 }
415
416
417
418
419
420
421
422
423
424
425
426 protected int hash(final Object key1, final Object key2, final Object key3, final Object key4, final Object key5) {
427 int h = 0;
428 if (key1 != null) {
429 h ^= key1.hashCode();
430 }
431 if (key2 != null) {
432 h ^= key2.hashCode();
433 }
434 if (key3 != null) {
435 h ^= key3.hashCode();
436 }
437 if (key4 != null) {
438 h ^= key4.hashCode();
439 }
440 if (key5 != null) {
441 h ^= key5.hashCode();
442 }
443 h += ~(h << 9);
444 h ^= h >>> 14;
445 h += h << 4;
446 h ^= h >>> 10;
447 return h;
448 }
449
450
451
452
453
454
455
456
457
458 protected boolean isEqualKey(final AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry,
459 final Object key1, final Object key2) {
460 final MultiKey<? extends K> multi = entry.getKey();
461 return
462 multi.size() == 2 &&
463 Objects.equals(key1, multi.getKey(0)) &&
464 Objects.equals(key2, multi.getKey(1));
465 }
466
467
468
469
470
471
472
473
474
475
476 protected boolean isEqualKey(final AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry,
477 final Object key1, final Object key2, final Object key3) {
478 final MultiKey<? extends K> multi = entry.getKey();
479 return
480 multi.size() == 3 &&
481 Objects.equals(key1, multi.getKey(0)) &&
482 Objects.equals(key2, multi.getKey(1)) &&
483 Objects.equals(key3, multi.getKey(2));
484 }
485
486
487
488
489
490
491
492
493
494
495
496 protected boolean isEqualKey(final AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry,
497 final Object key1, final Object key2, final Object key3, final Object key4) {
498 final MultiKey<? extends K> multi = entry.getKey();
499 return
500 multi.size() == 4 &&
501 Objects.equals(key1, multi.getKey(0)) &&
502 Objects.equals(key2, multi.getKey(1)) &&
503 Objects.equals(key3, multi.getKey(2)) &&
504 Objects.equals(key4, multi.getKey(3));
505 }
506
507
508
509
510
511
512
513
514
515
516
517
518 protected boolean isEqualKey(final AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry,
519 final Object key1, final Object key2, final Object key3, final Object key4, final Object key5) {
520 final MultiKey<? extends K> multi = entry.getKey();
521 return
522 multi.size() == 5 &&
523 Objects.equals(key1, multi.getKey(0)) &&
524 Objects.equals(key2, multi.getKey(1)) &&
525 Objects.equals(key3, multi.getKey(2)) &&
526 Objects.equals(key4, multi.getKey(3)) &&
527 Objects.equals(key5, multi.getKey(4));
528 }
529
530 @Override
531 public MapIterator<MultiKey<? extends K>, V> mapIterator() {
532 return decorated().mapIterator();
533 }
534
535
536
537
538
539
540
541
542
543
544
545
546 public V put(final K key1, final K key2, final K key3, final K key4, final K key5, final V value) {
547 final int hashCode = hash(key1, key2, key3, key4, key5);
548 final int index = decoratedHashIndex(hashCode);
549 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
550 while (entry != null) {
551 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
552 final V oldValue = entry.getValue();
553 decorated().updateEntry(entry, value);
554 return oldValue;
555 }
556 entry = entry.next;
557 }
558 decorated().addMapping(index, hashCode, new MultiKey<>(key1, key2, key3, key4, key5), value);
559 return null;
560 }
561
562
563
564
565
566
567
568
569
570
571
572 public V put(final K key1, final K key2, final K key3, final K key4, final V value) {
573 final int hashCode = hash(key1, key2, key3, key4);
574 final int index = decoratedHashIndex(hashCode);
575 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
576 while (entry != null) {
577 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
578 final V oldValue = entry.getValue();
579 decorated().updateEntry(entry, value);
580 return oldValue;
581 }
582 entry = entry.next;
583 }
584 decorated().addMapping(index, hashCode, new MultiKey<>(key1, key2, key3, key4), value);
585 return null;
586 }
587
588
589
590
591
592
593
594
595
596
597 public V put(final K key1, final K key2, final K key3, final V value) {
598 final int hashCode = hash(key1, key2, key3);
599 final int index = decoratedHashIndex(hashCode);
600 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
601 while (entry != null) {
602 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
603 final V oldValue = entry.getValue();
604 decorated().updateEntry(entry, value);
605 return oldValue;
606 }
607 entry = entry.next;
608 }
609 decorated().addMapping(index, hashCode, new MultiKey<>(key1, key2, key3), value);
610 return null;
611 }
612
613
614
615
616
617
618
619
620
621 public V put(final K key1, final K key2, final V value) {
622 final int hashCode = hash(key1, key2);
623 final int index = decoratedHashIndex(hashCode);
624 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
625 while (entry != null) {
626 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
627 final V oldValue = entry.getValue();
628 decorated().updateEntry(entry, value);
629 return oldValue;
630 }
631 entry = entry.next;
632 }
633 decorated().addMapping(index, hashCode, new MultiKey<>(key1, key2), value);
634 return null;
635 }
636
637
638
639
640
641
642
643
644
645
646
647 @Override
648 public V put(final MultiKey<? extends K> key, final V value) {
649 checkKey(key);
650 return super.put(key, value);
651 }
652
653
654
655
656
657
658
659
660
661 @Override
662 public void putAll(final Map<? extends MultiKey<? extends K>, ? extends V> mapToCopy) {
663 for (final MultiKey<? extends K> key : mapToCopy.keySet()) {
664 checkKey(key);
665 }
666 super.putAll(mapToCopy);
667 }
668
669
670
671
672
673
674
675
676 @SuppressWarnings("unchecked")
677 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
678 in.defaultReadObject();
679 map = (Map<MultiKey<? extends K>, V>) in.readObject();
680 }
681
682
683
684
685
686
687
688
689
690
691
692 public boolean removeAll(final Object key1) {
693 boolean modified = false;
694 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
695 while (it.hasNext()) {
696 final MultiKey<? extends K> multi = it.next();
697 if (multi.size() >= 1 &&
698 Objects.equals(key1, multi.getKey(0))) {
699 it.remove();
700 modified = true;
701 }
702 }
703 return modified;
704 }
705
706
707
708
709
710
711
712
713
714
715
716
717 public boolean removeAll(final Object key1, final Object key2) {
718 boolean modified = false;
719 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
720 while (it.hasNext()) {
721 final MultiKey<? extends K> multi = it.next();
722 if (multi.size() >= 2 &&
723 Objects.equals(key1, multi.getKey(0)) &&
724 Objects.equals(key2, multi.getKey(1))) {
725 it.remove();
726 modified = true;
727 }
728 }
729 return modified;
730 }
731
732
733
734
735
736
737
738
739
740
741
742
743
744 public boolean removeAll(final Object key1, final Object key2, final Object key3) {
745 boolean modified = false;
746 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
747 while (it.hasNext()) {
748 final MultiKey<? extends K> multi = it.next();
749 if (multi.size() >= 3 &&
750 Objects.equals(key1, multi.getKey(0)) &&
751 Objects.equals(key2, multi.getKey(1)) &&
752 Objects.equals(key3, multi.getKey(2))) {
753 it.remove();
754 modified = true;
755 }
756 }
757 return modified;
758 }
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773 public boolean removeAll(final Object key1, final Object key2, final Object key3, final Object key4) {
774 boolean modified = false;
775 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
776 while (it.hasNext()) {
777 final MultiKey<? extends K> multi = it.next();
778 if (multi.size() >= 4 &&
779 Objects.equals(key1, multi.getKey(0)) &&
780 Objects.equals(key2, multi.getKey(1)) &&
781 Objects.equals(key3, multi.getKey(2)) &&
782 Objects.equals(key4, multi.getKey(3))) {
783 it.remove();
784 modified = true;
785 }
786 }
787 return modified;
788 }
789
790
791
792
793
794
795
796
797
798 public V removeMultiKey(final Object key1, final Object key2) {
799 final int hashCode = hash(key1, key2);
800 final int index = decoratedHashIndex(hashCode);
801 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
802 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
803 while (entry != null) {
804 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
805 final V oldValue = entry.getValue();
806 decorated().removeMapping(entry, index, previous);
807 return oldValue;
808 }
809 previous = entry;
810 entry = entry.next;
811 }
812 return null;
813 }
814
815
816
817
818
819
820
821
822
823
824 public V removeMultiKey(final Object key1, final Object key2, final Object key3) {
825 final int hashCode = hash(key1, key2, key3);
826 final int index = decoratedHashIndex(hashCode);
827 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
828 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
829 while (entry != null) {
830 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
831 final V oldValue = entry.getValue();
832 decorated().removeMapping(entry, index, previous);
833 return oldValue;
834 }
835 previous = entry;
836 entry = entry.next;
837 }
838 return null;
839 }
840
841
842
843
844
845
846
847
848
849
850
851 public V removeMultiKey(final Object key1, final Object key2, final Object key3, final Object key4) {
852 final int hashCode = hash(key1, key2, key3, key4);
853 final int index = decoratedHashIndex(hashCode);
854 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
855 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
856 while (entry != null) {
857 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
858 final V oldValue = entry.getValue();
859 decorated().removeMapping(entry, index, previous);
860 return oldValue;
861 }
862 previous = entry;
863 entry = entry.next;
864 }
865 return null;
866 }
867
868
869
870
871
872
873
874
875
876
877
878
879 public V removeMultiKey(final Object key1, final Object key2, final Object key3,
880 final Object key4, final Object key5) {
881 final int hashCode = hash(key1, key2, key3, key4, key5);
882 final int index = decoratedHashIndex(hashCode);
883 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
884 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
885 while (entry != null) {
886 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
887 final V oldValue = entry.getValue();
888 decorated().removeMapping(entry, index, previous);
889 return oldValue;
890 }
891 previous = entry;
892 entry = entry.next;
893 }
894 return null;
895 }
896
897
898
899
900
901
902
903 private void writeObject(final ObjectOutputStream out) throws IOException {
904 out.defaultWriteObject();
905 out.writeObject(map);
906 }
907
908 }