42 * Container class for immutable collections. Not part of the public API.
43 * Mainly for namespace management and shared infrastructure.
44 *
45 * Serial warnings are suppressed throughout because all implementation
46 * classes use a serial proxy and thus have no need to declare serialVersionUID.
47 */
48 @SuppressWarnings("serial")
49 class ImmutableCollections {
50 /**
51 * A "salt" value used for randomizing iteration order. This is initialized once
52 * and stays constant for the lifetime of the JVM. It need not be truly random, but
53 * it needs to vary sufficiently from one run to the next so that iteration order
54 * will vary between JVM runs.
55 */
56 static final int SALT;
57 static {
58 long nt = System.nanoTime();
59 SALT = (int)((nt >>> 32) ^ nt);
60 }
61
62 /** No instances. */
63 private ImmutableCollections() { }
64
65 /**
66 * The reciprocal of load factor. Given a number of elements
67 * to store, multiply by this factor to get the table size.
68 */
69 static final int EXPAND_FACTOR = 2;
70
71 static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
72
73 // ---------- List Implementations ----------
74
75 abstract static class AbstractImmutableList<E> extends AbstractList<E>
76 implements RandomAccess, Serializable {
77 @Override public boolean add(E e) { throw uoe(); }
78 @Override public boolean addAll(Collection<? extends E> c) { throw uoe(); }
79 @Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); }
80 @Override public void clear() { throw uoe(); }
81 @Override public boolean remove(Object o) { throw uoe(); }
82 @Override public boolean removeAll(Collection<?> c) { throw uoe(); }
83 @Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); }
84 @Override public void replaceAll(UnaryOperator<E> operator) { throw uoe(); }
85 @Override public boolean retainAll(Collection<?> c) { throw uoe(); }
86 @Override public void sort(Comparator<? super E> c) { throw uoe(); }
87 }
88
89 static final class List0<E> extends AbstractImmutableList<E> {
90 private static final List0<?> INSTANCE = new List0<>();
91
92 @SuppressWarnings("unchecked")
93 static <T> List0<T> instance() {
94 return (List0<T>) INSTANCE;
95 }
96
97 private List0() { }
98
99 @Override
100 public int size() {
101 return 0;
102 }
103
104 @Override
105 public E get(int index) {
106 Objects.checkIndex(index, 0); // always throws IndexOutOfBoundsException
107 return null; // but the compiler doesn't know this
108 }
109
110 @Override
111 public Iterator<E> iterator() {
112 return Collections.emptyIterator();
113 }
114
115 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
116 throw new InvalidObjectException("not serial proxy");
117 }
118
119 private Object writeReplace() {
120 return new CollSer(CollSer.IMM_LIST);
121 }
122
123 @Override
124 public boolean contains(Object o) {
125 Objects.requireNonNull(o);
126 return false;
127 }
128
129 @Override
130 public boolean containsAll(Collection<?> o) {
131 return o.isEmpty(); // implicit nullcheck of o
132 }
133
134 @Override
135 public int hashCode() {
136 return 1;
137 }
138 }
139
140 static final class List1<E> extends AbstractImmutableList<E> {
141 @Stable
142 private final E e0;
143
144 List1(E e0) {
145 this.e0 = Objects.requireNonNull(e0);
146 }
147
148 @Override
149 public int size() {
150 return 1;
151 }
152
153 @Override
154 public E get(int index) {
155 Objects.checkIndex(index, 1);
156 return e0;
157 }
158
159 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
160 throw new InvalidObjectException("not serial proxy");
161 }
162
163 private Object writeReplace() {
164 return new CollSer(CollSer.IMM_LIST, e0);
165 }
166
167 @Override
168 public boolean contains(Object o) {
169 return o.equals(e0); // implicit nullcheck of o
170 }
171
172 @Override
173 public int hashCode() {
174 return 31 + e0.hashCode();
175 }
176 }
177
178 static final class List2<E> extends AbstractImmutableList<E> {
179 @Stable
180 private final E e0;
181 @Stable
182 private final E e1;
183
184 List2(E e0, E e1) {
185 this.e0 = Objects.requireNonNull(e0);
186 this.e1 = Objects.requireNonNull(e1);
187 }
188
189 @Override
190 public int size() {
191 return 2;
192 }
193
194 @Override
195 public E get(int index) {
196 Objects.checkIndex(index, 2);
197 if (index == 0) {
198 return e0;
199 } else { // index == 1
200 return e1;
201 }
202 }
203
204 @Override
205 public boolean contains(Object o) {
206 return o.equals(e0) || o.equals(e1); // implicit nullcheck of o
207 }
208
209 @Override
210 public int hashCode() {
211 int hash = 31 + e0.hashCode();
212 return 31 * hash + e1.hashCode();
213 }
214
215 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
216 throw new InvalidObjectException("not serial proxy");
217 }
218
219 private Object writeReplace() {
220 return new CollSer(CollSer.IMM_LIST, e0, e1);
221 }
222 }
223
224 static final class ListN<E> extends AbstractImmutableList<E> {
225 @Stable
226 private final E[] elements;
227
228 @SafeVarargs
229 ListN(E... input) {
230 // copy and check manually to avoid TOCTOU
231 @SuppressWarnings("unchecked")
232 E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
233 for (int i = 0; i < input.length; i++) {
234 tmp[i] = Objects.requireNonNull(input[i]);
235 }
236 this.elements = tmp;
237 }
238
239 @Override
240 public int size() {
241 return elements.length;
242 }
243
270 throw new InvalidObjectException("not serial proxy");
271 }
272
273 private Object writeReplace() {
274 return new CollSer(CollSer.IMM_LIST, elements);
275 }
276 }
277
278 // ---------- Set Implementations ----------
279
280 abstract static class AbstractImmutableSet<E> extends AbstractSet<E> implements Serializable {
281 @Override public boolean add(E e) { throw uoe(); }
282 @Override public boolean addAll(Collection<? extends E> c) { throw uoe(); }
283 @Override public void clear() { throw uoe(); }
284 @Override public boolean remove(Object o) { throw uoe(); }
285 @Override public boolean removeAll(Collection<?> c) { throw uoe(); }
286 @Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); }
287 @Override public boolean retainAll(Collection<?> c) { throw uoe(); }
288 }
289
290 static final class Set0<E> extends AbstractImmutableSet<E> {
291 private static final Set0<?> INSTANCE = new Set0<>();
292
293 @SuppressWarnings("unchecked")
294 static <T> Set0<T> instance() {
295 return (Set0<T>) INSTANCE;
296 }
297
298 private Set0() { }
299
300 @Override
301 public int size() {
302 return 0;
303 }
304
305 @Override
306 public boolean contains(Object o) {
307 Objects.requireNonNull(o);
308 return false;
309 }
310
311 @Override
312 public boolean containsAll(Collection<?> o) {
313 return o.isEmpty(); // implicit nullcheck of o
314 }
315
316 @Override
317 public Iterator<E> iterator() {
318 return Collections.emptyIterator();
319 }
320
321 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
322 throw new InvalidObjectException("not serial proxy");
323 }
324
325 private Object writeReplace() {
326 return new CollSer(CollSer.IMM_SET);
327 }
328
329 @Override
330 public int hashCode() {
331 return 0;
332 }
333 }
334
335 static final class Set1<E> extends AbstractImmutableSet<E> {
336 @Stable
337 private final E e0;
338
339 Set1(E e0) {
340 this.e0 = Objects.requireNonNull(e0);
341 }
342
343 @Override
344 public int size() {
345 return 1;
346 }
347
348 @Override
349 public boolean contains(Object o) {
350 return o.equals(e0); // implicit nullcheck of o
351 }
352
353 @Override
354 public Iterator<E> iterator() {
355 return Collections.singletonIterator(e0);
356 }
357
358 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
359 throw new InvalidObjectException("not serial proxy");
360 }
361
362 private Object writeReplace() {
363 return new CollSer(CollSer.IMM_SET, e0);
364 }
365
366 @Override
367 public int hashCode() {
368 return e0.hashCode();
369 }
370 }
371
372 static final class Set2<E> extends AbstractImmutableSet<E> {
373 @Stable
374 final E e0;
375 @Stable
376 final E e1;
377
378 Set2(E e0, E e1) {
379 if (e0.equals(Objects.requireNonNull(e1))) { // implicit nullcheck of e0
380 throw new IllegalArgumentException("duplicate element: " + e0);
381 }
382
383 if (SALT >= 0) {
384 this.e0 = e0;
385 this.e1 = e1;
386 } else {
387 this.e0 = e1;
388 this.e1 = e0;
389 }
390 }
391
392 @Override
393 public int size() {
394 return 2;
395 }
396
397 @Override
398 public boolean contains(Object o) {
399 return o.equals(e0) || o.equals(e1); // implicit nullcheck of o
400 }
401
402 @Override
403 public int hashCode() {
404 return e0.hashCode() + e1.hashCode();
405 }
406
407 @Override
408 public Iterator<E> iterator() {
409 return new Iterator<E>() {
410 private int idx = 0;
411
412 @Override
413 public boolean hasNext() {
414 return idx < 2;
415 }
416
417 @Override
418 public E next() {
419 if (idx == 0) {
420 idx = 1;
421 return e0;
422 } else if (idx == 1) {
423 idx = 2;
424 return e1;
425 } else {
426 throw new NoSuchElementException();
427 }
428 }
429 };
430 }
431
432 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
433 throw new InvalidObjectException("not serial proxy");
434 }
435
436 private Object writeReplace() {
437 return new CollSer(CollSer.IMM_SET, e0, e1);
438 }
439 }
440
441 /**
442 * An array-based Set implementation. The element array must be strictly
443 * larger than the size (the number of contained elements) so that at
444 * least one null is always present.
445 * @param <E> the element type
446 */
447 static final class SetN<E> extends AbstractImmutableSet<E> {
448 @Stable
449 final E[] elements;
450 @Stable
451 final int size;
452
453 @SafeVarargs
454 @SuppressWarnings("unchecked")
455 SetN(E... input) {
456 size = input.length; // implicit nullcheck of input
457
458 elements = (E[])new Object[EXPAND_FACTOR * input.length];
459 for (int i = 0; i < input.length; i++) {
460 E e = input[i];
461 int idx = probe(e); // implicit nullcheck of e
462 if (idx >= 0) {
463 throw new IllegalArgumentException("duplicate element: " + e);
464 } else {
465 elements[-(idx + 1)] = e;
466 }
467 }
468 }
469
470 @Override
471 public int size() {
472 return size;
473 }
474
475 @Override
476 public boolean contains(Object o) {
477 return probe(o) >= 0; // implicit nullcheck of o
478 }
479
480 @Override
481 public Iterator<E> iterator() {
482 return new Iterator<E>() {
483 private int idx = 0;
484
485 @Override
486 public boolean hasNext() {
487 while (idx < elements.length) {
488 if (elements[idx] != null)
489 return true;
490 idx++;
491 }
492 return false;
493 }
494
495 @Override
496 public E next() {
497 if (! hasNext()) {
548 }
549
550 // ---------- Map Implementations ----------
551
552 abstract static class AbstractImmutableMap<K,V> extends AbstractMap<K,V> implements Serializable {
553 @Override public void clear() { throw uoe(); }
554 @Override public V compute(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); }
555 @Override public V computeIfAbsent(K key, Function<? super K,? extends V> mf) { throw uoe(); }
556 @Override public V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); }
557 @Override public V merge(K key, V value, BiFunction<? super V,? super V,? extends V> rf) { throw uoe(); }
558 @Override public V put(K key, V value) { throw uoe(); }
559 @Override public void putAll(Map<? extends K,? extends V> m) { throw uoe(); }
560 @Override public V putIfAbsent(K key, V value) { throw uoe(); }
561 @Override public V remove(Object key) { throw uoe(); }
562 @Override public boolean remove(Object key, Object value) { throw uoe(); }
563 @Override public V replace(K key, V value) { throw uoe(); }
564 @Override public boolean replace(K key, V oldValue, V newValue) { throw uoe(); }
565 @Override public void replaceAll(BiFunction<? super K,? super V,? extends V> f) { throw uoe(); }
566 }
567
568 static final class Map0<K,V> extends AbstractImmutableMap<K,V> {
569 private static final Map0<?,?> INSTANCE = new Map0<>();
570
571 @SuppressWarnings("unchecked")
572 static <K,V> Map0<K,V> instance() {
573 return (Map0<K,V>) INSTANCE;
574 }
575
576 private Map0() { }
577
578 @Override
579 public Set<Map.Entry<K,V>> entrySet() {
580 return Set.of();
581 }
582
583 @Override
584 public boolean containsKey(Object o) {
585 Objects.requireNonNull(o);
586 return false;
587 }
588
589 @Override
590 public boolean containsValue(Object o) {
591 Objects.requireNonNull(o);
592 return false;
593 }
594
595 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
596 throw new InvalidObjectException("not serial proxy");
597 }
598
599 private Object writeReplace() {
600 return new CollSer(CollSer.IMM_MAP);
601 }
602
603 @Override
604 public int hashCode() {
605 return 0;
606 }
607 }
608
609 static final class Map1<K,V> extends AbstractImmutableMap<K,V> {
610 @Stable
611 private final K k0;
612 @Stable
613 private final V v0;
614
615 Map1(K k0, V v0) {
616 this.k0 = Objects.requireNonNull(k0);
617 this.v0 = Objects.requireNonNull(v0);
618 }
619
620 @Override
621 public Set<Map.Entry<K,V>> entrySet() {
622 return Set.of(new KeyValueHolder<>(k0, v0));
623 }
624
625 @Override
626 public boolean containsKey(Object o) {
627 return o.equals(k0); // implicit nullcheck of o
628 }
641 }
642
643 @Override
644 public int hashCode() {
645 return k0.hashCode() ^ v0.hashCode();
646 }
647 }
648
649 /**
650 * An array-based Map implementation. There is a single array "table" that
651 * contains keys and values interleaved: table[0] is kA, table[1] is vA,
652 * table[2] is kB, table[3] is vB, etc. The table size must be even. It must
653 * also be strictly larger than the size (the number of key-value pairs contained
654 * in the map) so that at least one null key is always present.
655 * @param <K> the key type
656 * @param <V> the value type
657 */
658 static final class MapN<K,V> extends AbstractImmutableMap<K,V> {
659 @Stable
660 final Object[] table; // pairs of key, value
661 @Stable
662 final int size; // number of pairs
663
664 MapN(Object... input) {
665 if ((input.length & 1) != 0) { // implicit nullcheck of input
666 throw new InternalError("length is odd");
667 }
668 size = input.length >> 1;
669
670 int len = EXPAND_FACTOR * input.length;
671 len = (len + 1) & ~1; // ensure table is even length
672 table = new Object[len];
673
674 for (int i = 0; i < input.length; i += 2) {
675 @SuppressWarnings("unchecked")
676 K k = Objects.requireNonNull((K)input[i]);
677 @SuppressWarnings("unchecked")
678 V v = Objects.requireNonNull((V)input[i+1]);
679 int idx = probe(k);
680 if (idx >= 0) {
681 throw new IllegalArgumentException("duplicate key: " + k);
682 } else {
683 int dest = -(idx + 1);
684 table[dest] = k;
685 table[dest+1] = v;
686 }
687 }
688 }
689
690 @Override
691 public boolean containsKey(Object o) {
692 return probe(o) >= 0; // implicit nullcheck of o
693 }
694
695 @Override
696 public boolean containsValue(Object o) {
697 for (int i = 1; i < table.length; i += 2) {
698 Object v = table[i];
699 if (v != null && o.equals(v)) { // implicit nullcheck of o
700 return true;
701 }
702 }
703 return false;
704 }
705
706 @Override
707 public int hashCode() {
708 int hash = 0;
709 for (int i = 0; i < table.length; i += 2) {
710 Object k = table[i];
711 if (k != null) {
712 hash += k.hashCode() ^ table[i + 1].hashCode();
713 }
714 }
715 return hash;
716 }
717
718 @Override
719 @SuppressWarnings("unchecked")
720 public V get(Object o) {
721 int i = probe(o);
722 if (i >= 0) {
723 return (V)table[i+1];
724 } else {
725 return null;
726 }
727 }
728
729 @Override
730 public int size() {
731 return size;
732 }
733
734 @Override
735 public Set<Map.Entry<K,V>> entrySet() {
736 return new AbstractSet<Map.Entry<K,V>>() {
737 @Override
738 public int size() {
739 return MapN.this.size;
740 }
931 * @throws InvalidObjectException if the tag value is illegal or if an exception
932 * is thrown during creation of the collection
933 * @throws ObjectStreamException if another serialization error has occurred
934 * @since 9
935 */
936 private Object readResolve() throws ObjectStreamException {
937 try {
938 if (array == null) {
939 throw new InvalidObjectException("null array");
940 }
941
942 // use low order 8 bits to indicate "kind"
943 // ignore high order 24 bits
944 switch (tag & 0xff) {
945 case IMM_LIST:
946 return List.of(array);
947 case IMM_SET:
948 return Set.of(array);
949 case IMM_MAP:
950 if (array.length == 0) {
951 return ImmutableCollections.Map0.instance();
952 } else if (array.length == 2) {
953 return new ImmutableCollections.Map1<>(array[0], array[1]);
954 } else {
955 return new ImmutableCollections.MapN<>(array);
956 }
957 default:
958 throw new InvalidObjectException(String.format("invalid flags 0x%x", tag));
959 }
960 } catch (NullPointerException|IllegalArgumentException ex) {
961 InvalidObjectException ioe = new InvalidObjectException("invalid object");
962 ioe.initCause(ex);
963 throw ioe;
964 }
965 }
966 }
|
42 * Container class for immutable collections. Not part of the public API.
43 * Mainly for namespace management and shared infrastructure.
44 *
45 * Serial warnings are suppressed throughout because all implementation
46 * classes use a serial proxy and thus have no need to declare serialVersionUID.
47 */
48 @SuppressWarnings("serial")
49 class ImmutableCollections {
50 /**
51 * A "salt" value used for randomizing iteration order. This is initialized once
52 * and stays constant for the lifetime of the JVM. It need not be truly random, but
53 * it needs to vary sufficiently from one run to the next so that iteration order
54 * will vary between JVM runs.
55 */
56 static final int SALT;
57 static {
58 long nt = System.nanoTime();
59 SALT = (int)((nt >>> 32) ^ nt);
60 }
61
62 private static final Object NULL = new Object();
63
64 /** No instances. */
65 private ImmutableCollections() { }
66
67 /**
68 * The reciprocal of load factor. Given a number of elements
69 * to store, multiply by this factor to get the table size.
70 */
71 static final int EXPAND_FACTOR = 2;
72
73 static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
74
75 // ---------- List Implementations ----------
76
77 abstract static class AbstractImmutableList<E> extends AbstractList<E>
78 implements RandomAccess, Serializable {
79 @Override public boolean add(E e) { throw uoe(); }
80 @Override public boolean addAll(Collection<? extends E> c) { throw uoe(); }
81 @Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); }
82 @Override public void clear() { throw uoe(); }
83 @Override public boolean remove(Object o) { throw uoe(); }
84 @Override public boolean removeAll(Collection<?> c) { throw uoe(); }
85 @Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); }
86 @Override public void replaceAll(UnaryOperator<E> operator) { throw uoe(); }
87 @Override public boolean retainAll(Collection<?> c) { throw uoe(); }
88 @Override public void sort(Comparator<? super E> c) { throw uoe(); }
89 }
90
91 static final List<?> EMPTY_LIST = new ListN<>();
92
93 static final Set<?> EMPTY_SET = new SetN<>();
94
95 static final Map<?,?> EMPTY_MAP = new MapN<>();
96
97 @SuppressWarnings("unchecked")
98 static <T> List<T> emptyList() {
99 return (List<T>) EMPTY_LIST;
100 }
101
102 @SuppressWarnings("unchecked")
103 static <T> Set<T> emptySet() {
104 return (Set<T>) EMPTY_SET;
105 }
106
107 @SuppressWarnings("unchecked")
108 static <K,V> Map<K,V> emptyMap() {
109 return (Map<K,V>) EMPTY_MAP;
110 }
111
112 static final class List1<E> extends AbstractImmutableList<E> {
113 @Stable
114 private final E e0;
115
116 List1(E e0) {
117 this.e0 = Objects.requireNonNull(e0);
118 }
119
120 @Override
121 public int size() {
122 return 1;
123 }
124
125 @Override
126 public E get(int index) {
127 Objects.checkIndex(index, 1);
128 return e0;
129 }
130
131 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
132 throw new InvalidObjectException("not serial proxy");
133 }
134
135 private Object writeReplace() {
136 return new CollSer(CollSer.IMM_LIST, e0);
137 }
138
139 @Override
140 public boolean contains(Object o) {
141 return o.equals(e0); // implicit nullcheck of o
142 }
143
144 @Override
145 public int hashCode() {
146 return 31 + e0.hashCode();
147 }
148 }
149
150 static final class ListN<E> extends AbstractImmutableList<E> {
151 @Stable
152 private final E[] elements;
153
154 @SafeVarargs
155 ListN(E... input) {
156 // copy and check manually to avoid TOCTOU
157 @SuppressWarnings("unchecked")
158 E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
159 for (int i = 0; i < input.length; i++) {
160 tmp[i] = Objects.requireNonNull(input[i]);
161 }
162 this.elements = tmp;
163 }
164
165 @Override
166 public int size() {
167 return elements.length;
168 }
169
196 throw new InvalidObjectException("not serial proxy");
197 }
198
199 private Object writeReplace() {
200 return new CollSer(CollSer.IMM_LIST, elements);
201 }
202 }
203
204 // ---------- Set Implementations ----------
205
206 abstract static class AbstractImmutableSet<E> extends AbstractSet<E> implements Serializable {
207 @Override public boolean add(E e) { throw uoe(); }
208 @Override public boolean addAll(Collection<? extends E> c) { throw uoe(); }
209 @Override public void clear() { throw uoe(); }
210 @Override public boolean remove(Object o) { throw uoe(); }
211 @Override public boolean removeAll(Collection<?> c) { throw uoe(); }
212 @Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); }
213 @Override public boolean retainAll(Collection<?> c) { throw uoe(); }
214 }
215
216 static final class Set1<E> extends AbstractImmutableSet<E> {
217 @Stable
218 private final E e0;
219
220 Set1(E e0) {
221 this.e0 = Objects.requireNonNull(e0);
222 }
223
224 @Override
225 public int size() {
226 return 1;
227 }
228
229 @Override
230 public boolean contains(Object o) {
231 return o.equals(e0); // implicit nullcheck of o
232 }
233
234 @Override
235 public Iterator<E> iterator() {
236 return Collections.singletonIterator(e0);
237 }
238
239 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
240 throw new InvalidObjectException("not serial proxy");
241 }
242
243 private Object writeReplace() {
244 return new CollSer(CollSer.IMM_SET, e0);
245 }
246
247 @Override
248 public int hashCode() {
249 return e0.hashCode();
250 }
251 }
252
253 /**
254 * An array-based Set implementation. The element array must be strictly
255 * larger than the size (the number of contained elements) so that at
256 * least one null is always present.
257 * @param <E> the element type
258 */
259 static final class SetN<E> extends AbstractImmutableSet<E> {
260 @Stable
261 final E[] elements;
262 @Stable
263 final int size;
264
265 @SafeVarargs
266 @SuppressWarnings("unchecked")
267 SetN(E... input) {
268 size = input.length; // implicit nullcheck of input
269
270 elements = (E[])new Object[EXPAND_FACTOR * input.length];
271 for (int i = 0; i < input.length; i++) {
272 E e = input[i];
273 int idx = probe(e); // implicit nullcheck of e
274 if (idx >= 0) {
275 throw new IllegalArgumentException("duplicate element: " + e);
276 } else {
277 elements[-(idx + 1)] = e;
278 }
279 }
280 }
281
282 @Override
283 public int size() {
284 return size;
285 }
286
287 @Override
288 public boolean contains(Object o) {
289 Objects.requireNonNull(0);
290 return size > 0 && probe(o) >= 0; // implicit nullcheck of o
291 }
292
293 @Override
294 public Iterator<E> iterator() {
295 return new Iterator<E>() {
296 private int idx = 0;
297
298 @Override
299 public boolean hasNext() {
300 while (idx < elements.length) {
301 if (elements[idx] != null)
302 return true;
303 idx++;
304 }
305 return false;
306 }
307
308 @Override
309 public E next() {
310 if (! hasNext()) {
361 }
362
363 // ---------- Map Implementations ----------
364
365 abstract static class AbstractImmutableMap<K,V> extends AbstractMap<K,V> implements Serializable {
366 @Override public void clear() { throw uoe(); }
367 @Override public V compute(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); }
368 @Override public V computeIfAbsent(K key, Function<? super K,? extends V> mf) { throw uoe(); }
369 @Override public V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); }
370 @Override public V merge(K key, V value, BiFunction<? super V,? super V,? extends V> rf) { throw uoe(); }
371 @Override public V put(K key, V value) { throw uoe(); }
372 @Override public void putAll(Map<? extends K,? extends V> m) { throw uoe(); }
373 @Override public V putIfAbsent(K key, V value) { throw uoe(); }
374 @Override public V remove(Object key) { throw uoe(); }
375 @Override public boolean remove(Object key, Object value) { throw uoe(); }
376 @Override public V replace(K key, V value) { throw uoe(); }
377 @Override public boolean replace(K key, V oldValue, V newValue) { throw uoe(); }
378 @Override public void replaceAll(BiFunction<? super K,? super V,? extends V> f) { throw uoe(); }
379 }
380
381 static final class Map1<K,V> extends AbstractImmutableMap<K,V> {
382 @Stable
383 private final K k0;
384 @Stable
385 private final V v0;
386
387 Map1(K k0, V v0) {
388 this.k0 = Objects.requireNonNull(k0);
389 this.v0 = Objects.requireNonNull(v0);
390 }
391
392 @Override
393 public Set<Map.Entry<K,V>> entrySet() {
394 return Set.of(new KeyValueHolder<>(k0, v0));
395 }
396
397 @Override
398 public boolean containsKey(Object o) {
399 return o.equals(k0); // implicit nullcheck of o
400 }
413 }
414
415 @Override
416 public int hashCode() {
417 return k0.hashCode() ^ v0.hashCode();
418 }
419 }
420
421 /**
422 * An array-based Map implementation. There is a single array "table" that
423 * contains keys and values interleaved: table[0] is kA, table[1] is vA,
424 * table[2] is kB, table[3] is vB, etc. The table size must be even. It must
425 * also be strictly larger than the size (the number of key-value pairs contained
426 * in the map) so that at least one null key is always present.
427 * @param <K> the key type
428 * @param <V> the value type
429 */
430 static final class MapN<K,V> extends AbstractImmutableMap<K,V> {
431 @Stable
432 final Object[] table; // pairs of key, value
433 final int size; // number of pairs
434
435 MapN(Object... input) {
436 if ((input.length & 1) != 0) { // implicit nullcheck of input
437 throw new InternalError("length is odd");
438 }
439 size = input.length >> 1;
440
441 int len = EXPAND_FACTOR * input.length;
442 len = (len + 1) & ~1; // ensure table is even length
443 table = new Object[len];
444
445 for (int i = 0; i < input.length; i += 2) {
446 @SuppressWarnings("unchecked")
447 K k = Objects.requireNonNull((K)input[i]);
448 @SuppressWarnings("unchecked")
449 V v = Objects.requireNonNull((V)input[i+1]);
450 int idx = probe(k);
451 if (idx >= 0) {
452 throw new IllegalArgumentException("duplicate key: " + k);
453 } else {
454 int dest = -(idx + 1);
455 table[dest] = k;
456 table[dest+1] = v;
457 }
458 }
459 }
460
461 @Override
462 public boolean containsKey(Object o) {
463 Objects.requireNonNull(0);
464 return size > 0 && probe(o) >= 0;
465 }
466
467 @Override
468 public boolean containsValue(Object o) {
469 for (int i = 1; i < table.length; i += 2) {
470 Object v = table[i];
471 if (v != null && o.equals(v)) { // implicit nullcheck of o
472 return true;
473 }
474 }
475 return false;
476 }
477
478 @Override
479 public int hashCode() {
480 int hash = 0;
481 for (int i = 0; i < table.length; i += 2) {
482 Object k = table[i];
483 if (k != null) {
484 hash += k.hashCode() ^ table[i + 1].hashCode();
485 }
486 }
487 return hash;
488 }
489
490 @Override
491 @SuppressWarnings("unchecked")
492 public V get(Object o) {
493 if (size == 0) {
494 return null;
495 }
496 int i = probe(o);
497 if (i >= 0) {
498 return (V)table[i+1];
499 } else {
500 return null;
501 }
502 }
503
504 @Override
505 public int size() {
506 return size;
507 }
508
509 @Override
510 public Set<Map.Entry<K,V>> entrySet() {
511 return new AbstractSet<Map.Entry<K,V>>() {
512 @Override
513 public int size() {
514 return MapN.this.size;
515 }
706 * @throws InvalidObjectException if the tag value is illegal or if an exception
707 * is thrown during creation of the collection
708 * @throws ObjectStreamException if another serialization error has occurred
709 * @since 9
710 */
711 private Object readResolve() throws ObjectStreamException {
712 try {
713 if (array == null) {
714 throw new InvalidObjectException("null array");
715 }
716
717 // use low order 8 bits to indicate "kind"
718 // ignore high order 24 bits
719 switch (tag & 0xff) {
720 case IMM_LIST:
721 return List.of(array);
722 case IMM_SET:
723 return Set.of(array);
724 case IMM_MAP:
725 if (array.length == 0) {
726 return ImmutableCollections.emptyMap();
727 } else if (array.length == 2) {
728 return new ImmutableCollections.Map1<>(array[0], array[1]);
729 } else {
730 return new ImmutableCollections.MapN<>(array);
731 }
732 default:
733 throw new InvalidObjectException(String.format("invalid flags 0x%x", tag));
734 }
735 } catch (NullPointerException|IllegalArgumentException ex) {
736 InvalidObjectException ioe = new InvalidObjectException("invalid object");
737 ioe.initCause(ex);
738 throw ioe;
739 }
740 }
741 }
|