< prev index next >
src/java.base/share/classes/java/util/ImmutableCollections.java
Print this page
rev 47862 : imported patch immu0
rev 47863 : imported patch listn
@@ -57,10 +57,12 @@
static {
long nt = System.nanoTime();
SALT = (int)((nt >>> 32) ^ nt);
}
+ private static final Object NULL = new Object();
+
/** No instances. */
private ImmutableCollections() { }
/**
* The reciprocal of load factor. Given a number of elements
@@ -70,270 +72,434 @@
static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
// ---------- List Implementations ----------
- abstract static class AbstractImmutableList<E> extends AbstractList<E>
- implements RandomAccess, Serializable {
+ static final List<?> EMPTY_LIST = new ListN<>();
+
+ static final Set<?> EMPTY_SET = new SetN<>();
+
+ static final Map<?,?> EMPTY_MAP = new MapN<>();
+
+ @SuppressWarnings("unchecked")
+ static <T> List<T> emptyList() {
+ return (List<T>) EMPTY_LIST;
+ }
+
+ @SuppressWarnings("unchecked")
+ static <T> Set<T> emptySet() {
+ return (Set<T>) EMPTY_SET;
+ }
+
+ @SuppressWarnings("unchecked")
+ static <K,V> Map<K,V> emptyMap() {
+ return (Map<K,V>) EMPTY_MAP;
+ }
+
+ static final class ListN<E> extends AbstractCollection<E>
+ implements List<E>, RandomAccess, Serializable {
+
+ // all mutating methods throw UnsupportedOperationException
@Override public boolean add(E e) { throw uoe(); }
+ @Override public void add(int index, E element) { 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 E remove(int index) { 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 E set(int index, E element) { 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;
+ public int indexOf(Object o) {
+ Objects.requireNonNull(o);
+ ListIterator<E> it = listIterator();
+ while (it.hasNext()) {
+ if (o.equals(it.next())) {
+ return it.previousIndex();
+ }
+ }
+ return -1;
}
@Override
- public E get(int index) {
- Objects.checkIndex(index, 0); // always throws IndexOutOfBoundsException
- return null; // but the compiler doesn't know this
+ public int lastIndexOf(Object o) {
+ Objects.requireNonNull(o);
+ ListIterator<E> it = listIterator();
+ while (it.hasNext()) {
+ if (o.equals(it.next())) {
+ return it.previousIndex();
+ }
+ }
+ return -1;
}
@Override
- public Iterator<E> iterator() {
- return Collections.emptyIterator();
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
}
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- throw new InvalidObjectException("not serial proxy");
+ if (!(o instanceof List)) {
+ return false;
}
- private Object writeReplace() {
- return new CollSer(CollSer.IMM_LIST);
+ if (!(o instanceof ListN)) {
+ ListN<?> other = (ListN<?>)o;
+ return this.e0.equals(other.e0) && this.e1.equals(e1);
}
- @Override
- public boolean contains(Object o) {
- Objects.requireNonNull(o);
+ ListIterator<E> e1 = listIterator();
+ ListIterator<?> e2 = ((List<?>) o).listIterator();
+ while (e1.hasNext() && e2.hasNext()) {
+ E o1 = e1.next();
+ Object o2 = e2.next();
+ if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
-
- @Override
- public boolean containsAll(Collection<?> o) {
- return o.isEmpty(); // implicit nullcheck of o
+ return !(e1.hasNext() || e2.hasNext());
}
@Override
- public int hashCode() {
- return 1;
+ public List<E> subList(int fromIndex, int toIndex) {
+ int size = size();
+ Objects.checkFromToIndex(fromIndex, toIndex, size);
+ if (size == 0 || fromIndex == toIndex) {
+ return emptyList();
+ } else if (size == 1) {
+ // checks above deal with corner cases subList(0,0) and subList(1,1)
+ // that would return the empty list
+ assert(fromIndex == 0 && toIndex == 1);
+ return this;
+ } else {
+ return new SubList<E>(this, fromIndex, toIndex);
}
}
- static final class List1<E> extends AbstractImmutableList<E> {
- @Stable
- private final E e0;
+ private static class SubList<E> extends AbstractList<E> implements RandomAccess {
+ private final List<E> root;
+ private final int offset;
+ int size;
- List1(E e0) {
- this.e0 = Objects.requireNonNull(e0);
+ // all mutating methods throw UnsupportedOperationException
+ @Override public boolean add(E e) { throw uoe(); }
+ @Override public void add(int index, E element) { 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 E remove(int index) { 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 E set(int index, E element) { throw uoe(); }
+ @Override public void sort(Comparator<? super E> c) { throw uoe(); }
+
+ /**
+ * Constructs a sublist of an arbitrary AbstractList, which is
+ * not a SubList itself.
+ */
+ public SubList(List<E> root, int fromIndex, int toIndex) {
+ this.root = root;
+ this.offset = fromIndex;
+ this.size = toIndex - fromIndex;
}
- @Override
- public int size() {
- return 1;
+ /**
+ * Constructs a sublist of another SubList.
+ */
+ protected SubList(SubList<E> parent, int fromIndex, int toIndex) {
+ this.root = parent.root;
+ this.offset = parent.offset + fromIndex;
+ this.size = toIndex - fromIndex;
}
- @Override
public E get(int index) {
- Objects.checkIndex(index, 1);
- return e0;
+ Objects.checkIndex(index, size);
+ return root.get(offset + index);
}
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- throw new InvalidObjectException("not serial proxy");
+ public int size() {
+ return size;
}
- private Object writeReplace() {
- return new CollSer(CollSer.IMM_LIST, e0);
+ protected void removeRange(int fromIndex, int toIndex) {
+ throw uoe();
}
- @Override
- public boolean contains(Object o) {
- return o.equals(e0); // implicit nullcheck of o
+ public Iterator<E> iterator() {
+ return listIterator();
}
- @Override
- public int hashCode() {
- return 31 + e0.hashCode();
- }
+ public ListIterator<E> listIterator(int index) {
+ rangeCheck(index);
+
+ return new ListIterator<E>() {
+ private final ListIterator<E> i =
+ root.listIterator(offset + index);
+
+ public boolean hasNext() {
+ return nextIndex() < size;
}
- static final class List2<E> extends AbstractImmutableList<E> {
- @Stable
- private final E e0;
- @Stable
- private final E e1;
+ public E next() {
+ if (hasNext())
+ return i.next();
+ else
+ throw new NoSuchElementException();
+ }
- List2(E e0, E e1) {
- this.e0 = Objects.requireNonNull(e0);
- this.e1 = Objects.requireNonNull(e1);
+ public boolean hasPrevious() {
+ return previousIndex() >= 0;
}
- @Override
- public int size() {
- return 2;
+ public E previous() {
+ if (hasPrevious())
+ return i.previous();
+ else
+ throw new NoSuchElementException();
}
- @Override
- public E get(int index) {
- Objects.checkIndex(index, 2);
- if (index == 0) {
- return e0;
- } else { // index == 1
- return e1;
+ public int nextIndex() {
+ return i.nextIndex() - offset;
}
+
+ public int previousIndex() {
+ return i.previousIndex() - offset;
}
- @Override
- public boolean contains(Object o) {
- return o.equals(e0) || o.equals(e1); // implicit nullcheck of o
+ public void remove() { throw uoe(); }
+ public void set(E e) { throw uoe(); }
+ public void add(E e) { throw uoe(); }
+ };
}
- @Override
- public int hashCode() {
- int hash = 31 + e0.hashCode();
- return 31 * hash + e1.hashCode();
+ public List<E> subList(int fromIndex, int toIndex) {
+ subListRangeCheck(fromIndex, toIndex, size);
+ return new SubList<>(this, fromIndex, toIndex);
}
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- throw new InvalidObjectException("not serial proxy");
+ private void rangeCheck(int index) {
+ if (index < 0 || index > size)
+ throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
- private Object writeReplace() {
- return new CollSer(CollSer.IMM_LIST, e0, e1);
+ private String outOfBoundsMsg(int index) {
+ return "Index: "+index+", Size: "+size;
}
}
- static final class ListN<E> extends AbstractImmutableList<E> {
@Stable
- private final E[] elements;
+ private final Object e0;
+
+ @Stable
+ private final Object e1;
+
+ private final int size;
@SafeVarargs
ListN(E... input) {
+ if (input.length == 1) {
+ e0 = Objects.requireNonNull(input[0]);
+ e1 = NULL;
+ size = 1;
+ } else if (input.length == 2) {
+ e0 = Objects.requireNonNull(input[0]);
+ e1 = Objects.requireNonNull(input[1]);
+ size = 2;
+ } else {
+ e0 = NULL;
// 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;
+ this.e1 = tmp;
+ size = input.length;
+ }
}
@Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
public int size() {
- return elements.length;
+ return size;
}
@Override
+ @SuppressWarnings("unchecked")
public E get(int index) {
- Objects.checkIndex(index, elements.length);
- return elements[index];
+ int size = size();
+ Objects.checkIndex(index, size); // IOOBE if size == 0, or size == 1 and index == 1
+ if (size <= 2) {
+ if (index == 0) {
+ return (E)e0;
+ } else {
+ return (E)e1;
+ }
+ } else {
+ return ((E[]) e1)[index];
+ }
}
@Override
+ @SuppressWarnings("unchecked")
public boolean contains(Object o) {
- for (E e : elements) {
+ int size = size();
+ Objects.requireNonNull(o);
+ if (size == 1) {
+ return o.equals(e0);
+ } else if (size == 2) {
+ return o.equals(e0) || o.equals(e1);
+ } else {
+ for (E e : (E[])e1) {
if (o.equals(e)) { // implicit nullcheck of o
return true;
}
}
return false;
}
+ }
@Override
+ @SuppressWarnings("unchecked")
public int hashCode() {
+ int size = size();
+ if (size == 1) {
+ return 31 + e0.hashCode();
+ } else if (size == 2) {
+ return (31 + e0.hashCode()) * 31 + e1.hashCode();
+ }
+
int hash = 1;
- for (E e : elements) {
+ for (E e : (E[])e1) {
hash = 31 * hash + e.hashCode();
}
return hash;
}
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- throw new InvalidObjectException("not serial proxy");
+ @Override
+ public Iterator<E> iterator() {
+ return new Itr();
}
- private Object writeReplace() {
- return new CollSer(CollSer.IMM_LIST, elements);
+ @Override
+ public ListIterator<E> listIterator() {
+ return listIterator(0);
}
+
+ @Override
+ public ListIterator<E> listIterator(final int index) {
+ Objects.checkIndex(index, size());
+ return new ListItr(index);
}
- // ---------- Set Implementations ----------
+ private class Itr implements Iterator<E> {
+ Itr() {
+ size = size();
+ }
- 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(); }
+ int cursor = 0;
+
+ private final int size;
+
+ public boolean hasNext() {
+ return cursor != size;
}
- static final class Set0<E> extends AbstractImmutableSet<E> {
- private static final Set0<?> INSTANCE = new Set0<>();
+ public E next() {
+ try {
+ int i = cursor;
+ E next = get(i);
+ cursor = i + 1;
+ return next;
+ } catch (IndexOutOfBoundsException e) {
+ throw new NoSuchElementException();
+ }
+ }
- @SuppressWarnings("unchecked")
- static <T> Set0<T> instance() {
- return (Set0<T>) INSTANCE;
+ public void remove() {
+ throw uoe();
+ }
}
- private Set0() { }
+ private class ListItr extends Itr implements ListIterator<E> {
+ ListItr(int index) {
+ cursor = index;
+ }
- @Override
- public int size() {
- return 0;
+ public boolean hasPrevious() {
+ return cursor != 0;
}
- @Override
- public boolean contains(Object o) {
- Objects.requireNonNull(o);
- return false;
+ public E previous() {
+ try {
+ int i = cursor - 1;
+ E previous = get(i);
+ cursor = i;
+ return previous;
+ } catch (IndexOutOfBoundsException e) {
+ throw new NoSuchElementException();
+ }
}
- @Override
- public boolean containsAll(Collection<?> o) {
- return o.isEmpty(); // implicit nullcheck of o
+ public int nextIndex() {
+ return cursor;
}
- @Override
- public Iterator<E> iterator() {
- return Collections.emptyIterator();
+ public int previousIndex() {
+ return cursor - 1;
+ }
+
+ public void set(E e) {
+ throw uoe();
+ }
+
+ public void add(E e) {
+ throw uoe();
+ }
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new InvalidObjectException("not serial proxy");
}
+ @SuppressWarnings("unchecked")
private Object writeReplace() {
- return new CollSer(CollSer.IMM_SET);
+ int size = size();
+ if (size == 1) {
+ return new CollSer(CollSer.IMM_LIST, (E)e0);
+ } else if (size == 2) {
+ return new CollSer(CollSer.IMM_LIST, (E)e0, (E)e1);
+ } else {
+ return new CollSer(CollSer.IMM_LIST, (E[])e1);
}
-
- @Override
- public int hashCode() {
- return 0;
}
}
+ // ---------- 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 Set1<E> extends AbstractImmutableSet<E> {
@Stable
private final E e0;
Set1(E e0) {
@@ -367,79 +533,10 @@
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
@@ -472,11 +569,12 @@
return size;
}
@Override
public boolean contains(Object o) {
- return probe(o) >= 0; // implicit nullcheck of o
+ Objects.requireNonNull(0);
+ return size > 0 && probe(o) >= 0; // implicit nullcheck of o
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
@@ -563,51 +661,10 @@
@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;
@@ -656,11 +713,10 @@
* @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");
@@ -687,11 +743,12 @@
}
}
@Override
public boolean containsKey(Object o) {
- return probe(o) >= 0; // implicit nullcheck of o
+ Objects.requireNonNull(0);
+ return size > 0 && probe(o) >= 0;
}
@Override
public boolean containsValue(Object o) {
for (int i = 1; i < table.length; i += 2) {
@@ -716,10 +773,13 @@
}
@Override
@SuppressWarnings("unchecked")
public V get(Object o) {
+ if (size == 0) {
+ return null;
+ }
int i = probe(o);
if (i >= 0) {
return (V)table[i+1];
} else {
return null;
@@ -946,11 +1006,11 @@
return List.of(array);
case IMM_SET:
return Set.of(array);
case IMM_MAP:
if (array.length == 0) {
- return ImmutableCollections.Map0.instance();
+ return ImmutableCollections.emptyMap();
} else if (array.length == 2) {
return new ImmutableCollections.Map1<>(array[0], array[1]);
} else {
return new ImmutableCollections.MapN<>(array);
}
< prev index next >