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 }