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
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 public class MultiKeyMap<K, V> extends AbstractMapDecorator<MultiKey<? extends K>, V>
88 implements Serializable, Cloneable {
89
90
91 private static final long serialVersionUID = -1788199231038721040L;
92
93
94
95
96
97
98
99
100
101
102
103
104
105 public static <K, V> MultiKeyMap<K, V> multiKeyMap(final AbstractHashedMap<MultiKey<? extends K>, V> map) {
106 Objects.requireNonNull(map, "map");
107 if (map.isEmpty()) {
108 return new MultiKeyMap<>(map);
109 }
110 throw new IllegalArgumentException("Map must be empty");
111 }
112
113
114
115
116 public MultiKeyMap() {
117 this(new HashedMap<>());
118 }
119
120
121
122
123
124
125
126
127
128 protected MultiKeyMap(final AbstractHashedMap<MultiKey<? extends K>, V> map) {
129 super(map);
130 this.map = map;
131 }
132
133
134
135
136
137
138 protected void checkKey(final MultiKey<?> key) {
139 Objects.requireNonNull(key, "key");
140 }
141
142
143
144
145
146
147 @SuppressWarnings("unchecked")
148 @Override
149 public MultiKeyMap<K, V> clone() {
150 try {
151 return (MultiKeyMap<K, V>) super.clone();
152 } catch (final CloneNotSupportedException e) {
153 throw new UnsupportedOperationException(e);
154 }
155 }
156
157
158
159
160
161
162
163
164 public boolean containsKey(final Object key1, final Object key2) {
165 final int hashCode = hash(key1, key2);
166 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
167 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
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 =
188 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
189 while (entry != null) {
190 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
191 return true;
192 }
193 entry = entry.next;
194 }
195 return false;
196 }
197
198
199
200
201
202
203
204
205
206
207 public boolean containsKey(final Object key1, final Object key2, final Object key3, final Object key4) {
208 final int hashCode = hash(key1, key2, key3, key4);
209 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
210 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
211 while (entry != null) {
212 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
213 return true;
214 }
215 entry = entry.next;
216 }
217 return false;
218 }
219
220
221
222
223
224
225
226
227
228
229
230 public boolean containsKey(final Object key1, final Object key2, final Object key3,
231 final Object key4, final Object key5) {
232 final int hashCode = hash(key1, key2, key3, key4, key5);
233 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
234 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
235 while (entry != null) {
236 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
237 return true;
238 }
239 entry = entry.next;
240 }
241 return false;
242 }
243
244
245
246
247 @Override
248 protected AbstractHashedMap<MultiKey<? extends K>, V> decorated() {
249 return (AbstractHashedMap<MultiKey<? extends K>, V>) super.decorated();
250 }
251
252
253
254
255
256
257
258
259 public V get(final Object key1, final Object key2) {
260 final int hashCode = hash(key1, key2);
261 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
262 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
263 while (entry != null) {
264 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
265 return entry.getValue();
266 }
267 entry = entry.next;
268 }
269 return null;
270 }
271
272
273
274
275
276
277
278
279
280 public V get(final Object key1, final Object key2, final Object key3) {
281 final int hashCode = hash(key1, key2, key3);
282 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
283 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
284 while (entry != null) {
285 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
286 return entry.getValue();
287 }
288 entry = entry.next;
289 }
290 return null;
291 }
292
293
294
295
296
297
298
299
300
301
302 public V get(final Object key1, final Object key2, final Object key3, final Object key4) {
303 final int hashCode = hash(key1, key2, key3, key4);
304 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
305 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
306 while (entry != null) {
307 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
308 return entry.getValue();
309 }
310 entry = entry.next;
311 }
312 return null;
313 }
314
315
316
317
318
319
320
321
322
323
324
325 public V get(final Object key1, final Object key2, final Object key3, final Object key4, final Object key5) {
326 final int hashCode = hash(key1, key2, key3, key4, key5);
327 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
328 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
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 (key1 == multi.getKey(0) || key1 != null && key1.equals(multi.getKey(0))) &&
464 (key2 == multi.getKey(1) || key2 != null && key2.equals(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 (key1 == multi.getKey(0) || key1 != null && key1.equals(multi.getKey(0))) &&
482 (key2 == multi.getKey(1) || key2 != null && key2.equals(multi.getKey(1))) &&
483 (key3 == multi.getKey(2) || key3 != null && key3.equals(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 (key1 == multi.getKey(0) || key1 != null && key1.equals(multi.getKey(0))) &&
502 (key2 == multi.getKey(1) || key2 != null && key2.equals(multi.getKey(1))) &&
503 (key3 == multi.getKey(2) || key3 != null && key3.equals(multi.getKey(2))) &&
504 (key4 == multi.getKey(3) || key4 != null && key4.equals(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 (key1 == multi.getKey(0) || key1 != null && key1.equals(multi.getKey(0))) &&
524 (key2 == multi.getKey(1) || key2 != null && key2.equals(multi.getKey(1))) &&
525 (key3 == multi.getKey(2) || key3 != null && key3.equals(multi.getKey(2))) &&
526 (key4 == multi.getKey(3) || key4 != null && key4.equals(multi.getKey(3))) &&
527 (key5 == multi.getKey(4) || key5 != null && key5.equals(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 = decorated().hashIndex(hashCode, decorated().data.length);
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 = decorated().hashIndex(hashCode, decorated().data.length);
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 = decorated().hashIndex(hashCode, decorated().data.length);
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 = decorated().hashIndex(hashCode, decorated().data.length);
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 public boolean removeAll(final Object key1) {
692 boolean modified = false;
693 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
694 while (it.hasNext()) {
695 final MultiKey<? extends K> multi = it.next();
696 if (multi.size() >= 1 &&
697 (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0)))) {
698 it.remove();
699 modified = true;
700 }
701 }
702 return modified;
703 }
704
705
706
707
708
709
710
711
712
713
714
715 public boolean removeAll(final Object key1, final Object key2) {
716 boolean modified = false;
717 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
718 while (it.hasNext()) {
719 final MultiKey<? extends K> multi = it.next();
720 if (multi.size() >= 2 &&
721 (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0))) &&
722 (key2 == null ? multi.getKey(1) == null : key2.equals(multi.getKey(1)))) {
723 it.remove();
724 modified = true;
725 }
726 }
727 return modified;
728 }
729
730
731
732
733
734
735
736
737
738
739
740
741 public boolean removeAll(final Object key1, final Object key2, final Object key3) {
742 boolean modified = false;
743 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
744 while (it.hasNext()) {
745 final MultiKey<? extends K> multi = it.next();
746 if (multi.size() >= 3 &&
747 (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0))) &&
748 (key2 == null ? multi.getKey(1) == null : key2.equals(multi.getKey(1))) &&
749 (key3 == null ? multi.getKey(2) == null : key3.equals(multi.getKey(2)))) {
750 it.remove();
751 modified = true;
752 }
753 }
754 return modified;
755 }
756
757
758
759
760
761
762
763
764
765
766
767
768
769 public boolean removeAll(final Object key1, final Object key2, final Object key3, final Object key4) {
770 boolean modified = false;
771 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
772 while (it.hasNext()) {
773 final MultiKey<? extends K> multi = it.next();
774 if (multi.size() >= 4 &&
775 (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0))) &&
776 (key2 == null ? multi.getKey(1) == null : key2.equals(multi.getKey(1))) &&
777 (key3 == null ? multi.getKey(2) == null : key3.equals(multi.getKey(2))) &&
778 (key4 == null ? multi.getKey(3) == null : key4.equals(multi.getKey(3)))) {
779 it.remove();
780 modified = true;
781 }
782 }
783 return modified;
784 }
785
786
787
788
789
790
791
792
793
794 public V removeMultiKey(final Object key1, final Object key2) {
795 final int hashCode = hash(key1, key2);
796 final int index = decorated().hashIndex(hashCode, decorated().data.length);
797 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
798 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
799 while (entry != null) {
800 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
801 final V oldValue = entry.getValue();
802 decorated().removeMapping(entry, index, previous);
803 return oldValue;
804 }
805 previous = entry;
806 entry = entry.next;
807 }
808 return null;
809 }
810
811
812
813
814
815
816
817
818
819
820 public V removeMultiKey(final Object key1, final Object key2, final Object key3) {
821 final int hashCode = hash(key1, key2, key3);
822 final int index = decorated().hashIndex(hashCode, decorated().data.length);
823 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
824 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
825 while (entry != null) {
826 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
827 final V oldValue = entry.getValue();
828 decorated().removeMapping(entry, index, previous);
829 return oldValue;
830 }
831 previous = entry;
832 entry = entry.next;
833 }
834 return null;
835 }
836
837
838
839
840
841
842
843
844
845
846
847 public V removeMultiKey(final Object key1, final Object key2, final Object key3, final Object key4) {
848 final int hashCode = hash(key1, key2, key3, key4);
849 final int index = decorated().hashIndex(hashCode, decorated().data.length);
850 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
851 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
852 while (entry != null) {
853 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
854 final V oldValue = entry.getValue();
855 decorated().removeMapping(entry, index, previous);
856 return oldValue;
857 }
858 previous = entry;
859 entry = entry.next;
860 }
861 return null;
862 }
863
864
865
866
867
868
869
870
871
872
873
874
875 public V removeMultiKey(final Object key1, final Object key2, final Object key3,
876 final Object key4, final Object key5) {
877 final int hashCode = hash(key1, key2, key3, key4, key5);
878 final int index = decorated().hashIndex(hashCode, decorated().data.length);
879 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
880 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
881 while (entry != null) {
882 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
883 final V oldValue = entry.getValue();
884 decorated().removeMapping(entry, index, previous);
885 return oldValue;
886 }
887 previous = entry;
888 entry = entry.next;
889 }
890 return null;
891 }
892
893
894
895
896
897
898
899 private void writeObject(final ObjectOutputStream out) throws IOException {
900 out.defaultWriteObject();
901 out.writeObject(map);
902 }
903
904 }