1 /* 2 * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.util; 27 28 import java.io.IOException; 29 import java.io.InvalidObjectException; 30 import java.io.ObjectInputStream; 31 import java.io.ObjectOutputStream; 32 import java.io.ObjectStreamException; 33 import java.io.Serializable; 34 import java.util.function.BiFunction; 35 import java.util.function.Function; 36 import java.util.function.Predicate; 37 import java.util.function.UnaryOperator; 38 import jdk.internal.misc.SharedSecrets; 39 import jdk.internal.vm.annotation.Stable; 40 41 /** 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 244 @Override 245 public E get(int index) { 246 Objects.checkIndex(index, elements.length); 247 return elements[index]; 248 } 249 250 @Override 251 public boolean contains(Object o) { 252 for (E e : elements) { 253 if (o.equals(e)) { // implicit nullcheck of o 254 return true; 255 } 256 } 257 return false; 258 } 259 260 @Override 261 public int hashCode() { 262 int hash = 1; 263 for (E e : elements) { 264 hash = 31 * hash + e.hashCode(); 265 } 266 return hash; 267 } 268 269 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 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()) { 498 throw new NoSuchElementException(); 499 } 500 return elements[idx++]; 501 } 502 }; 503 } 504 505 @Override 506 public int hashCode() { 507 int h = 0; 508 for (E e : elements) { 509 if (e != null) { 510 h += e.hashCode(); 511 } 512 } 513 return h; 514 } 515 516 // returns index at which element is present; or if absent, 517 // (-i - 1) where i is location where element should be inserted. 518 // Callers are relying on this method to perform an implicit nullcheck 519 // of pe 520 private int probe(Object pe) { 521 int idx = Math.floorMod(pe.hashCode() ^ SALT, elements.length); 522 while (true) { 523 E ee = elements[idx]; 524 if (ee == null) { 525 return -idx - 1; 526 } else if (pe.equals(ee)) { 527 return idx; 528 } else if (++idx == elements.length) { 529 idx = 0; 530 } 531 } 532 } 533 534 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 535 throw new InvalidObjectException("not serial proxy"); 536 } 537 538 private Object writeReplace() { 539 Object[] array = new Object[size]; 540 int dest = 0; 541 for (Object o : elements) { 542 if (o != null) { 543 array[dest++] = o; 544 } 545 } 546 return new CollSer(CollSer.IMM_SET, array); 547 } 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 } 629 630 @Override 631 public boolean containsValue(Object o) { 632 return o.equals(v0); // implicit nullcheck of o 633 } 634 635 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 636 throw new InvalidObjectException("not serial proxy"); 637 } 638 639 private Object writeReplace() { 640 return new CollSer(CollSer.IMM_MAP, k0, v0); 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 } 741 742 @Override 743 public Iterator<Map.Entry<K,V>> iterator() { 744 return new Iterator<Map.Entry<K,V>>() { 745 int idx = 0; 746 747 @Override 748 public boolean hasNext() { 749 while (idx < table.length) { 750 if (table[idx] != null) 751 return true; 752 idx += 2; 753 } 754 return false; 755 } 756 757 @Override 758 public Map.Entry<K,V> next() { 759 if (hasNext()) { 760 @SuppressWarnings("unchecked") 761 Map.Entry<K,V> e = 762 new KeyValueHolder<>((K)table[idx], (V)table[idx+1]); 763 idx += 2; 764 return e; 765 } else { 766 throw new NoSuchElementException(); 767 } 768 } 769 }; 770 } 771 }; 772 } 773 774 // returns index at which the probe key is present; or if absent, 775 // (-i - 1) where i is location where element should be inserted. 776 // Callers are relying on this method to perform an implicit nullcheck 777 // of pk. 778 private int probe(Object pk) { 779 int idx = Math.floorMod(pk.hashCode() ^ SALT, table.length >> 1) << 1; 780 while (true) { 781 @SuppressWarnings("unchecked") 782 K ek = (K)table[idx]; 783 if (ek == null) { 784 return -idx - 1; 785 } else if (pk.equals(ek)) { 786 return idx; 787 } else if ((idx += 2) == table.length) { 788 idx = 0; 789 } 790 } 791 } 792 793 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 794 throw new InvalidObjectException("not serial proxy"); 795 } 796 797 private Object writeReplace() { 798 Object[] array = new Object[2 * size]; 799 int len = table.length; 800 int dest = 0; 801 for (int i = 0; i < len; i += 2) { 802 if (table[i] != null) { 803 array[dest++] = table[i]; 804 array[dest++] = table[i+1]; 805 } 806 } 807 return new CollSer(CollSer.IMM_MAP, array); 808 } 809 } 810 } 811 812 // ---------- Serialization Proxy ---------- 813 814 /** 815 * A unified serialization proxy class for the immutable collections. 816 * 817 * @serial 818 * @since 9 819 */ 820 final class CollSer implements Serializable { 821 private static final long serialVersionUID = 6309168927139932177L; 822 823 static final int IMM_LIST = 1; 824 static final int IMM_SET = 2; 825 static final int IMM_MAP = 3; 826 827 /** 828 * Indicates the type of collection that is serialized. 829 * The low order 8 bits have the value 1 for an immutable 830 * {@code List}, 2 for an immutable {@code Set}, and 3 for 831 * an immutable {@code Map}. Any other value causes an 832 * {@link InvalidObjectException} to be thrown. The high 833 * order 24 bits are zero when an instance is serialized, 834 * and they are ignored when an instance is deserialized. 835 * They can thus be used by future implementations without 836 * causing compatibility issues. 837 * 838 * <p>The tag value also determines the interpretation of the 839 * transient {@code Object[] array} field. 840 * For {@code List} and {@code Set}, the array's length is the size 841 * of the collection, and the array contains the elements of the collection. 842 * Null elements are not allowed. For {@code Set}, duplicate elements 843 * are not allowed. 844 * 845 * <p>For {@code Map}, the array's length is twice the number of mappings 846 * present in the map. The array length is necessarily even. 847 * The array contains a succession of key and value pairs: 848 * {@code k1, v1, k2, v2, ..., kN, vN.} Nulls are not allowed, 849 * and duplicate keys are not allowed. 850 * 851 * @serial 852 * @since 9 853 */ 854 private final int tag; 855 856 /** 857 * @serial 858 * @since 9 859 */ 860 private transient Object[] array; 861 862 CollSer(int t, Object... a) { 863 tag = t; 864 array = a; 865 } 866 867 /** 868 * Reads objects from the stream and stores them 869 * in the transient {@code Object[] array} field. 870 * 871 * @serialData 872 * A nonnegative int, indicating the count of objects, 873 * followed by that many objects. 874 * 875 * @param ois the ObjectInputStream from which data is read 876 * @throws IOException if an I/O error occurs 877 * @throws ClassNotFoundException if a serialized class cannot be loaded 878 * @throws InvalidObjectException if the count is negative 879 * @since 9 880 */ 881 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 882 ois.defaultReadObject(); 883 int len = ois.readInt(); 884 885 if (len < 0) { 886 throw new InvalidObjectException("negative length " + len); 887 } 888 889 SharedSecrets.getJavaObjectInputStreamAccess().checkArray(ois, Object[].class, len); 890 Object[] a = new Object[len]; 891 for (int i = 0; i < len; i++) { 892 a[i] = ois.readObject(); 893 } 894 895 array = a; 896 } 897 898 /** 899 * Writes objects to the stream from 900 * the transient {@code Object[] array} field. 901 * 902 * @serialData 903 * A nonnegative int, indicating the count of objects, 904 * followed by that many objects. 905 * 906 * @param oos the ObjectOutputStream to which data is written 907 * @throws IOException if an I/O error occurs 908 * @since 9 909 */ 910 private void writeObject(ObjectOutputStream oos) throws IOException { 911 oos.defaultWriteObject(); 912 oos.writeInt(array.length); 913 for (int i = 0; i < array.length; i++) { 914 oos.writeObject(array[i]); 915 } 916 } 917 918 /** 919 * Creates and returns an immutable collection from this proxy class. 920 * The instance returned is created as if by calling one of the 921 * static factory methods for 922 * <a href="List.html#immutable">List</a>, 923 * <a href="Map.html#immutable">Map</a>, or 924 * <a href="Set.html#immutable">Set</a>. 925 * This proxy class is the serial form for all immutable collection instances, 926 * regardless of implementation type. This is necessary to ensure that the 927 * existence of any particular implementation type is kept out of the 928 * serialized form. 929 * 930 * @return a collection created from this proxy object 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 }