mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8187443: Forest Consolidation: Move files to unified layout
Reviewed-by: darcy, ihse
This commit is contained in:
parent
270fe13182
commit
3789983e89
56923 changed files with 3 additions and 15727 deletions
|
@ -1,964 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
/**
|
||||
* Container class for immutable collections. Not part of the public API.
|
||||
* Mainly for namespace management and shared infrastructure.
|
||||
*
|
||||
* Serial warnings are suppressed throughout because all implementation
|
||||
* classes use a serial proxy and thus have no need to declare serialVersionUID.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class ImmutableCollections {
|
||||
/**
|
||||
* A "salt" value used for randomizing iteration order. This is initialized once
|
||||
* and stays constant for the lifetime of the JVM. It need not be truly random, but
|
||||
* it needs to vary sufficiently from one run to the next so that iteration order
|
||||
* will vary between JVM runs.
|
||||
*/
|
||||
static final int SALT;
|
||||
static {
|
||||
long nt = System.nanoTime();
|
||||
SALT = (int)((nt >>> 32) ^ nt);
|
||||
}
|
||||
|
||||
/** No instances. */
|
||||
private ImmutableCollections() { }
|
||||
|
||||
/**
|
||||
* The reciprocal of load factor. Given a number of elements
|
||||
* to store, multiply by this factor to get the table size.
|
||||
*/
|
||||
static final int EXPAND_FACTOR = 2;
|
||||
|
||||
static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
|
||||
|
||||
// ---------- List Implementations ----------
|
||||
|
||||
abstract static class AbstractImmutableList<E> extends AbstractList<E>
|
||||
implements RandomAccess, Serializable {
|
||||
@Override public boolean add(E e) { throw uoe(); }
|
||||
@Override public boolean addAll(Collection<? extends E> c) { throw uoe(); }
|
||||
@Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); }
|
||||
@Override public void clear() { throw uoe(); }
|
||||
@Override public boolean remove(Object o) { throw uoe(); }
|
||||
@Override public boolean removeAll(Collection<?> c) { throw uoe(); }
|
||||
@Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); }
|
||||
@Override public void replaceAll(UnaryOperator<E> operator) { throw uoe(); }
|
||||
@Override public boolean retainAll(Collection<?> c) { throw uoe(); }
|
||||
@Override public void sort(Comparator<? super E> c) { throw uoe(); }
|
||||
}
|
||||
|
||||
static final class List0<E> extends AbstractImmutableList<E> {
|
||||
private static final List0<?> INSTANCE = new List0<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> List0<T> instance() {
|
||||
return (List0<T>) INSTANCE;
|
||||
}
|
||||
|
||||
private List0() { }
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
Objects.checkIndex(index, 0); // always throws IndexOutOfBoundsException
|
||||
return null; // but the compiler doesn't know this
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
return new CollSer(CollSer.IMM_LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
Objects.requireNonNull(o);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> o) {
|
||||
return o.isEmpty(); // implicit nullcheck of o
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static final class List1<E> extends AbstractImmutableList<E> {
|
||||
@Stable
|
||||
private final E e0;
|
||||
|
||||
List1(E e0) {
|
||||
this.e0 = Objects.requireNonNull(e0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
Objects.checkIndex(index, 1);
|
||||
return e0;
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
return new CollSer(CollSer.IMM_LIST, e0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return o.equals(e0); // implicit nullcheck of o
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 + e0.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
static final class List2<E> extends AbstractImmutableList<E> {
|
||||
@Stable
|
||||
private final E e0;
|
||||
@Stable
|
||||
private final E e1;
|
||||
|
||||
List2(E e0, E e1) {
|
||||
this.e0 = Objects.requireNonNull(e0);
|
||||
this.e1 = Objects.requireNonNull(e1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
Objects.checkIndex(index, 2);
|
||||
if (index == 0) {
|
||||
return e0;
|
||||
} else { // index == 1
|
||||
return e1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return o.equals(e0) || o.equals(e1); // implicit nullcheck of o
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 31 + e0.hashCode();
|
||||
return 31 * hash + e1.hashCode();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
return new CollSer(CollSer.IMM_LIST, e0, e1);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ListN<E> extends AbstractImmutableList<E> {
|
||||
@Stable
|
||||
private final E[] elements;
|
||||
|
||||
@SafeVarargs
|
||||
ListN(E... input) {
|
||||
// copy and check manually to avoid TOCTOU
|
||||
@SuppressWarnings("unchecked")
|
||||
E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
|
||||
for (int i = 0; i < input.length; i++) {
|
||||
tmp[i] = Objects.requireNonNull(input[i]);
|
||||
}
|
||||
this.elements = tmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return elements.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
Objects.checkIndex(index, elements.length);
|
||||
return elements[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
for (E e : elements) {
|
||||
if (o.equals(e)) { // implicit nullcheck of o
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 1;
|
||||
for (E e : elements) {
|
||||
hash = 31 * hash + e.hashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
return new CollSer(CollSer.IMM_LIST, elements);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Set Implementations ----------
|
||||
|
||||
abstract static class AbstractImmutableSet<E> extends AbstractSet<E> implements Serializable {
|
||||
@Override public boolean add(E e) { throw uoe(); }
|
||||
@Override public boolean addAll(Collection<? extends E> c) { throw uoe(); }
|
||||
@Override public void clear() { throw uoe(); }
|
||||
@Override public boolean remove(Object o) { throw uoe(); }
|
||||
@Override public boolean removeAll(Collection<?> c) { throw uoe(); }
|
||||
@Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); }
|
||||
@Override public boolean retainAll(Collection<?> c) { throw uoe(); }
|
||||
}
|
||||
|
||||
static final class Set0<E> extends AbstractImmutableSet<E> {
|
||||
private static final Set0<?> INSTANCE = new Set0<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> Set0<T> instance() {
|
||||
return (Set0<T>) INSTANCE;
|
||||
}
|
||||
|
||||
private Set0() { }
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
Objects.requireNonNull(o);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> o) {
|
||||
return o.isEmpty(); // implicit nullcheck of o
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
return new CollSer(CollSer.IMM_SET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Set1<E> extends AbstractImmutableSet<E> {
|
||||
@Stable
|
||||
private final E e0;
|
||||
|
||||
Set1(E e0) {
|
||||
this.e0 = Objects.requireNonNull(e0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return o.equals(e0); // implicit nullcheck of o
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return Collections.singletonIterator(e0);
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
return new CollSer(CollSer.IMM_SET, e0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return e0.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
static final class Set2<E> extends AbstractImmutableSet<E> {
|
||||
@Stable
|
||||
final E e0;
|
||||
@Stable
|
||||
final E e1;
|
||||
|
||||
Set2(E e0, E e1) {
|
||||
if (e0.equals(Objects.requireNonNull(e1))) { // implicit nullcheck of e0
|
||||
throw new IllegalArgumentException("duplicate element: " + e0);
|
||||
}
|
||||
|
||||
if (SALT >= 0) {
|
||||
this.e0 = e0;
|
||||
this.e1 = e1;
|
||||
} else {
|
||||
this.e0 = e1;
|
||||
this.e1 = e0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return o.equals(e0) || o.equals(e1); // implicit nullcheck of o
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return e0.hashCode() + e1.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Iterator<E>() {
|
||||
private int idx = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return idx < 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
if (idx == 0) {
|
||||
idx = 1;
|
||||
return e0;
|
||||
} else if (idx == 1) {
|
||||
idx = 2;
|
||||
return e1;
|
||||
} else {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
return new CollSer(CollSer.IMM_SET, e0, e1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An array-based Set implementation. The element array must be strictly
|
||||
* larger than the size (the number of contained elements) so that at
|
||||
* least one null is always present.
|
||||
* @param <E> the element type
|
||||
*/
|
||||
static final class SetN<E> extends AbstractImmutableSet<E> {
|
||||
@Stable
|
||||
final E[] elements;
|
||||
@Stable
|
||||
final int size;
|
||||
|
||||
@SafeVarargs
|
||||
@SuppressWarnings("unchecked")
|
||||
SetN(E... input) {
|
||||
size = input.length; // implicit nullcheck of input
|
||||
|
||||
elements = (E[])new Object[EXPAND_FACTOR * input.length];
|
||||
for (int i = 0; i < input.length; i++) {
|
||||
E e = input[i];
|
||||
int idx = probe(e); // implicit nullcheck of e
|
||||
if (idx >= 0) {
|
||||
throw new IllegalArgumentException("duplicate element: " + e);
|
||||
} else {
|
||||
elements[-(idx + 1)] = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return probe(o) >= 0; // implicit nullcheck of o
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Iterator<E>() {
|
||||
private int idx = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
while (idx < elements.length) {
|
||||
if (elements[idx] != null)
|
||||
return true;
|
||||
idx++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
if (! hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return elements[idx++];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = 0;
|
||||
for (E e : elements) {
|
||||
if (e != null) {
|
||||
h += e.hashCode();
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
// returns index at which element is present; or if absent,
|
||||
// (-i - 1) where i is location where element should be inserted.
|
||||
// Callers are relying on this method to perform an implicit nullcheck
|
||||
// of pe
|
||||
private int probe(Object pe) {
|
||||
int idx = Math.floorMod(pe.hashCode() ^ SALT, elements.length);
|
||||
while (true) {
|
||||
E ee = elements[idx];
|
||||
if (ee == null) {
|
||||
return -idx - 1;
|
||||
} else if (pe.equals(ee)) {
|
||||
return idx;
|
||||
} else if (++idx == elements.length) {
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
Object[] array = new Object[size];
|
||||
int dest = 0;
|
||||
for (Object o : elements) {
|
||||
if (o != null) {
|
||||
array[dest++] = o;
|
||||
}
|
||||
}
|
||||
return new CollSer(CollSer.IMM_SET, array);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Map Implementations ----------
|
||||
|
||||
abstract static class AbstractImmutableMap<K,V> extends AbstractMap<K,V> implements Serializable {
|
||||
@Override public void clear() { throw uoe(); }
|
||||
@Override public V compute(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); }
|
||||
@Override public V computeIfAbsent(K key, Function<? super K,? extends V> mf) { throw uoe(); }
|
||||
@Override public V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); }
|
||||
@Override public V merge(K key, V value, BiFunction<? super V,? super V,? extends V> rf) { throw uoe(); }
|
||||
@Override public V put(K key, V value) { throw uoe(); }
|
||||
@Override public void putAll(Map<? extends K,? extends V> m) { throw uoe(); }
|
||||
@Override public V putIfAbsent(K key, V value) { throw uoe(); }
|
||||
@Override public V remove(Object key) { throw uoe(); }
|
||||
@Override public boolean remove(Object key, Object value) { throw uoe(); }
|
||||
@Override public V replace(K key, V value) { throw uoe(); }
|
||||
@Override public boolean replace(K key, V oldValue, V newValue) { throw uoe(); }
|
||||
@Override public void replaceAll(BiFunction<? super K,? super V,? extends V> f) { throw uoe(); }
|
||||
}
|
||||
|
||||
static final class Map0<K,V> extends AbstractImmutableMap<K,V> {
|
||||
private static final Map0<?,?> INSTANCE = new Map0<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <K,V> Map0<K,V> instance() {
|
||||
return (Map0<K,V>) INSTANCE;
|
||||
}
|
||||
|
||||
private Map0() { }
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<K,V>> entrySet() {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
Objects.requireNonNull(o);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
Objects.requireNonNull(o);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
return new CollSer(CollSer.IMM_MAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Map1<K,V> extends AbstractImmutableMap<K,V> {
|
||||
@Stable
|
||||
private final K k0;
|
||||
@Stable
|
||||
private final V v0;
|
||||
|
||||
Map1(K k0, V v0) {
|
||||
this.k0 = Objects.requireNonNull(k0);
|
||||
this.v0 = Objects.requireNonNull(v0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<K,V>> entrySet() {
|
||||
return Set.of(new KeyValueHolder<>(k0, v0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return o.equals(k0); // implicit nullcheck of o
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return o.equals(v0); // implicit nullcheck of o
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
return new CollSer(CollSer.IMM_MAP, k0, v0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return k0.hashCode() ^ v0.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An array-based Map implementation. There is a single array "table" that
|
||||
* contains keys and values interleaved: table[0] is kA, table[1] is vA,
|
||||
* table[2] is kB, table[3] is vB, etc. The table size must be even. It must
|
||||
* also be strictly larger than the size (the number of key-value pairs contained
|
||||
* in the map) so that at least one null key is always present.
|
||||
* @param <K> the key type
|
||||
* @param <V> the value type
|
||||
*/
|
||||
static final class MapN<K,V> extends AbstractImmutableMap<K,V> {
|
||||
@Stable
|
||||
final Object[] table; // pairs of key, value
|
||||
@Stable
|
||||
final int size; // number of pairs
|
||||
|
||||
MapN(Object... input) {
|
||||
if ((input.length & 1) != 0) { // implicit nullcheck of input
|
||||
throw new InternalError("length is odd");
|
||||
}
|
||||
size = input.length >> 1;
|
||||
|
||||
int len = EXPAND_FACTOR * input.length;
|
||||
len = (len + 1) & ~1; // ensure table is even length
|
||||
table = new Object[len];
|
||||
|
||||
for (int i = 0; i < input.length; i += 2) {
|
||||
@SuppressWarnings("unchecked")
|
||||
K k = Objects.requireNonNull((K)input[i]);
|
||||
@SuppressWarnings("unchecked")
|
||||
V v = Objects.requireNonNull((V)input[i+1]);
|
||||
int idx = probe(k);
|
||||
if (idx >= 0) {
|
||||
throw new IllegalArgumentException("duplicate key: " + k);
|
||||
} else {
|
||||
int dest = -(idx + 1);
|
||||
table[dest] = k;
|
||||
table[dest+1] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return probe(o) >= 0; // implicit nullcheck of o
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
for (int i = 1; i < table.length; i += 2) {
|
||||
Object v = table[i];
|
||||
if (v != null && o.equals(v)) { // implicit nullcheck of o
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
for (int i = 0; i < table.length; i += 2) {
|
||||
Object k = table[i];
|
||||
if (k != null) {
|
||||
hash += k.hashCode() ^ table[i + 1].hashCode();
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public V get(Object o) {
|
||||
int i = probe(o);
|
||||
if (i >= 0) {
|
||||
return (V)table[i+1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<K,V>> entrySet() {
|
||||
return new AbstractSet<Map.Entry<K,V>>() {
|
||||
@Override
|
||||
public int size() {
|
||||
return MapN.this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K,V>> iterator() {
|
||||
return new Iterator<Map.Entry<K,V>>() {
|
||||
int idx = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
while (idx < table.length) {
|
||||
if (table[idx] != null)
|
||||
return true;
|
||||
idx += 2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<K,V> next() {
|
||||
if (hasNext()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map.Entry<K,V> e =
|
||||
new KeyValueHolder<>((K)table[idx], (V)table[idx+1]);
|
||||
idx += 2;
|
||||
return e;
|
||||
} else {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// returns index at which the probe key is present; or if absent,
|
||||
// (-i - 1) where i is location where element should be inserted.
|
||||
// Callers are relying on this method to perform an implicit nullcheck
|
||||
// of pk.
|
||||
private int probe(Object pk) {
|
||||
int idx = Math.floorMod(pk.hashCode() ^ SALT, table.length >> 1) << 1;
|
||||
while (true) {
|
||||
@SuppressWarnings("unchecked")
|
||||
K ek = (K)table[idx];
|
||||
if (ek == null) {
|
||||
return -idx - 1;
|
||||
} else if (pk.equals(ek)) {
|
||||
return idx;
|
||||
} else if ((idx += 2) == table.length) {
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("not serial proxy");
|
||||
}
|
||||
|
||||
private Object writeReplace() {
|
||||
Object[] array = new Object[2 * size];
|
||||
int len = table.length;
|
||||
int dest = 0;
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
if (table[i] != null) {
|
||||
array[dest++] = table[i];
|
||||
array[dest++] = table[i+1];
|
||||
}
|
||||
}
|
||||
return new CollSer(CollSer.IMM_MAP, array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Serialization Proxy ----------
|
||||
|
||||
/**
|
||||
* A unified serialization proxy class for the immutable collections.
|
||||
*
|
||||
* @serial
|
||||
* @since 9
|
||||
*/
|
||||
final class CollSer implements Serializable {
|
||||
private static final long serialVersionUID = 6309168927139932177L;
|
||||
|
||||
static final int IMM_LIST = 1;
|
||||
static final int IMM_SET = 2;
|
||||
static final int IMM_MAP = 3;
|
||||
|
||||
/**
|
||||
* Indicates the type of collection that is serialized.
|
||||
* The low order 8 bits have the value 1 for an immutable
|
||||
* {@code List}, 2 for an immutable {@code Set}, and 3 for
|
||||
* an immutable {@code Map}. Any other value causes an
|
||||
* {@link InvalidObjectException} to be thrown. The high
|
||||
* order 24 bits are zero when an instance is serialized,
|
||||
* and they are ignored when an instance is deserialized.
|
||||
* They can thus be used by future implementations without
|
||||
* causing compatibility issues.
|
||||
*
|
||||
* <p>The tag value also determines the interpretation of the
|
||||
* transient {@code Object[] array} field.
|
||||
* For {@code List} and {@code Set}, the array's length is the size
|
||||
* of the collection, and the array contains the elements of the collection.
|
||||
* Null elements are not allowed. For {@code Set}, duplicate elements
|
||||
* are not allowed.
|
||||
*
|
||||
* <p>For {@code Map}, the array's length is twice the number of mappings
|
||||
* present in the map. The array length is necessarily even.
|
||||
* The array contains a succession of key and value pairs:
|
||||
* {@code k1, v1, k2, v2, ..., kN, vN.} Nulls are not allowed,
|
||||
* and duplicate keys are not allowed.
|
||||
*
|
||||
* @serial
|
||||
* @since 9
|
||||
*/
|
||||
private final int tag;
|
||||
|
||||
/**
|
||||
* @serial
|
||||
* @since 9
|
||||
*/
|
||||
private transient Object[] array;
|
||||
|
||||
CollSer(int t, Object... a) {
|
||||
tag = t;
|
||||
array = a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads objects from the stream and stores them
|
||||
* in the transient {@code Object[] array} field.
|
||||
*
|
||||
* @serialData
|
||||
* A nonnegative int, indicating the count of objects,
|
||||
* followed by that many objects.
|
||||
*
|
||||
* @param ois the ObjectInputStream from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
* @throws InvalidObjectException if the count is negative
|
||||
* @since 9
|
||||
*/
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
ois.defaultReadObject();
|
||||
int len = ois.readInt();
|
||||
|
||||
if (len < 0) {
|
||||
throw new InvalidObjectException("negative length " + len);
|
||||
}
|
||||
|
||||
Object[] a = new Object[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
a[i] = ois.readObject();
|
||||
}
|
||||
|
||||
array = a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes objects to the stream from
|
||||
* the transient {@code Object[] array} field.
|
||||
*
|
||||
* @serialData
|
||||
* A nonnegative int, indicating the count of objects,
|
||||
* followed by that many objects.
|
||||
*
|
||||
* @param oos the ObjectOutputStream to which data is written
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @since 9
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream oos) throws IOException {
|
||||
oos.defaultWriteObject();
|
||||
oos.writeInt(array.length);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
oos.writeObject(array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an immutable collection from this proxy class.
|
||||
* The instance returned is created as if by calling one of the
|
||||
* static factory methods for
|
||||
* <a href="List.html#immutable">List</a>,
|
||||
* <a href="Map.html#immutable">Map</a>, or
|
||||
* <a href="Set.html#immutable">Set</a>.
|
||||
* This proxy class is the serial form for all immutable collection instances,
|
||||
* regardless of implementation type. This is necessary to ensure that the
|
||||
* existence of any particular implementation type is kept out of the
|
||||
* serialized form.
|
||||
*
|
||||
* @return a collection created from this proxy object
|
||||
* @throws InvalidObjectException if the tag value is illegal or if an exception
|
||||
* is thrown during creation of the collection
|
||||
* @throws ObjectStreamException if another serialization error has occurred
|
||||
* @since 9
|
||||
*/
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
try {
|
||||
if (array == null) {
|
||||
throw new InvalidObjectException("null array");
|
||||
}
|
||||
|
||||
// use low order 8 bits to indicate "kind"
|
||||
// ignore high order 24 bits
|
||||
switch (tag & 0xff) {
|
||||
case IMM_LIST:
|
||||
return List.of(array);
|
||||
case IMM_SET:
|
||||
return Set.of(array);
|
||||
case IMM_MAP:
|
||||
if (array.length == 0) {
|
||||
return ImmutableCollections.Map0.instance();
|
||||
} else if (array.length == 2) {
|
||||
return new ImmutableCollections.Map1<>(array[0], array[1]);
|
||||
} else {
|
||||
return new ImmutableCollections.MapN<>(array);
|
||||
}
|
||||
default:
|
||||
throw new InvalidObjectException(String.format("invalid flags 0x%x", tag));
|
||||
}
|
||||
} catch (NullPointerException|IllegalArgumentException ex) {
|
||||
InvalidObjectException ioe = new InvalidObjectException("invalid object");
|
||||
ioe.initCause(ex);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue