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.ForceInline; 40 import jdk.internal.vm.annotation.Stable; 41 42 /** 43 * Container class for immutable collections. Not part of the public API. 44 * Mainly for namespace management and shared infrastructure. 45 * 46 * Serial warnings are suppressed throughout because all implementation 47 * classes use a serial proxy and thus have no need to declare serialVersionUID. 48 */ 49 @SuppressWarnings("serial") 50 class ImmutableCollections { 51 /** 52 * A "salt" value used for randomizing iteration order. This is initialized once 53 * and stays constant for the lifetime of the JVM. It need not be truly random, but 54 * it needs to vary sufficiently from one run to the next so that iteration order 55 * will vary between JVM runs. 56 */ 57 static final int SALT; 58 static { 59 long nt = System.nanoTime(); 60 SALT = (int)((nt >>> 32) ^ nt); 61 } 62 63 /** No instances. */ 64 private ImmutableCollections() { } 65 66 /** 67 * The reciprocal of load factor. Given a number of elements 68 * to store, multiply by this factor to get the table size. 69 */ 70 static final int EXPAND_FACTOR = 2; 71 72 static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); } 73 74 // ---------- List Implementations ---------- 75 76 @SuppressWarnings("unchecked") 77 static <E> List<E> emptyList() { 78 return (List<E>) ListN.EMPTY_LIST; 79 } 80 81 static abstract class AbstractImmutableCollection<E> extends AbstractCollection<E> { 82 // all mutating methods throw UnsupportedOperationException 83 @Override public boolean add(E e) { throw uoe(); } 84 @Override public boolean addAll(Collection<? extends E> c) { throw uoe(); } 85 @Override public void clear() { throw uoe(); } 86 @Override public boolean remove(Object o) { throw uoe(); } 87 @Override public boolean removeAll(Collection<?> c) { throw uoe(); } 88 @Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); } 89 @Override public boolean retainAll(Collection<?> c) { throw uoe(); } 90 } 91 92 static abstract class AbstractImmutableSet<E> extends AbstractImmutableCollection<E> 93 implements Set<E> { 94 95 @Override 96 public boolean equals(Object o) { 97 if (o == this) 98 return true; 99 100 if (!(o instanceof Set)) 101 return false; 102 Collection<?> c = (Collection<?>) o; 103 if (c.size() != size()) 104 return false; 105 try { 106 return containsAll(c); 107 } catch (ClassCastException | NullPointerException unused) { 108 return false; 109 } 110 } 111 } 112 113 static abstract class AbstractImmutableList<E> extends AbstractImmutableCollection<E> 114 implements List<E>, RandomAccess { 115 116 // all mutating methods throw UnsupportedOperationException 117 @Override public void add(int index, E element) { throw uoe(); } 118 @Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); } 119 @Override public E remove(int index) { throw uoe(); } 120 @Override public void replaceAll(UnaryOperator<E> operator) { throw uoe(); } 121 @Override public E set(int index, E element) { throw uoe(); } 122 @Override public void sort(Comparator<? super E> c) { throw uoe(); } 123 124 @Override 125 public List<E> subList(int fromIndex, int toIndex) { 126 int size = size(); 127 subListRangeCheck(fromIndex, toIndex, size); 128 return new SubList<E>(this, fromIndex, toIndex); 129 } 130 131 private static final class SubList<E> extends AbstractImmutableList<E> 132 implements RandomAccess { 133 private final List<E> root; 134 final int offset; 135 int size; 136 137 /** 138 * Constructs a sublist of an arbitrary AbstractList, which is 139 * not a SubList itself. 140 */ 141 SubList(List<E> root, int fromIndex, int toIndex) { 142 this.root = root; 143 this.offset = fromIndex; 144 this.size = toIndex - fromIndex; 145 } 146 147 /** 148 * Constructs a sublist of another SubList. 149 */ 150 SubList(SubList<E> parent, int fromIndex, int toIndex) { 151 this.root = parent.root; 152 this.offset = parent.offset + fromIndex; 153 this.size = toIndex - fromIndex; 154 } 155 156 public E get(int index) { 157 Objects.checkIndex(index, size); 158 return root.get(offset + index); 159 } 160 161 public int size() { 162 return size; 163 } 164 165 public Iterator<E> iterator() { 166 return listIterator(); 167 } 168 169 public ListIterator<E> listIterator(int index) { 170 rangeCheck(index); 171 172 ListIterator<E> i = root.listIterator(offset + index); 173 174 return new ListIterator<>() { 175 176 public boolean hasNext() { 177 return nextIndex() < size; 178 } 179 180 public E next() { 181 if (hasNext()) { 182 return i.next(); 183 } else { 184 throw new NoSuchElementException(); 185 } 186 } 187 188 public boolean hasPrevious() { 189 return previousIndex() >= 0; 190 } 191 192 public E previous() { 193 if (hasPrevious()) { 194 return i.previous(); 195 } else { 196 throw new NoSuchElementException(); 197 } 198 } 199 200 public int nextIndex() { 201 return i.nextIndex() - offset; 202 } 203 204 public int previousIndex() { 205 return i.previousIndex() - offset; 206 } 207 208 public void remove() { throw uoe(); } 209 public void set(E e) { throw uoe(); } 210 public void add(E e) { throw uoe(); } 211 }; 212 } 213 214 public List<E> subList(int fromIndex, int toIndex) { 215 subListRangeCheck(fromIndex, toIndex, size); 216 return new SubList<>(this, fromIndex, toIndex); 217 } 218 219 private void rangeCheck(int index) { 220 if (index < 0 || index > size) { 221 throw outOfBounds(index); 222 } 223 } 224 } 225 226 static void subListRangeCheck(int fromIndex, int toIndex, int size) { 227 if (fromIndex < 0) 228 throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); 229 if (toIndex > size) 230 throw new IndexOutOfBoundsException("toIndex = " + toIndex); 231 if (fromIndex > toIndex) 232 throw new IllegalArgumentException("fromIndex(" + fromIndex + 233 ") > toIndex(" + toIndex + ")"); 234 } 235 236 @Override 237 public Iterator<E> iterator() { 238 return new Itr(size()); 239 } 240 241 @Override 242 public ListIterator<E> listIterator() { 243 return listIterator(0); 244 } 245 246 @Override 247 public ListIterator<E> listIterator(final int index) { 248 int size = size(); 249 if (index < 0 || index > size) { 250 throw outOfBounds(index); 251 } 252 return new ListItr(index, size); 253 } 254 255 private class Itr implements Iterator<E> { 256 257 int cursor; 258 259 private final int size; 260 261 Itr(int size) { 262 this.size = size; 263 } 264 265 public boolean hasNext() { 266 return cursor != size; 267 } 268 269 public E next() { 270 try { 271 int i = cursor; 272 E next = get(i); 273 cursor = i + 1; 274 return next; 275 } catch (IndexOutOfBoundsException e) { 276 throw new NoSuchElementException(); 277 } 278 } 279 280 public void remove() { 281 throw uoe(); 282 } 283 } 284 285 private class ListItr extends Itr implements ListIterator<E> { 286 287 ListItr(int index, int size) { 288 super(size); 289 cursor = index; 290 } 291 292 public boolean hasPrevious() { 293 return cursor != 0; 294 } 295 296 public E previous() { 297 try { 298 int i = cursor - 1; 299 E previous = get(i); 300 cursor = i; 301 return previous; 302 } catch (IndexOutOfBoundsException e) { 303 throw new NoSuchElementException(); 304 } 305 } 306 307 public int nextIndex() { 308 return cursor; 309 } 310 311 public int previousIndex() { 312 return cursor - 1; 313 } 314 315 public void set(E e) { 316 throw uoe(); 317 } 318 319 public void add(E e) { 320 throw uoe(); 321 } 322 } 323 324 @Override 325 public boolean equals(Object o) { 326 if (o == this) { 327 return true; 328 } 329 330 if (!(o instanceof List)) { 331 return false; 332 } 333 334 Iterator<?> oit = ((List<?>) o).iterator(); 335 for (int i = 0, s = size(); i < s; i++) { 336 if (!oit.hasNext() || !get(i).equals(oit.next())) { 337 return false; 338 } 339 } 340 return !oit.hasNext(); 341 } 342 343 @Override 344 public int indexOf(Object o) { 345 Objects.requireNonNull(o); 346 for (int i = 0, s = size(); i < s; i++) { 347 if (o.equals(get(i))) { 348 return i; 349 } 350 } 351 return -1; 352 } 353 354 @Override 355 public int lastIndexOf(Object o) { 356 Objects.requireNonNull(o); 357 for (int i = size() - 1; i >= 0; i--) { 358 if (o.equals(get(i))) { 359 return i; 360 } 361 } 362 return -1; 363 } 364 365 @Override 366 public int hashCode() { 367 int hash = 1; 368 for (int i = 0, s = size(); i < s; i++) { 369 hash = 31 * hash + get(i).hashCode(); 370 } 371 return hash; 372 } 373 374 @Override 375 public boolean contains(Object o) { 376 return indexOf(o) >= 0; 377 } 378 379 IndexOutOfBoundsException outOfBounds(int index) { 380 return new IndexOutOfBoundsException("Index: " + index + " Size: " + size()); 381 } 382 } 383 384 static final class List12<E> extends AbstractImmutableList<E> 385 implements Serializable { 386 387 @Stable 388 private final E e0; 389 390 @Stable 391 private final E e1; 392 393 List12(E e0) { 394 this.e0 = Objects.requireNonNull(e0); 395 this.e1 = null; 396 } 397 398 List12(E e0, E e1) { 399 this.e0 = Objects.requireNonNull(e0); 400 this.e1 = Objects.requireNonNull(e1); 401 } 402 403 @Override 404 public int size() { 405 return e1 != null ? 2 : 1; 406 } 407 408 @Override 409 public E get(int index) { 410 if (index == 0) { 411 return e0; 412 } else if (index == 1 && e1 != null) { 413 return e1; 414 } 415 throw outOfBounds(index); 416 } 417 418 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 419 throw new InvalidObjectException("not serial proxy"); 420 } 421 422 private Object writeReplace() { 423 if (e1 == null) { 424 return new CollSer(CollSer.IMM_LIST, e0); 425 } else { 426 return new CollSer(CollSer.IMM_LIST, e0, e1); 427 } 428 } 429 430 } 431 432 static final class ListN<E> extends AbstractImmutableList<E> 433 implements Serializable { 434 435 static final List<?> EMPTY_LIST = new ListN<>(); 436 437 @Stable 438 private final E[] elements; 439 440 @SafeVarargs 441 ListN(E... input) { 442 // copy and check manually to avoid TOCTOU 443 @SuppressWarnings("unchecked") 444 E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input 445 for (int i = 0; i < input.length; i++) { 446 tmp[i] = Objects.requireNonNull(input[i]); 447 } 448 elements = tmp; 449 } 450 451 @Override 452 public boolean isEmpty() { 453 return size() == 0; 454 } 455 456 @Override 457 public int size() { 458 return elements.length; 459 } 460 461 @Override 462 public E get(int index) { 463 return elements[index]; 464 } 465 466 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 467 throw new InvalidObjectException("not serial proxy"); 468 } 469 470 private Object writeReplace() { 471 return new CollSer(CollSer.IMM_LIST, elements); 472 } 473 } 474 475 // ---------- Set Implementations ---------- 476 477 @SuppressWarnings("unchecked") 478 static <E> Set<E> emptySet() { 479 return (Set<E>) SetN.EMPTY_SET; 480 } 481 482 static final class Set12<E> extends AbstractImmutableSet<E> 483 implements Serializable { 484 485 @Stable 486 final E e0; 487 @Stable 488 final E e1; 489 490 Set12(E e0) { 491 this.e0 = Objects.requireNonNull(e0); 492 this.e1 = null; 493 } 494 495 Set12(E e0, E e1) { 496 if (e0.equals(Objects.requireNonNull(e1))) { // implicit nullcheck of e0 497 throw new IllegalArgumentException("duplicate element: " + e0); 498 } 499 500 if (SALT >= 0) { 501 this.e0 = e0; 502 this.e1 = e1; 503 } else { 504 this.e0 = e1; 505 this.e1 = e0; 506 } 507 } 508 509 @Override 510 public int size() { 511 return (e1 == null) ? 1 : 2; 512 } 513 514 @Override 515 public boolean contains(Object o) { 516 return o.equals(e0) || o.equals(e1); // implicit nullcheck of o 517 } 518 519 @Override 520 public int hashCode() { 521 return e0.hashCode() + (e1 == null ? 0 : e1.hashCode()); 522 } 523 524 @Override 525 public Iterator<E> iterator() { 526 return new Iterator<E>() { 527 private int idx = size(); 528 529 @Override 530 public boolean hasNext() { 531 return idx > 0; 532 } 533 534 @Override 535 public E next() { 536 if (idx == 1) { 537 idx = 0; 538 return e0; 539 } else if (idx == 2) { 540 idx = 1; 541 return e1; 542 } else { 543 throw new NoSuchElementException(); 544 } 545 } 546 }; 547 } 548 549 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 550 throw new InvalidObjectException("not serial proxy"); 551 } 552 553 private Object writeReplace() { 554 if (e1 == null) { 555 return new CollSer(CollSer.IMM_SET, e0); 556 } else { 557 return new CollSer(CollSer.IMM_SET, e0, e1); 558 } 559 } 560 } 561 562 /** 563 * An array-based Set implementation. The element array must be strictly 564 * larger than the size (the number of contained elements) so that at 565 * least one null is always present. 566 * @param <E> the element type 567 */ 568 static final class SetN<E> extends AbstractImmutableSet<E> 569 implements Serializable { 570 571 static final Set<?> EMPTY_SET = new SetN<>(); 572 573 @Stable 574 final E[] elements; 575 @Stable 576 final int size; 577 578 @SafeVarargs 579 @SuppressWarnings("unchecked") 580 SetN(E... input) { 581 size = input.length; // implicit nullcheck of input 582 583 elements = (E[])new Object[EXPAND_FACTOR * input.length]; 584 for (int i = 0; i < input.length; i++) { 585 E e = input[i]; 586 int idx = probe(e); // implicit nullcheck of e 587 if (idx >= 0) { 588 throw new IllegalArgumentException("duplicate element: " + e); 589 } else { 590 elements[-(idx + 1)] = e; 591 } 592 } 593 } 594 595 @Override 596 public int size() { 597 return size; 598 } 599 600 @Override 601 public boolean contains(Object o) { 602 Objects.requireNonNull(o); 603 return size > 0 && probe(o) >= 0; 604 } 605 606 @Override 607 public Iterator<E> iterator() { 608 return new Iterator<E>() { 609 private int idx = 0; 610 611 @Override 612 public boolean hasNext() { 613 while (idx < elements.length) { 614 if (elements[idx] != null) 615 return true; 616 idx++; 617 } 618 return false; 619 } 620 621 @Override 622 public E next() { 623 if (! hasNext()) { 624 throw new NoSuchElementException(); 625 } 626 return elements[idx++]; 627 } 628 }; 629 } 630 631 @Override 632 public int hashCode() { 633 int h = 0; 634 for (E e : elements) { 635 if (e != null) { 636 h += e.hashCode(); 637 } 638 } 639 return h; 640 } 641 642 // returns index at which element is present; or if absent, 643 // (-i - 1) where i is location where element should be inserted. 644 // Callers are relying on this method to perform an implicit nullcheck 645 // of pe 646 private int probe(Object pe) { 647 int idx = Math.floorMod(pe.hashCode() ^ SALT, elements.length); 648 while (true) { 649 E ee = elements[idx]; 650 if (ee == null) { 651 return -idx - 1; 652 } else if (pe.equals(ee)) { 653 return idx; 654 } else if (++idx == elements.length) { 655 idx = 0; 656 } 657 } 658 } 659 660 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 661 throw new InvalidObjectException("not serial proxy"); 662 } 663 664 private Object writeReplace() { 665 Object[] array = new Object[size]; 666 int dest = 0; 667 for (Object o : elements) { 668 if (o != null) { 669 array[dest++] = o; 670 } 671 } 672 return new CollSer(CollSer.IMM_SET, array); 673 } 674 } 675 676 // ---------- Map Implementations ---------- 677 678 @SuppressWarnings("unchecked") 679 static <K,V> Map<K,V> emptyMap() { 680 return (Map<K,V>) MapN.EMPTY_MAP; 681 } 682 683 abstract static class AbstractImmutableMap<K,V> extends AbstractMap<K,V> implements Serializable { 684 @Override public void clear() { throw uoe(); } 685 @Override public V compute(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); } 686 @Override public V computeIfAbsent(K key, Function<? super K,? extends V> mf) { throw uoe(); } 687 @Override public V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); } 688 @Override public V merge(K key, V value, BiFunction<? super V,? super V,? extends V> rf) { throw uoe(); } 689 @Override public V put(K key, V value) { throw uoe(); } 690 @Override public void putAll(Map<? extends K,? extends V> m) { throw uoe(); } 691 @Override public V putIfAbsent(K key, V value) { throw uoe(); } 692 @Override public V remove(Object key) { throw uoe(); } 693 @Override public boolean remove(Object key, Object value) { throw uoe(); } 694 @Override public V replace(K key, V value) { throw uoe(); } 695 @Override public boolean replace(K key, V oldValue, V newValue) { throw uoe(); } 696 @Override public void replaceAll(BiFunction<? super K,? super V,? extends V> f) { throw uoe(); } 697 } 698 699 static final class Map1<K,V> extends AbstractImmutableMap<K,V> { 700 @Stable 701 private final K k0; 702 @Stable 703 private final V v0; 704 705 Map1(K k0, V v0) { 706 this.k0 = Objects.requireNonNull(k0); 707 this.v0 = Objects.requireNonNull(v0); 708 } 709 710 @Override 711 public Set<Map.Entry<K,V>> entrySet() { 712 return Set.of(new KeyValueHolder<>(k0, v0)); 713 } 714 715 @Override 716 public boolean containsKey(Object o) { 717 return o.equals(k0); // implicit nullcheck of o 718 } 719 720 @Override 721 public boolean containsValue(Object o) { 722 return o.equals(v0); // implicit nullcheck of o 723 } 724 725 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 726 throw new InvalidObjectException("not serial proxy"); 727 } 728 729 private Object writeReplace() { 730 return new CollSer(CollSer.IMM_MAP, k0, v0); 731 } 732 733 @Override 734 public int hashCode() { 735 return k0.hashCode() ^ v0.hashCode(); 736 } 737 } 738 739 /** 740 * An array-based Map implementation. There is a single array "table" that 741 * contains keys and values interleaved: table[0] is kA, table[1] is vA, 742 * table[2] is kB, table[3] is vB, etc. The table size must be even. It must 743 * also be strictly larger than the size (the number of key-value pairs contained 744 * in the map) so that at least one null key is always present. 745 * @param <K> the key type 746 * @param <V> the value type 747 */ 748 static final class MapN<K,V> extends AbstractImmutableMap<K,V> { 749 750 static final Map<?,?> EMPTY_MAP = new MapN<>(); 751 752 @Stable 753 final Object[] table; // pairs of key, value 754 755 @Stable 756 final int size; // number of pairs 757 758 MapN(Object... input) { 759 if ((input.length & 1) != 0) { // implicit nullcheck of input 760 throw new InternalError("length is odd"); 761 } 762 size = input.length >> 1; 763 764 int len = EXPAND_FACTOR * input.length; 765 len = (len + 1) & ~1; // ensure table is even length 766 table = new Object[len]; 767 768 for (int i = 0; i < input.length; i += 2) { 769 @SuppressWarnings("unchecked") 770 K k = Objects.requireNonNull((K)input[i]); 771 @SuppressWarnings("unchecked") 772 V v = Objects.requireNonNull((V)input[i+1]); 773 int idx = probe(k); 774 if (idx >= 0) { 775 throw new IllegalArgumentException("duplicate key: " + k); 776 } else { 777 int dest = -(idx + 1); 778 table[dest] = k; 779 table[dest+1] = v; 780 } 781 } 782 } 783 784 @Override 785 public boolean containsKey(Object o) { 786 Objects.requireNonNull(o); 787 return size > 0 && probe(o) >= 0; 788 } 789 790 @Override 791 public boolean containsValue(Object o) { 792 Objects.requireNonNull(o); 793 for (int i = 1; i < table.length; i += 2) { 794 Object v = table[i]; 795 if (v != null && o.equals(v)) { 796 return true; 797 } 798 } 799 return false; 800 } 801 802 @Override 803 public int hashCode() { 804 int hash = 0; 805 for (int i = 0; i < table.length; i += 2) { 806 Object k = table[i]; 807 if (k != null) { 808 hash += k.hashCode() ^ table[i + 1].hashCode(); 809 } 810 } 811 return hash; 812 } 813 814 @Override 815 @SuppressWarnings("unchecked") 816 public V get(Object o) { 817 if (size == 0) { 818 Objects.requireNonNull(o); 819 return null; 820 } 821 int i = probe(o); 822 if (i >= 0) { 823 return (V)table[i+1]; 824 } else { 825 return null; 826 } 827 } 828 829 @Override 830 public int size() { 831 return size; 832 } 833 834 @Override 835 public Set<Map.Entry<K,V>> entrySet() { 836 return new AbstractSet<>() { 837 @Override 838 public int size() { 839 return MapN.this.size; 840 } 841 842 @Override 843 public Iterator<Map.Entry<K,V>> iterator() { 844 return new Iterator<>() { 845 int idx = 0; 846 847 @Override 848 public boolean hasNext() { 849 while (idx < table.length) { 850 if (table[idx] != null) 851 return true; 852 idx += 2; 853 } 854 return false; 855 } 856 857 @Override 858 public Map.Entry<K,V> next() { 859 if (hasNext()) { 860 @SuppressWarnings("unchecked") 861 Map.Entry<K,V> e = 862 new KeyValueHolder<>((K)table[idx], (V)table[idx+1]); 863 idx += 2; 864 return e; 865 } else { 866 throw new NoSuchElementException(); 867 } 868 } 869 }; 870 } 871 }; 872 } 873 874 // returns index at which the probe key is present; or if absent, 875 // (-i - 1) where i is location where element should be inserted. 876 // Callers are relying on this method to perform an implicit nullcheck 877 // of pk. 878 private int probe(Object pk) { 879 int idx = Math.floorMod(pk.hashCode() ^ SALT, table.length >> 1) << 1; 880 while (true) { 881 @SuppressWarnings("unchecked") 882 K ek = (K)table[idx]; 883 if (ek == null) { 884 return -idx - 1; 885 } else if (pk.equals(ek)) { 886 return idx; 887 } else if ((idx += 2) == table.length) { 888 idx = 0; 889 } 890 } 891 } 892 893 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 894 throw new InvalidObjectException("not serial proxy"); 895 } 896 897 private Object writeReplace() { 898 Object[] array = new Object[2 * size]; 899 int len = table.length; 900 int dest = 0; 901 for (int i = 0; i < len; i += 2) { 902 if (table[i] != null) { 903 array[dest++] = table[i]; 904 array[dest++] = table[i+1]; 905 } 906 } 907 return new CollSer(CollSer.IMM_MAP, array); 908 } 909 } 910 } 911 912 // ---------- Serialization Proxy ---------- 913 914 /** 915 * A unified serialization proxy class for the immutable collections. 916 * 917 * @serial 918 * @since 9 919 */ 920 final class CollSer implements Serializable { 921 private static final long serialVersionUID = 6309168927139932177L; 922 923 static final int IMM_LIST = 1; 924 static final int IMM_SET = 2; 925 static final int IMM_MAP = 3; 926 927 /** 928 * Indicates the type of collection that is serialized. 929 * The low order 8 bits have the value 1 for an immutable 930 * {@code List}, 2 for an immutable {@code Set}, and 3 for 931 * an immutable {@code Map}. Any other value causes an 932 * {@link InvalidObjectException} to be thrown. The high 933 * order 24 bits are zero when an instance is serialized, 934 * and they are ignored when an instance is deserialized. 935 * They can thus be used by future implementations without 936 * causing compatibility issues. 937 * 938 * <p>The tag value also determines the interpretation of the 939 * transient {@code Object[] array} field. 940 * For {@code List} and {@code Set}, the array's length is the size 941 * of the collection, and the array contains the elements of the collection. 942 * Null elements are not allowed. For {@code Set}, duplicate elements 943 * are not allowed. 944 * 945 * <p>For {@code Map}, the array's length is twice the number of mappings 946 * present in the map. The array length is necessarily even. 947 * The array contains a succession of key and value pairs: 948 * {@code k1, v1, k2, v2, ..., kN, vN.} Nulls are not allowed, 949 * and duplicate keys are not allowed. 950 * 951 * @serial 952 * @since 9 953 */ 954 private final int tag; 955 956 /** 957 * @serial 958 * @since 9 959 */ 960 private transient Object[] array; 961 962 CollSer(int t, Object... a) { 963 tag = t; 964 array = a; 965 } 966 967 /** 968 * Reads objects from the stream and stores them 969 * in the transient {@code Object[] array} field. 970 * 971 * @serialData 972 * A nonnegative int, indicating the count of objects, 973 * followed by that many objects. 974 * 975 * @param ois the ObjectInputStream from which data is read 976 * @throws IOException if an I/O error occurs 977 * @throws ClassNotFoundException if a serialized class cannot be loaded 978 * @throws InvalidObjectException if the count is negative 979 * @since 9 980 */ 981 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 982 ois.defaultReadObject(); 983 int len = ois.readInt(); 984 985 if (len < 0) { 986 throw new InvalidObjectException("negative length " + len); 987 } 988 989 SharedSecrets.getJavaObjectInputStreamAccess().checkArray(ois, Object[].class, len); 990 Object[] a = new Object[len]; 991 for (int i = 0; i < len; i++) { 992 a[i] = ois.readObject(); 993 } 994 995 array = a; 996 } 997 998 /** 999 * Writes objects to the stream from 1000 * the transient {@code Object[] array} field. 1001 * 1002 * @serialData 1003 * A nonnegative int, indicating the count of objects, 1004 * followed by that many objects. 1005 * 1006 * @param oos the ObjectOutputStream to which data is written 1007 * @throws IOException if an I/O error occurs 1008 * @since 9 1009 */ 1010 private void writeObject(ObjectOutputStream oos) throws IOException { 1011 oos.defaultWriteObject(); 1012 oos.writeInt(array.length); 1013 for (int i = 0; i < array.length; i++) { 1014 oos.writeObject(array[i]); 1015 } 1016 } 1017 1018 /** 1019 * Creates and returns an immutable collection from this proxy class. 1020 * The instance returned is created as if by calling one of the 1021 * static factory methods for 1022 * <a href="List.html#immutable">List</a>, 1023 * <a href="Map.html#immutable">Map</a>, or 1024 * <a href="Set.html#immutable">Set</a>. 1025 * This proxy class is the serial form for all immutable collection instances, 1026 * regardless of implementation type. This is necessary to ensure that the 1027 * existence of any particular implementation type is kept out of the 1028 * serialized form. 1029 * 1030 * @return a collection created from this proxy object 1031 * @throws InvalidObjectException if the tag value is illegal or if an exception 1032 * is thrown during creation of the collection 1033 * @throws ObjectStreamException if another serialization error has occurred 1034 * @since 9 1035 */ 1036 private Object readResolve() throws ObjectStreamException { 1037 try { 1038 if (array == null) { 1039 throw new InvalidObjectException("null array"); 1040 } 1041 1042 // use low order 8 bits to indicate "kind" 1043 // ignore high order 24 bits 1044 switch (tag & 0xff) { 1045 case IMM_LIST: 1046 return List.of(array); 1047 case IMM_SET: 1048 return Set.of(array); 1049 case IMM_MAP: 1050 if (array.length == 0) { 1051 return ImmutableCollections.emptyMap(); 1052 } else if (array.length == 2) { 1053 return new ImmutableCollections.Map1<>(array[0], array[1]); 1054 } else { 1055 return new ImmutableCollections.MapN<>(array); 1056 } 1057 default: 1058 throw new InvalidObjectException(String.format("invalid flags 0x%x", tag)); 1059 } 1060 } catch (NullPointerException|IllegalArgumentException ex) { 1061 InvalidObjectException ioe = new InvalidObjectException("invalid object"); 1062 ioe.initCause(ex); 1063 throw ioe; 1064 } 1065 } 1066 }