提交 f0907e25 编写于 作者: P psandoz

8011426: java.util collection Spliterator implementations

Summary: Spliterator implementations for collection classes in java.util.
Reviewed-by: mduigou, briangoetz
Contributed-by: NDoug Lea &lt;dl@cs.oswego.edu&gt;, Paul Sandoz <paul.sandoz@oracle.com>
上级 053e0cf5
...@@ -29,6 +29,10 @@ import java.util.function.Consumer; ...@@ -29,6 +29,10 @@ import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/** /**
* Resizable-array implementation of the <tt>List</tt> interface. Implements * Resizable-array implementation of the <tt>List</tt> interface. Implements
* all optional list operations, and permits all elements, including * all optional list operations, and permits all elements, including
...@@ -124,7 +128,7 @@ public class ArrayList<E> extends AbstractList<E> ...@@ -124,7 +128,7 @@ public class ArrayList<E> extends AbstractList<E>
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added. * DEFAULT_CAPACITY when the first element is added.
*/ */
private transient Object[] elementData; transient Object[] elementData; // non-private to simplify nested class access
/** /**
* The size of the ArrayList (the number of elements it contains). * The size of the ArrayList (the number of elements it contains).
...@@ -857,6 +861,27 @@ public class ArrayList<E> extends AbstractList<E> ...@@ -857,6 +861,27 @@ public class ArrayList<E> extends AbstractList<E>
} }
} }
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
lastRet = cursor = i;
checkForComodification();
}
final void checkForComodification() { final void checkForComodification() {
if (modCount != expectedModCount) if (modCount != expectedModCount)
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
...@@ -1092,6 +1117,26 @@ public class ArrayList<E> extends AbstractList<E> ...@@ -1092,6 +1117,26 @@ public class ArrayList<E> extends AbstractList<E>
return (E) elementData[offset + (lastRet = i)]; return (E) elementData[offset + (lastRet = i)];
} }
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = SubList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[offset + (i++)]);
}
// update once at end of iteration to reduce heap write traffic
lastRet = cursor = i;
checkForComodification();
}
public int nextIndex() { public int nextIndex() {
return cursor; return cursor;
} }
...@@ -1171,6 +1216,12 @@ public class ArrayList<E> extends AbstractList<E> ...@@ -1171,6 +1216,12 @@ public class ArrayList<E> extends AbstractList<E>
if (ArrayList.this.modCount != this.modCount) if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
} }
public Spliterator<E> spliterator() {
checkForComodification();
return new ArrayListSpliterator<E>(ArrayList.this, offset,
offset + this.size, this.modCount);
}
} }
@Override @Override
...@@ -1188,6 +1239,128 @@ public class ArrayList<E> extends AbstractList<E> ...@@ -1188,6 +1239,128 @@ public class ArrayList<E> extends AbstractList<E>
} }
} }
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
/** Index-based split-by-two, lazily initialized Spliterator */
static final class ArrayListSpliterator<E> implements Spliterator<E> {
/*
* If ArrayLists were immutable, or structurally immutable (no
* adds, removes, etc), we could implement their spliterators
* with Arrays.spliterator. Instead we detect as much
* interference during traversal as practical without
* sacrificing much performance. We rely primarily on
* modCounts. These are not guaranteed to detect concurrency
* violations, and are sometimes overly conservative about
* within-thread interference, but detect enough problems to
* be worthwhile in practice. To carry this out, we (1) lazily
* initialize fence and expectedModCount until the latest
* point that we need to commit to the state we are checking
* against; thus improving precision. (This doesn't apply to
* SubLists, that create spliterators with current non-lazy
* values). (2) We perform only a single
* ConcurrentModificationException check at the end of forEach
* (the most performance-sensitive method). When using forEach
* (as opposed to iterators), we can normally only detect
* interference after actions, not before. Further
* CME-triggering checks apply to all other possible
* violations of assumptions for example null or too-small
* elementData array given its size(), that could only have
* occurred due to interference. This allows the inner loop
* of forEach to run without any further checks, and
* simplifies lambda-resolution. While this does entail a
* number of checks, note that in the common case of
* list.stream().forEach(a), no checks or other computation
* occur anywhere other than inside forEach itself. The other
* less-often-used methods cannot take advantage of most of
* these streamlinings.
*/
private final ArrayList<E> list;
private int index; // current index, modified on advance/split
private int fence; // -1 until used; then one past last index
private int expectedModCount; // initialized when fence set
/** Create new spliterator covering the given range */
ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
int expectedModCount) {
this.list = list; // OK if null unless traversed
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
private int getFence() { // initialize fence to size on first use
int hi; // (a specialized variant appears in method forEach)
ArrayList<E> lst;
if ((hi = fence) < 0) {
if ((lst = list) == null)
hi = fence = 0;
else {
expectedModCount = lst.modCount;
hi = fence = lst.size;
}
}
return hi;
}
public ArrayListSpliterator<E> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null : // divide range in half unless too small
new ArrayListSpliterator<E>(list, lo, index = mid,
expectedModCount);
}
public boolean tryAdvance(Consumer<? super E> action) {
if (action == null)
throw new NullPointerException();
int hi = getFence(), i = index;
if (i < hi) {
index = i + 1;
@SuppressWarnings("unchecked") E e = (E)list.elementData[i];
action.accept(e);
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
public void forEachRemaining(Consumer<? super E> action) {
int i, hi, mc; // hoist accesses and checks from loop
ArrayList<E> lst; Object[] a;
if (action == null)
throw new NullPointerException();
if ((lst = list) != null && (a = lst.elementData) != null) {
if ((hi = fence) < 0) {
mc = lst.modCount;
hi = lst.size;
}
else
mc = expectedModCount;
if ((i = index) >= 0 && (index = hi) <= a.length) {
for (; i < hi; ++i) {
@SuppressWarnings("unchecked") E e = (E) a[i];
action.accept(e);
}
if (lst.modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
public long estimateSize() {
return (long) (getFence() - index);
}
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
@Override @Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter); Objects.requireNonNull(filter);
......
...@@ -1088,6 +1088,11 @@ public class Collections { ...@@ -1088,6 +1088,11 @@ public class Collections {
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public void forEachRemaining(Consumer<? super E> action) {
// Use backing collection version
i.forEachRemaining(action);
}
}; };
} }
...@@ -1114,6 +1119,7 @@ public class Collections { ...@@ -1114,6 +1119,7 @@ public class Collections {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
// Override default methods in Collection
@Override @Override
public void forEach(Consumer<? super E> action) { public void forEach(Consumer<? super E> action) {
c.forEach(action); c.forEach(action);
...@@ -1122,6 +1128,11 @@ public class Collections { ...@@ -1122,6 +1128,11 @@ public class Collections {
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public Spliterator<E> spliterator() {
return (Spliterator<E>)c.spliterator();
}
} }
/** /**
...@@ -1285,6 +1296,11 @@ public class Collections { ...@@ -1285,6 +1296,11 @@ public class Collections {
public void add(E e) { public void add(E e) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public void forEachRemaining(Consumer<? super E> action) {
i.forEachRemaining(action);
}
}; };
} }
...@@ -1664,7 +1680,8 @@ public class Collections { ...@@ -1664,7 +1680,8 @@ public class Collections {
* through the returned collection.<p> * through the returned collection.<p>
* *
* It is imperative that the user manually synchronize on the returned * It is imperative that the user manually synchronize on the returned
* collection when iterating over it: * collection when traversing it via {@link Iterator} or
* {@link Spliterator}:
* <pre> * <pre>
* Collection c = Collections.synchronizedCollection(myCollection); * Collection c = Collections.synchronizedCollection(myCollection);
* ... * ...
...@@ -1761,18 +1778,22 @@ public class Collections { ...@@ -1761,18 +1778,22 @@ public class Collections {
public String toString() { public String toString() {
synchronized (mutex) {return c.toString();} synchronized (mutex) {return c.toString();}
} }
private void writeObject(ObjectOutputStream s) throws IOException { // Override default methods in Collection
synchronized (mutex) {s.defaultWriteObject();}
}
@Override @Override
public void forEach(Consumer<? super E> action) { public void forEach(Consumer<? super E> consumer) {
synchronized (mutex) {c.forEach(action);} synchronized (mutex) {c.forEach(consumer);}
} }
@Override @Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
synchronized (mutex) {return c.removeIf(filter);} synchronized (mutex) {return c.removeIf(filter);}
} }
@Override
public Spliterator<E> spliterator() {
return c.spliterator(); // Must be manually synched by user!
}
private void writeObject(ObjectOutputStream s) throws IOException {
synchronized (mutex) {s.defaultWriteObject();}
}
} }
/** /**
...@@ -2533,14 +2554,15 @@ public class Collections { ...@@ -2533,14 +2554,15 @@ public class Collections {
return c.addAll(checkedCopyOf(coll)); return c.addAll(checkedCopyOf(coll));
} }
// Override default methods in Collection
@Override @Override
public void forEach(Consumer<? super E> action) { public void forEach(Consumer<? super E> action) {c.forEach(action);}
c.forEach(action);
}
@Override @Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
return c.removeIf(filter); return c.removeIf(filter);
} }
@Override
public Spliterator<E> spliterator() {return c.spliterator();}
} }
/** /**
...@@ -2796,6 +2818,11 @@ public class Collections { ...@@ -2796,6 +2818,11 @@ public class Collections {
typeCheck(e); typeCheck(e);
i.add(e); i.add(e);
} }
@Override
public void forEachRemaining(Consumer<? super E> action) {
i.forEachRemaining(action);
}
}; };
} }
...@@ -3334,6 +3361,10 @@ public class Collections { ...@@ -3334,6 +3361,10 @@ public class Collections {
public boolean hasNext() { return false; } public boolean hasNext() { return false; }
public E next() { throw new NoSuchElementException(); } public E next() { throw new NoSuchElementException(); }
public void remove() { throw new IllegalStateException(); } public void remove() { throw new IllegalStateException(); }
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
}
} }
/** /**
...@@ -3474,6 +3505,7 @@ public class Collections { ...@@ -3474,6 +3505,7 @@ public class Collections {
return a; return a;
} }
// Override default methods in Collection
@Override @Override
public void forEach(Consumer<? super E> action) { public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action); Objects.requireNonNull(action);
...@@ -3483,6 +3515,8 @@ public class Collections { ...@@ -3483,6 +3515,8 @@ public class Collections {
Objects.requireNonNull(filter); Objects.requireNonNull(filter);
return false; return false;
} }
@Override
public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
// Preserves singleton property // Preserves singleton property
private Object readResolve() { private Object readResolve() {
...@@ -3592,15 +3626,20 @@ public class Collections { ...@@ -3592,15 +3626,20 @@ public class Collections {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
// Override default methods in Collection
@Override @Override
public void forEach(Consumer<? super E> action) { public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action); Objects.requireNonNull(action);
} }
@Override @Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter); Objects.requireNonNull(filter);
return false; return false;
} }
@Override
public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
} }
/** /**
...@@ -3670,10 +3709,6 @@ public class Collections { ...@@ -3670,10 +3709,6 @@ public class Collections {
public int hashCode() { return 1; } public int hashCode() { return 1; }
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
}
@Override @Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter); Objects.requireNonNull(filter);
...@@ -3688,6 +3723,15 @@ public class Collections { ...@@ -3688,6 +3723,15 @@ public class Collections {
Objects.requireNonNull(c); Objects.requireNonNull(c);
} }
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
}
@Override
public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
// Preserves singleton property // Preserves singleton property
private Object readResolve() { private Object readResolve() {
return EMPTY_LIST; return EMPTY_LIST;
...@@ -3843,6 +3887,60 @@ public class Collections { ...@@ -3843,6 +3887,60 @@ public class Collections {
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
if (hasNext) {
action.accept(e);
hasNext = false;
}
}
};
}
/**
* Creates a {@code Spliterator} with only the specified element
*
* @param <T> Type of elements
* @return A singleton {@code Spliterator}
*/
static <T> Spliterator<T> singletonSpliterator(final T element) {
return new Spliterator<T>() {
long est = 1;
@Override
public Spliterator<T> trySplit() {
return null;
}
@Override
public boolean tryAdvance(Consumer<? super T> consumer) {
Objects.requireNonNull(consumer);
if (est > 0) {
est--;
consumer.accept(element);
return true;
}
return false;
}
@Override
public void forEachRemaining(Consumer<? super T> consumer) {
tryAdvance(consumer);
}
@Override
public long estimateSize() {
return est;
}
@Override
public int characteristics() {
int value = (element != null) ? Spliterator.NONNULL : 0;
return value | Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.IMMUTABLE |
Spliterator.DISTINCT | Spliterator.ORDERED;
}
}; };
} }
...@@ -3867,11 +3965,16 @@ public class Collections { ...@@ -3867,11 +3965,16 @@ public class Collections {
public boolean contains(Object o) {return eq(o, element);} public boolean contains(Object o) {return eq(o, element);}
// Override default methods for Collection
@Override @Override
public void forEach(Consumer<? super E> action) { public void forEach(Consumer<? super E> action) {
action.accept(element); action.accept(element);
} }
@Override @Override
public Spliterator<E> spliterator() {
return singletonSpliterator(element);
}
@Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
...@@ -3916,6 +4019,7 @@ public class Collections { ...@@ -3916,6 +4019,7 @@ public class Collections {
return element; return element;
} }
// Override default methods for Collection
@Override @Override
public void forEach(Consumer<? super E> action) { public void forEach(Consumer<? super E> action) {
action.accept(element); action.accept(element);
...@@ -3931,6 +4035,10 @@ public class Collections { ...@@ -3931,6 +4035,10 @@ public class Collections {
@Override @Override
public void sort(Comparator<? super E> c) { public void sort(Comparator<? super E> c) {
} }
@Override
public Spliterator<E> spliterator() {
return singletonSpliterator(element);
}
} }
/** /**
...@@ -4529,6 +4637,7 @@ public class Collections { ...@@ -4529,6 +4637,7 @@ public class Collections {
public boolean retainAll(Collection<?> c) {return s.retainAll(c);} public boolean retainAll(Collection<?> c) {return s.retainAll(c);}
// addAll is the only inherited implementation // addAll is the only inherited implementation
// Override default methods in Collection
@Override @Override
public void forEach(Consumer<? super E> action) { public void forEach(Consumer<? super E> action) {
s.forEach(action); s.forEach(action);
...@@ -4538,6 +4647,9 @@ public class Collections { ...@@ -4538,6 +4647,9 @@ public class Collections {
return s.removeIf(filter); return s.removeIf(filter);
} }
@Override
public Spliterator<E> spliterator() {return s.spliterator();}
private static final long serialVersionUID = 2454657854757543876L; private static final long serialVersionUID = 2454657854757543876L;
private void readObject(java.io.ObjectInputStream stream) private void readObject(java.io.ObjectInputStream stream)
...@@ -4597,10 +4709,11 @@ public class Collections { ...@@ -4597,10 +4709,11 @@ public class Collections {
public boolean retainAll(Collection<?> c) {return q.retainAll(c);} public boolean retainAll(Collection<?> c) {return q.retainAll(c);}
// We use inherited addAll; forwarding addAll would be wrong // We use inherited addAll; forwarding addAll would be wrong
// Override default methods in Collection
@Override @Override
public void forEach(Consumer<? super E> action) { public void forEach(Consumer<? super E> action) {q.forEach(action);}
q.forEach(action); @Override
} public Spliterator<E> spliterator() {return q.spliterator();}
@Override @Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
return q.removeIf(filter); return q.removeIf(filter);
......
...@@ -1230,6 +1230,14 @@ public class HashMap<K,V> ...@@ -1230,6 +1230,14 @@ public class HashMap<K,V>
public void clear() { public void clear() {
HashMap.this.clear(); HashMap.this.clear();
} }
public Spliterator<K> spliterator() {
if (HashMap.this.getClass() == HashMap.class)
return new KeySpliterator<K,V>(HashMap.this, 0, -1, 0, 0);
else
return Spliterators.spliterator
(this, Spliterator.SIZED | Spliterator.DISTINCT);
}
} }
/** /**
...@@ -1263,6 +1271,14 @@ public class HashMap<K,V> ...@@ -1263,6 +1271,14 @@ public class HashMap<K,V>
public void clear() { public void clear() {
HashMap.this.clear(); HashMap.this.clear();
} }
public Spliterator<V> spliterator() {
if (HashMap.this.getClass() == HashMap.class)
return new ValueSpliterator<K,V>(HashMap.this, 0, -1, 0, 0);
else
return Spliterators.spliterator
(this, Spliterator.SIZED);
}
} }
/** /**
...@@ -1310,6 +1326,14 @@ public class HashMap<K,V> ...@@ -1310,6 +1326,14 @@ public class HashMap<K,V>
public void clear() { public void clear() {
HashMap.this.clear(); HashMap.this.clear();
} }
public Spliterator<Map.Entry<K,V>> spliterator() {
if (HashMap.this.getClass() == HashMap.class)
return new EntrySpliterator<K,V>(HashMap.this, 0, -1, 0, 0);
else
return Spliterators.spliterator
(this, Spliterator.SIZED | Spliterator.DISTINCT);
}
} }
/** /**
...@@ -1406,4 +1430,257 @@ public class HashMap<K,V> ...@@ -1406,4 +1430,257 @@ public class HashMap<K,V>
// These methods are used when serializing HashSets // These methods are used when serializing HashSets
int capacity() { return table.length; } int capacity() { return table.length; }
float loadFactor() { return loadFactor; } float loadFactor() { return loadFactor; }
/**
* Standin until HM overhaul; based loosely on Weak and Identity HM.
*/
static class HashMapSpliterator<K,V> {
final HashMap<K,V> map;
HashMap.Entry<K,V> current; // current node
int index; // current index, modified on advance/split
int fence; // one past last index
int est; // size estimate
int expectedModCount; // for comodification checks
HashMapSpliterator(HashMap<K,V> m, int origin,
int fence, int est,
int expectedModCount) {
this.map = m;
this.index = origin;
this.fence = fence;
this.est = est;
this.expectedModCount = expectedModCount;
}
final int getFence() { // initialize fence and size on first use
int hi;
if ((hi = fence) < 0) {
HashMap<K,V> m = map;
est = m.size;
expectedModCount = m.modCount;
hi = fence = m.table.length;
}
return hi;
}
public final long estimateSize() {
getFence(); // force init
return (long) est;
}
}
static final class KeySpliterator<K,V>
extends HashMapSpliterator<K,V>
implements Spliterator<K> {
KeySpliterator(HashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
}
public KeySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid || current != null) ? null :
new KeySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super K> action) {
int i, hi, mc;
if (action == null)
throw new NullPointerException();
HashMap<K,V> m = map;
HashMap.Entry<K,V>[] tab = (HashMap.Entry<K,V>[])m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
hi = fence = tab.length;
}
else
mc = expectedModCount;
if (tab.length >= hi && (i = index) >= 0 && i < (index = hi)) {
HashMap.Entry<K,V> p = current;
do {
if (p == null)
p = tab[i++];
else {
action.accept(p.getKey());
p = p.next;
}
} while (p != null || i < hi);
if (m.modCount != mc)
throw new ConcurrentModificationException();
}
}
@SuppressWarnings("unchecked")
public boolean tryAdvance(Consumer<? super K> action) {
int hi;
if (action == null)
throw new NullPointerException();
HashMap.Entry<K,V>[] tab = (HashMap.Entry<K,V>[])map.table;
if (tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
if (current == null)
current = tab[index++];
else {
K k = current.getKey();
current = current.next;
action.accept(k);
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
}
}
return false;
}
public int characteristics() {
return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
Spliterator.DISTINCT;
}
}
static final class ValueSpliterator<K,V>
extends HashMapSpliterator<K,V>
implements Spliterator<V> {
ValueSpliterator(HashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
}
public ValueSpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid || current != null) ? null :
new ValueSpliterator<K,V>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super V> action) {
int i, hi, mc;
if (action == null)
throw new NullPointerException();
HashMap<K,V> m = map;
HashMap.Entry<K,V>[] tab = (HashMap.Entry<K,V>[])m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
hi = fence = tab.length;
}
else
mc = expectedModCount;
if (tab.length >= hi && (i = index) >= 0 && i < (index = hi)) {
HashMap.Entry<K,V> p = current;
do {
if (p == null)
p = tab[i++];
else {
action.accept(p.getValue());
p = p.next;
}
} while (p != null || i < hi);
if (m.modCount != mc)
throw new ConcurrentModificationException();
}
}
@SuppressWarnings("unchecked")
public boolean tryAdvance(Consumer<? super V> action) {
int hi;
if (action == null)
throw new NullPointerException();
HashMap.Entry<K,V>[] tab = (HashMap.Entry<K,V>[])map.table;
if (tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
if (current == null)
current = tab[index++];
else {
V v = current.getValue();
current = current.next;
action.accept(v);
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
}
}
return false;
}
public int characteristics() {
return (fence < 0 || est == map.size ? Spliterator.SIZED : 0);
}
}
static final class EntrySpliterator<K,V>
extends HashMapSpliterator<K,V>
implements Spliterator<Map.Entry<K,V>> {
EntrySpliterator(HashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
}
public EntrySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid || current != null) ? null :
new EntrySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
int i, hi, mc;
if (action == null)
throw new NullPointerException();
HashMap<K,V> m = map;
HashMap.Entry<K,V>[] tab = (HashMap.Entry<K,V>[])m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
hi = fence = tab.length;
}
else
mc = expectedModCount;
if (tab.length >= hi && (i = index) >= 0 && i < (index = hi)) {
HashMap.Entry<K,V> p = current;
do {
if (p == null)
p = tab[i++];
else {
action.accept(p);
p = p.next;
}
} while (p != null || i < hi);
if (m.modCount != mc)
throw new ConcurrentModificationException();
}
}
@SuppressWarnings("unchecked")
public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
int hi;
if (action == null)
throw new NullPointerException();
HashMap.Entry<K,V>[] tab = (HashMap.Entry<K,V>[])map.table;
if (tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
if (current == null)
current = tab[index++];
else {
HashMap.Entry<K,V> e = current;
current = current.next;
action.accept(e);
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
}
}
return false;
}
public int characteristics() {
return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
Spliterator.DISTINCT;
}
}
} }
/* /*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -311,4 +311,8 @@ public class HashSet<E> ...@@ -311,4 +311,8 @@ public class HashSet<E>
map.put(e, PRESENT); map.put(e, PRESENT);
} }
} }
public Spliterator<E> spliterator() {
return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
}
} }
/* /*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -24,8 +24,10 @@ ...@@ -24,8 +24,10 @@
*/ */
package java.util; package java.util;
import java.io.*; import java.io.*;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.function.Consumer;
/** /**
* This class implements the <tt>Map</tt> interface with a hash table, using * This class implements the <tt>Map</tt> interface with a hash table, using
...@@ -162,19 +164,19 @@ public class IdentityHashMap<K,V> ...@@ -162,19 +164,19 @@ public class IdentityHashMap<K,V>
/** /**
* The table, resized as necessary. Length MUST always be a power of two. * The table, resized as necessary. Length MUST always be a power of two.
*/ */
private transient Object[] table; transient Object[] table; // non-private to simplify nested class access
/** /**
* The number of key-value mappings contained in this identity hash map. * The number of key-value mappings contained in this identity hash map.
* *
* @serial * @serial
*/ */
private int size; int size;
/** /**
* The number of modifications, to support fast-fail iterators * The number of modifications, to support fast-fail iterators
*/ */
private transient int modCount; transient int modCount;
/** /**
* The next size value at which to resize (capacity * load factor). * The next size value at which to resize (capacity * load factor).
...@@ -184,7 +186,7 @@ public class IdentityHashMap<K,V> ...@@ -184,7 +186,7 @@ public class IdentityHashMap<K,V>
/** /**
* Value representing null keys inside tables. * Value representing null keys inside tables.
*/ */
private static final Object NULL_KEY = new Object(); static final Object NULL_KEY = new Object();
/** /**
* Use NULL_KEY for key if it is null. * Use NULL_KEY for key if it is null.
...@@ -196,7 +198,7 @@ public class IdentityHashMap<K,V> ...@@ -196,7 +198,7 @@ public class IdentityHashMap<K,V>
/** /**
* Returns internal representation of null key back to caller as null. * Returns internal representation of null key back to caller as null.
*/ */
private static Object unmaskNull(Object key) { static final Object unmaskNull(Object key) {
return (key == NULL_KEY ? null : key); return (key == NULL_KEY ? null : key);
} }
...@@ -1012,7 +1014,7 @@ public class IdentityHashMap<K,V> ...@@ -1012,7 +1014,7 @@ public class IdentityHashMap<K,V>
return result; return result;
} }
public Object[] toArray() { public Object[] toArray() {
return toArray(new Object[size()]); return toArray(new Object[0]);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) { public <T> T[] toArray(T[] a) {
...@@ -1042,6 +1044,10 @@ public class IdentityHashMap<K,V> ...@@ -1042,6 +1044,10 @@ public class IdentityHashMap<K,V>
} }
return a; return a;
} }
public Spliterator<K> spliterator() {
return new KeySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);
}
} }
/** /**
...@@ -1095,7 +1101,7 @@ public class IdentityHashMap<K,V> ...@@ -1095,7 +1101,7 @@ public class IdentityHashMap<K,V>
IdentityHashMap.this.clear(); IdentityHashMap.this.clear();
} }
public Object[] toArray() { public Object[] toArray() {
return toArray(new Object[size()]); return toArray(new Object[0]);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) { public <T> T[] toArray(T[] a) {
...@@ -1124,6 +1130,10 @@ public class IdentityHashMap<K,V> ...@@ -1124,6 +1130,10 @@ public class IdentityHashMap<K,V>
} }
return a; return a;
} }
public Spliterator<V> spliterator() {
return new ValueSpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);
}
} }
/** /**
...@@ -1211,7 +1221,7 @@ public class IdentityHashMap<K,V> ...@@ -1211,7 +1221,7 @@ public class IdentityHashMap<K,V>
} }
public Object[] toArray() { public Object[] toArray() {
return toArray(new Object[size()]); return toArray(new Object[0]);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
...@@ -1242,6 +1252,10 @@ public class IdentityHashMap<K,V> ...@@ -1242,6 +1252,10 @@ public class IdentityHashMap<K,V>
} }
return a; return a;
} }
public Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);
}
} }
...@@ -1322,4 +1336,223 @@ public class IdentityHashMap<K,V> ...@@ -1322,4 +1336,223 @@ public class IdentityHashMap<K,V>
tab[i] = k; tab[i] = k;
tab[i + 1] = value; tab[i + 1] = value;
} }
/**
* Similar form as array-based Spliterators, but skips blank elements,
* and guestimates size as decreasing by half per split.
*/
static class IdentityHashMapSpliterator<K,V> {
final IdentityHashMap<K,V> map;
int index; // current index, modified on advance/split
int fence; // -1 until first use; then one past last index
int est; // size estimate
int expectedModCount; // initialized when fence set
IdentityHashMapSpliterator(IdentityHashMap<K,V> map, int origin,
int fence, int est, int expectedModCount) {
this.map = map;
this.index = origin;
this.fence = fence;
this.est = est;
this.expectedModCount = expectedModCount;
}
final int getFence() { // initialize fence and size on first use
int hi;
if ((hi = fence) < 0) {
est = map.size;
expectedModCount = map.modCount;
hi = fence = map.table.length;
}
return hi;
}
public final long estimateSize() {
getFence(); // force init
return (long) est;
}
}
static final class KeySpliterator<K,V>
extends IdentityHashMapSpliterator<K,V>
implements Spliterator<K> {
KeySpliterator(IdentityHashMap<K,V> map, int origin, int fence, int est,
int expectedModCount) {
super(map, origin, fence, est, expectedModCount);
}
public KeySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;
return (lo >= mid) ? null :
new KeySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super K> action) {
if (action == null)
throw new NullPointerException();
int i, hi, mc; Object key;
IdentityHashMap<K,V> m; Object[] a;
if ((m = map) != null && (a = m.table) != null &&
(i = index) >= 0 && (index = hi = getFence()) <= a.length) {
for (; i < hi; i += 2) {
if ((key = a[i]) != null)
action.accept((K)unmaskNull(key));
}
if (m.modCount == expectedModCount)
return;
}
throw new ConcurrentModificationException();
}
@SuppressWarnings("unchecked")
public boolean tryAdvance(Consumer<? super K> action) {
if (action == null)
throw new NullPointerException();
Object[] a = map.table;
int hi = getFence();
while (index < hi) {
Object key = a[index];
index += 2;
if (key != null) {
action.accept((K)unmaskNull(key));
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
}
return false;
}
public int characteristics() {
return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;
}
}
static final class ValueSpliterator<K,V>
extends IdentityHashMapSpliterator<K,V>
implements Spliterator<V> {
ValueSpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
}
public ValueSpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;
return (lo >= mid) ? null :
new ValueSpliterator<K,V>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
public void forEachRemaining(Consumer<? super V> action) {
if (action == null)
throw new NullPointerException();
int i, hi, mc;
IdentityHashMap<K,V> m; Object[] a;
if ((m = map) != null && (a = m.table) != null &&
(i = index) >= 0 && (index = hi = getFence()) <= a.length) {
for (; i < hi; i += 2) {
if (a[i] != null) {
@SuppressWarnings("unchecked") V v = (V)a[i+1];
action.accept(v);
}
}
if (m.modCount == expectedModCount)
return;
}
throw new ConcurrentModificationException();
}
public boolean tryAdvance(Consumer<? super V> action) {
if (action == null)
throw new NullPointerException();
Object[] a = map.table;
int hi = getFence();
while (index < hi) {
Object key = a[index];
@SuppressWarnings("unchecked") V v = (V)a[index+1];
index += 2;
if (key != null) {
action.accept(v);
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
}
return false;
}
public int characteristics() {
return (fence < 0 || est == map.size ? SIZED : 0);
}
}
static final class EntrySpliterator<K,V>
extends IdentityHashMapSpliterator<K,V>
implements Spliterator<Map.Entry<K,V>> {
EntrySpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
}
public EntrySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;
return (lo >= mid) ? null :
new EntrySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {
if (action == null)
throw new NullPointerException();
int i, hi, mc;
IdentityHashMap<K,V> m; Object[] a;
if ((m = map) != null && (a = m.table) != null &&
(i = index) >= 0 && (index = hi = getFence()) <= a.length) {
for (; i < hi; i += 2) {
Object key = a[i];
if (key != null) {
@SuppressWarnings("unchecked") K k =
(K)unmaskNull(key);
@SuppressWarnings("unchecked") V v = (V)a[i+1];
action.accept
(new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
}
}
if (m.modCount == expectedModCount)
return;
}
throw new ConcurrentModificationException();
}
public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
if (action == null)
throw new NullPointerException();
Object[] a = map.table;
int hi = getFence();
while (index < hi) {
Object key = a[index];
@SuppressWarnings("unchecked") V v = (V)a[index+1];
index += 2;
if (key != null) {
@SuppressWarnings("unchecked") K k =
(K)unmaskNull(key);
action.accept
(new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
}
return false;
}
public int characteristics() {
return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;
}
}
} }
/* /*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -168,4 +168,18 @@ public class LinkedHashSet<E> ...@@ -168,4 +168,18 @@ public class LinkedHashSet<E>
super(Math.max(2*c.size(), 11), .75f, true); super(Math.max(2*c.size(), 11), .75f, true);
addAll(c); addAll(c);
} }
/**
* Creates a {@code Spliterator}, over the elements in this set, that
* reports {@code SIZED}, {@code DISTINCT} and {@code ORDERED}.
* Overriding implementations are expected to document if the
* {@code Spliterator} reports any additional and relevant characteristic
* values.
*
* @return a {@code Spliterator} over the elements in this set
*/
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
}
} }
/* /*
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
package java.util; package java.util;
import java.util.function.Consumer;
/** /**
* Doubly-linked list implementation of the {@code List} and {@code Deque} * Doubly-linked list implementation of the {@code List} and {@code Deque}
* interfaces. Implements all optional list operations, and permits all * interfaces. Implements all optional list operations, and permits all
...@@ -948,6 +950,16 @@ public class LinkedList<E> ...@@ -948,6 +950,16 @@ public class LinkedList<E>
expectedModCount++; expectedModCount++;
} }
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
next = next.next;
nextIndex++;
}
checkForComodification();
}
final void checkForComodification() { final void checkForComodification() {
if (modCount != expectedModCount) if (modCount != expectedModCount)
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
...@@ -1135,4 +1147,103 @@ public class LinkedList<E> ...@@ -1135,4 +1147,103 @@ public class LinkedList<E>
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
linkLast((E)s.readObject()); linkLast((E)s.readObject());
} }
public Spliterator<E> spliterator() {
return new LLSpliterator<E>(this, -1, 0);
}
/** A customized variant of Spliterators.IteratorSpliterator */
static final class LLSpliterator<E> implements Spliterator<E> {
static final int BATCH_UNIT = 1 << 10; // batch array size increment
static final int MAX_BATCH = 1 << 25; // max batch array size;
final LinkedList<E> list; // null OK unless traversed
Node<E> current; // current node; null until initialized
int est; // size estimate; -1 until first needed
int expectedModCount; // initialized when est set
int batch; // batch size for splits
LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {
this.list = list;
this.est = est;
this.expectedModCount = expectedModCount;
}
final int getEst() {
int s; // force initialization
final LinkedList<E> lst;
if ((s = est) < 0) {
if ((lst = list) == null)
s = est = 0;
else {
expectedModCount = lst.modCount;
current = lst.first;
s = est = lst.size;
}
}
return s;
}
public long estimateSize() { return (long) getEst(); }
public Spliterator<E> trySplit() {
Node<E> p;
int s = getEst();
if (s > 1 && (p = current) != null) {
int n = batch + BATCH_UNIT;
if (n > s)
n = s;
if (n > MAX_BATCH)
n = MAX_BATCH;
Object[] a;
try {
a = new Object[n];
} catch (OutOfMemoryError oome) {
return null;
}
int j = 0;
do { a[j++] = p.item; } while ((p = p.next) != null && j < n);
current = p;
batch = j;
est = s - j;
return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
}
return null;
}
public void forEachRemaining(Consumer<? super E> action) {
Node<E> p; int n;
if (action == null) throw new NullPointerException();
if ((n = getEst()) > 0 && (p = current) != null) {
current = null;
est = 0;
do {
E e = p.item;
p = p.next;
action.accept(e);
} while (p != null && --n > 0);
}
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
}
public boolean tryAdvance(Consumer<? super E> action) {
Node<E> p;
if (action == null) throw new NullPointerException();
if (getEst() > 0 && (p = current) != null) {
--est;
E e = p.item;
current = p.next;
action.accept(e);
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
} }
/* /*
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
package java.util; package java.util;
import java.util.function.Consumer;
/** /**
* An unbounded priority {@linkplain Queue queue} based on a priority heap. * An unbounded priority {@linkplain Queue queue} based on a priority heap.
* The elements of the priority queue are ordered according to their * The elements of the priority queue are ordered according to their
...@@ -56,7 +58,7 @@ package java.util; ...@@ -56,7 +58,7 @@ package java.util;
* the priority queue in any particular order. If you need ordered * the priority queue in any particular order. If you need ordered
* traversal, consider using {@code Arrays.sort(pq.toArray())}. * traversal, consider using {@code Arrays.sort(pq.toArray())}.
* *
* <p> <strong>Note that this implementation is not synchronized.</strong> * <p><strong>Note that this implementation is not synchronized.</strong>
* Multiple threads should not access a {@code PriorityQueue} * Multiple threads should not access a {@code PriorityQueue}
* instance concurrently if any of the threads modifies the queue. * instance concurrently if any of the threads modifies the queue.
* Instead, use the thread-safe {@link * Instead, use the thread-safe {@link
...@@ -92,7 +94,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -92,7 +94,7 @@ public class PriorityQueue<E> extends AbstractQueue<E>
* heap and each descendant d of n, n <= d. The element with the * heap and each descendant d of n, n <= d. The element with the
* lowest value is in queue[0], assuming the queue is nonempty. * lowest value is in queue[0], assuming the queue is nonempty.
*/ */
private transient Object[] queue; transient Object[] queue; // non-private to simplify nested class access
/** /**
* The number of elements in the priority queue. * The number of elements in the priority queue.
...@@ -109,7 +111,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -109,7 +111,7 @@ public class PriorityQueue<E> extends AbstractQueue<E>
* The number of times this priority queue has been * The number of times this priority queue has been
* <i>structurally modified</i>. See AbstractList for gory details. * <i>structurally modified</i>. See AbstractList for gory details.
*/ */
private transient int modCount = 0; transient int modCount = 0; // non-private to simplify nested class access
/** /**
* Creates a {@code PriorityQueue} with the default initial * Creates a {@code PriorityQueue} with the default initial
...@@ -332,9 +334,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -332,9 +334,7 @@ public class PriorityQueue<E> extends AbstractQueue<E>
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public E peek() { public E peek() {
if (size == 0) return (size == 0) ? null : (E) queue[0];
return null;
return (E) queue[0];
} }
private int indexOf(Object o) { private int indexOf(Object o) {
...@@ -431,15 +431,14 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -431,15 +431,14 @@ public class PriorityQueue<E> extends AbstractQueue<E>
* precise control over the runtime type of the output array, and may, * precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs. * under certain circumstances, be used to save allocation costs.
* *
* <p>Suppose <tt>x</tt> is a queue known to contain only strings. * <p>Suppose {@code x} is a queue known to contain only strings.
* The following code can be used to dump the queue into a newly * The following code can be used to dump the queue into a newly
* allocated array of <tt>String</tt>: * allocated array of {@code String}:
* *
* <pre> * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
* String[] y = x.toArray(new String[0]);</pre>
* *
* Note that <tt>toArray(new Object[0])</tt> is identical in function to * Note that {@code toArray(new Object[0])} is identical in function to
* <tt>toArray()</tt>. * {@code toArray()}.
* *
* @param a the array into which the elements of the queue are to * @param a the array into which the elements of the queue are to
* be stored, if it is big enough; otherwise, a new array of the * be stored, if it is big enough; otherwise, a new array of the
...@@ -452,6 +451,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -452,6 +451,7 @@ public class PriorityQueue<E> extends AbstractQueue<E>
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) { public <T> T[] toArray(T[] a) {
final int size = this.size;
if (a.length < size) if (a.length < size)
// Make a new array of a's runtime type, but my contents: // Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(queue, size, a.getClass()); return (T[]) Arrays.copyOf(queue, size, a.getClass());
...@@ -569,15 +569,14 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -569,15 +569,14 @@ public class PriorityQueue<E> extends AbstractQueue<E>
size = 0; size = 0;
} }
@SuppressWarnings("unchecked")
public E poll() { public E poll() {
if (size == 0) if (size == 0)
return null; return null;
int s = --size; int s = --size;
modCount++; modCount++;
@SuppressWarnings("unchecked") E result = (E) queue[0];
E result = (E) queue[0]; E x = (E) queue[s];
@SuppressWarnings("unchecked")
E x = (E) queue[s];
queue[s] = null; queue[s] = null;
if (s != 0) if (s != 0)
siftDown(0, x); siftDown(0, x);
...@@ -596,15 +595,15 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -596,15 +595,15 @@ public class PriorityQueue<E> extends AbstractQueue<E>
* position before i. This fact is used by iterator.remove so as to * position before i. This fact is used by iterator.remove so as to
* avoid missing traversing elements. * avoid missing traversing elements.
*/ */
@SuppressWarnings("unchecked")
private E removeAt(int i) { private E removeAt(int i) {
assert i >= 0 && i < size; // assert i >= 0 && i < size;
modCount++; modCount++;
int s = --size; int s = --size;
if (s == i) // removed last element if (s == i) // removed last element
queue[i] = null; queue[i] = null;
else { else {
@SuppressWarnings("unchecked") E moved = (E) queue[s];
E moved = (E) queue[s];
queue[s] = null; queue[s] = null;
siftDown(i, moved); siftDown(i, moved);
if (queue[i] == moved) { if (queue[i] == moved) {
...@@ -649,12 +648,12 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -649,12 +648,12 @@ public class PriorityQueue<E> extends AbstractQueue<E>
queue[k] = key; queue[k] = key;
} }
@SuppressWarnings("unchecked")
private void siftUpUsingComparator(int k, E x) { private void siftUpUsingComparator(int k, E x) {
while (k > 0) { while (k > 0) {
int parent = (k - 1) >>> 1; int parent = (k - 1) >>> 1;
@SuppressWarnings("unchecked") Object e = queue[parent];
E e = (E) queue[parent]; if (comparator.compare(x, (E) e) >= 0)
if (comparator.compare(x, e) >= 0)
break; break;
queue[k] = e; queue[k] = e;
k = parent; k = parent;
...@@ -738,8 +737,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -738,8 +737,7 @@ public class PriorityQueue<E> extends AbstractQueue<E>
} }
/** /**
* Saves the state of the instance to a stream (that * Saves this queue to a stream (that is, serializes it).
* is, serializes it).
* *
* @serialData The length of the array backing the instance is * @serialData The length of the array backing the instance is
* emitted (int), followed by all of its elements * emitted (int), followed by all of its elements
...@@ -747,7 +745,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -747,7 +745,7 @@ public class PriorityQueue<E> extends AbstractQueue<E>
* @param s the stream * @param s the stream
*/ */
private void writeObject(java.io.ObjectOutputStream s) private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{ throws java.io.IOException {
// Write out element count, and any hidden stuff // Write out element count, and any hidden stuff
s.defaultWriteObject(); s.defaultWriteObject();
...@@ -783,4 +781,99 @@ public class PriorityQueue<E> extends AbstractQueue<E> ...@@ -783,4 +781,99 @@ public class PriorityQueue<E> extends AbstractQueue<E>
// spec has never explained what that might be. // spec has never explained what that might be.
heapify(); heapify();
} }
public final Spliterator<E> spliterator() {
return new PriorityQueueSpliterator<E>(this, 0, -1, 0);
}
static final class PriorityQueueSpliterator<E> implements Spliterator<E> {
/*
* This is very similar to ArrayList Spliterator, except for
* extra null checks.
*/
private final PriorityQueue<E> pq;
private int index; // current index, modified on advance/split
private int fence; // -1 until first use
private int expectedModCount; // initialized when fence set
/** Creates new spliterator covering the given range */
PriorityQueueSpliterator(PriorityQueue<E> pq, int origin, int fence,
int expectedModCount) {
this.pq = pq;
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
private int getFence() { // initialize fence to size on first use
int hi;
if ((hi = fence) < 0) {
expectedModCount = pq.modCount;
hi = fence = pq.size;
}
return hi;
}
public PriorityQueueSpliterator<E> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null :
new PriorityQueueSpliterator<E>(pq, lo, index = mid,
expectedModCount);
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> action) {
int i, hi, mc; // hoist accesses and checks from loop
PriorityQueue<E> q; Object[] a;
if (action == null)
throw new NullPointerException();
if ((q = pq) != null && (a = q.queue) != null) {
if ((hi = fence) < 0) {
mc = q.modCount;
hi = q.size;
}
else
mc = expectedModCount;
if ((i = index) >= 0 && (index = hi) <= a.length) {
for (E e;; ++i) {
if (i < hi) {
if ((e = (E) a[i]) == null) // must be CME
break;
action.accept(e);
}
else if (q.modCount != mc)
break;
else
return;
}
}
}
throw new ConcurrentModificationException();
}
public boolean tryAdvance(Consumer<? super E> action) {
if (action == null)
throw new NullPointerException();
int hi = getFence(), lo = index;
if (lo >= 0 && lo < hi) {
index = lo + 1;
@SuppressWarnings("unchecked") E e = (E)pq.queue[lo];
if (e == null)
throw new ConcurrentModificationException();
action.accept(e);
if (pq.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
public long estimateSize() {
return (long) (getFence() - index);
}
public int characteristics() {
return Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.NONNULL;
}
}
} }
...@@ -533,5 +533,9 @@ public class TreeSet<E> extends AbstractSet<E> ...@@ -533,5 +533,9 @@ public class TreeSet<E> extends AbstractSet<E>
tm.readTreeSet(size, s, PRESENT); tm.readTreeSet(size, s, PRESENT);
} }
public Spliterator<E> spliterator() {
return TreeMap.keySpliteratorFor(m);
}
private static final long serialVersionUID = -2479143000061671589L; private static final long serialVersionUID = -2479143000061671589L;
} }
/* /*
* Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,6 +29,8 @@ import java.util.function.Consumer; ...@@ -29,6 +29,8 @@ import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
import java.util.function.Consumer;
/** /**
* The {@code Vector} class implements a growable array of * The {@code Vector} class implements a growable array of
* objects. Like an array, it contains components that can be * objects. Like an array, it contains components that can be
...@@ -1155,6 +1157,28 @@ public class Vector<E> ...@@ -1155,6 +1157,28 @@ public class Vector<E>
lastRet = -1; lastRet = -1;
} }
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
synchronized (Vector.this) {
final int size = Vector.this.elementCount;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = Vector.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
action.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
lastRet = cursor = i;
checkForComodification();
}
}
final void checkForComodification() { final void checkForComodification() {
if (modCount != expectedModCount) if (modCount != expectedModCount)
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
...@@ -1298,4 +1322,96 @@ public class Vector<E> ...@@ -1298,4 +1322,96 @@ public class Vector<E>
} }
modCount++; modCount++;
} }
@Override
public Spliterator<E> spliterator() {
return new VectorSpliterator<>(this, null, 0, -1, 0);
}
/** Similar to ArrayList Spliterator */
static final class VectorSpliterator<E> implements Spliterator<E> {
private final Vector<E> list;
private Object[] array;
private int index; // current index, modified on advance/split
private int fence; // -1 until used; then one past last index
private int expectedModCount; // initialized when fence set
/** Create new spliterator covering the given range */
VectorSpliterator(Vector<E> list, Object[] array, int origin, int fence,
int expectedModCount) {
this.list = list;
this.array = array;
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
private int getFence() { // initialize on first use
int hi;
if ((hi = fence) < 0) {
synchronized(list) {
array = list.elementData;
expectedModCount = list.modCount;
hi = fence = list.elementCount;
}
}
return hi;
}
public Spliterator<E> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null :
new VectorSpliterator<E>(list, array, lo, index = mid,
expectedModCount);
}
@SuppressWarnings("unchecked")
public boolean tryAdvance(Consumer<? super E> action) {
int i;
if (action == null)
throw new NullPointerException();
if (getFence() > (i = index)) {
index = i + 1;
action.accept((E)array[i]);
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> action) {
int i, hi; // hoist accesses and checks from loop
Vector<E> lst; Object[] a;
if (action == null)
throw new NullPointerException();
if ((lst = list) != null) {
if ((hi = fence) < 0) {
synchronized(lst) {
expectedModCount = lst.modCount;
a = array = lst.elementData;
hi = fence = lst.elementCount;
}
}
else
a = array;
if (a != null && (i = index) >= 0 && (index = hi) <= a.length) {
while (i < hi)
action.accept((E) a[i++]);
if (lst.modCount == expectedModCount)
return;
}
}
throw new ConcurrentModificationException();
}
public long estimateSize() {
return (long) (getFence() - index);
}
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
} }
/* /*
* Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -24,8 +24,10 @@ ...@@ -24,8 +24,10 @@
*/ */
package java.util; package java.util;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.util.function.Consumer;
/** /**
...@@ -898,6 +900,10 @@ public class WeakHashMap<K,V> ...@@ -898,6 +900,10 @@ public class WeakHashMap<K,V>
public void clear() { public void clear() {
WeakHashMap.this.clear(); WeakHashMap.this.clear();
} }
public Spliterator<K> spliterator() {
return new KeySpliterator<>(WeakHashMap.this, 0, -1, 0, 0);
}
} }
/** /**
...@@ -934,6 +940,10 @@ public class WeakHashMap<K,V> ...@@ -934,6 +940,10 @@ public class WeakHashMap<K,V>
public void clear() { public void clear() {
WeakHashMap.this.clear(); WeakHashMap.this.clear();
} }
public Spliterator<V> spliterator() {
return new ValueSpliterator<>(WeakHashMap.this, 0, -1, 0, 0);
}
} }
/** /**
...@@ -994,5 +1004,288 @@ public class WeakHashMap<K,V> ...@@ -994,5 +1004,288 @@ public class WeakHashMap<K,V>
public <T> T[] toArray(T[] a) { public <T> T[] toArray(T[] a) {
return deepCopy().toArray(a); return deepCopy().toArray(a);
} }
public Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(WeakHashMap.this, 0, -1, 0, 0);
}
}
/**
* Similar form as other hash Spliterators, but skips dead
* elements.
*/
static class WeakHashMapSpliterator<K,V> {
final WeakHashMap<K,V> map;
WeakHashMap.Entry<K,V> current; // current node
int index; // current index, modified on advance/split
int fence; // -1 until first use; then one past last index
int est; // size estimate
int expectedModCount; // for comodification checks
WeakHashMapSpliterator(WeakHashMap<K,V> m, int origin,
int fence, int est,
int expectedModCount) {
this.map = m;
this.index = origin;
this.fence = fence;
this.est = est;
this.expectedModCount = expectedModCount;
}
final int getFence() { // initialize fence and size on first use
int hi;
if ((hi = fence) < 0) {
WeakHashMap<K,V> m = map;
est = m.size();
expectedModCount = m.modCount;
hi = fence = m.table.length;
}
return hi;
}
public final long estimateSize() {
getFence(); // force init
return (long) est;
}
}
static final class KeySpliterator<K,V>
extends WeakHashMapSpliterator<K,V>
implements Spliterator<K> {
KeySpliterator(WeakHashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
}
public KeySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null :
new KeySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
public void forEachRemaining(Consumer<? super K> action) {
int i, hi, mc;
if (action == null)
throw new NullPointerException();
WeakHashMap<K,V> m = map;
WeakHashMap.Entry<K,V>[] tab = m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
hi = fence = tab.length;
}
else
mc = expectedModCount;
if (tab.length >= hi && (i = index) >= 0 && i < hi) {
index = hi;
WeakHashMap.Entry<K,V> p = current;
do {
if (p == null)
p = tab[i++];
else {
Object x = p.get();
p = p.next;
if (x != null) {
@SuppressWarnings("unchecked") K k =
(K) WeakHashMap.unmaskNull(x);
action.accept(k);
}
}
} while (p != null || i < hi);
}
if (m.modCount != mc)
throw new ConcurrentModificationException();
}
public boolean tryAdvance(Consumer<? super K> action) {
int hi;
if (action == null)
throw new NullPointerException();
WeakHashMap.Entry<K,V>[] tab = map.table;
if (tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
if (current == null)
current = tab[index++];
else {
Object x = current.get();
current = current.next;
if (x != null) {
@SuppressWarnings("unchecked") K k =
(K) WeakHashMap.unmaskNull(x);
action.accept(k);
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
}
}
}
return false;
}
public int characteristics() {
return Spliterator.DISTINCT;
}
}
static final class ValueSpliterator<K,V>
extends WeakHashMapSpliterator<K,V>
implements Spliterator<V> {
ValueSpliterator(WeakHashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
}
public ValueSpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null :
new ValueSpliterator<K,V>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
public void forEachRemaining(Consumer<? super V> action) {
int i, hi, mc;
if (action == null)
throw new NullPointerException();
WeakHashMap<K,V> m = map;
WeakHashMap.Entry<K,V>[] tab = m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
hi = fence = tab.length;
}
else
mc = expectedModCount;
if (tab.length >= hi && (i = index) >= 0 && i < hi) {
index = hi;
WeakHashMap.Entry<K,V> p = current;
do {
if (p == null)
p = tab[i++];
else {
Object x = p.get();
V v = p.value;
p = p.next;
if (x != null)
action.accept(v);
}
} while (p != null || i < hi);
}
if (m.modCount != mc)
throw new ConcurrentModificationException();
}
public boolean tryAdvance(Consumer<? super V> action) {
int hi;
if (action == null)
throw new NullPointerException();
WeakHashMap.Entry<K,V>[] tab = map.table;
if (tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
if (current == null)
current = tab[index++];
else {
Object x = current.get();
V v = current.value;
current = current.next;
if (x != null) {
action.accept(v);
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
}
}
}
return false;
}
public int characteristics() {
return 0;
}
}
static final class EntrySpliterator<K,V>
extends WeakHashMapSpliterator<K,V>
implements Spliterator<Map.Entry<K,V>> {
EntrySpliterator(WeakHashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
}
public EntrySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null :
new EntrySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {
int i, hi, mc;
if (action == null)
throw new NullPointerException();
WeakHashMap<K,V> m = map;
WeakHashMap.Entry<K,V>[] tab = m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
hi = fence = tab.length;
}
else
mc = expectedModCount;
if (tab.length >= hi && (i = index) >= 0 && i < hi) {
index = hi;
WeakHashMap.Entry<K,V> p = current;
do {
if (p == null)
p = tab[i++];
else {
Object x = p.get();
V v = p.value;
p = p.next;
if (x != null) {
@SuppressWarnings("unchecked") K k =
(K) WeakHashMap.unmaskNull(x);
action.accept
(new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
}
}
} while (p != null || i < hi);
}
if (m.modCount != mc)
throw new ConcurrentModificationException();
}
public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
int hi;
if (action == null)
throw new NullPointerException();
WeakHashMap.Entry<K,V>[] tab = map.table;
if (tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
if (current == null)
current = tab[index++];
else {
Object x = current.get();
V v = current.value;
current = current.next;
if (x != null) {
@SuppressWarnings("unchecked") K k =
(K) WeakHashMap.unmaskNull(x);
action.accept
(new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
}
}
}
return false;
}
public int characteristics() {
return Spliterator.DISTINCT;
}
} }
} }
...@@ -184,6 +184,8 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -184,6 +184,8 @@ public class SpliteratorTraversingAndSplittingTest {
@Override @Override
public boolean tryAdvance(Consumer<? super Integer> action) { public boolean tryAdvance(Consumer<? super Integer> action) {
if (action == null)
throw new NullPointerException();
if (it.hasNext()) { if (it.hasNext()) {
action.accept(it.next()); action.accept(it.next());
return true; return true;
...@@ -193,7 +195,7 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -193,7 +195,7 @@ public class SpliteratorTraversingAndSplittingTest {
} }
} }
} }
db.add("new Spliterators.AbstractAdvancingSpliterator()", db.add("new Spliterators.AbstractSpliterator()",
() -> new SpliteratorFromIterator(exp.iterator(), exp.size())); () -> new SpliteratorFromIterator(exp.iterator(), exp.size()));
// Collections // Collections
...@@ -370,7 +372,28 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -370,7 +372,28 @@ public class SpliteratorTraversingAndSplittingTest {
db.addCollection(c -> Collections.singletonList(exp.get(0))); db.addCollection(c -> Collections.singletonList(exp.get(0)));
} }
// @@@ Collections.synchronized/unmodifiable/checked wrappers // Collections.synchronized/unmodifiable/checked wrappers
db.addCollection(Collections::unmodifiableCollection);
db.addCollection(c -> Collections.unmodifiableSet(new HashSet<>(c)));
db.addCollection(c -> Collections.unmodifiableSortedSet(new TreeSet<>(c)));
db.addList(c -> Collections.unmodifiableList(new ArrayList<>(c)));
db.addMap(Collections::unmodifiableMap);
db.addMap(m -> Collections.unmodifiableSortedMap(new TreeMap<>(m)));
db.addCollection(Collections::synchronizedCollection);
db.addCollection(c -> Collections.synchronizedSet(new HashSet<>(c)));
db.addCollection(c -> Collections.synchronizedSortedSet(new TreeSet<>(c)));
db.addList(c -> Collections.synchronizedList(new ArrayList<>(c)));
db.addMap(Collections::synchronizedMap);
db.addMap(m -> Collections.synchronizedSortedMap(new TreeMap<>(m)));
db.addCollection(c -> Collections.checkedCollection(c, Integer.class));
db.addCollection(c -> Collections.checkedQueue(new ArrayDeque<>(c), Integer.class));
db.addCollection(c -> Collections.checkedSet(new HashSet<>(c), Integer.class));
db.addCollection(c -> Collections.checkedSortedSet(new TreeSet<>(c), Integer.class));
db.addList(c -> Collections.checkedList(new ArrayList<>(c), Integer.class));
db.addMap(c -> Collections.checkedMap(c, Integer.class, Integer.class));
db.addMap(m -> Collections.checkedSortedMap(new TreeMap<>(m), Integer.class, Integer.class));
// Maps // Maps
...@@ -400,6 +423,13 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -400,6 +423,13 @@ public class SpliteratorTraversingAndSplittingTest {
return Collections.unmodifiableList(exp); return Collections.unmodifiableList(exp);
} }
@Test(dataProvider = "Spliterator<Integer>")
@SuppressWarnings({"unchecked", "rawtypes"})
public void testNullPointerException(String description, Collection exp, Supplier<Spliterator> s) {
executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining(null));
executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance(null));
}
@Test(dataProvider = "Spliterator<Integer>") @Test(dataProvider = "Spliterator<Integer>")
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
public void testForEach(String description, Collection exp, Supplier<Spliterator> s) { public void testForEach(String description, Collection exp, Supplier<Spliterator> s) {
...@@ -507,6 +537,8 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -507,6 +537,8 @@ public class SpliteratorTraversingAndSplittingTest {
@Override @Override
public boolean tryAdvance(IntConsumer action) { public boolean tryAdvance(IntConsumer action) {
if (action == null)
throw new NullPointerException();
if (index < a.length) { if (index < a.length) {
action.accept(a[index++]); action.accept(a[index++]);
return true; return true;
...@@ -552,6 +584,12 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -552,6 +584,12 @@ public class SpliteratorTraversingAndSplittingTest {
return b -> new BoxingAdapter(b); return b -> new BoxingAdapter(b);
} }
@Test(dataProvider = "Spliterator.OfInt")
public void testIntNullPointerException(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining((IntConsumer) null));
executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance((IntConsumer) null));
}
@Test(dataProvider = "Spliterator.OfInt") @Test(dataProvider = "Spliterator.OfInt")
public void testIntForEach(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) { public void testIntForEach(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
testForEach(exp, s, intBoxingConsumer()); testForEach(exp, s, intBoxingConsumer());
...@@ -652,6 +690,8 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -652,6 +690,8 @@ public class SpliteratorTraversingAndSplittingTest {
@Override @Override
public boolean tryAdvance(LongConsumer action) { public boolean tryAdvance(LongConsumer action) {
if (action == null)
throw new NullPointerException();
if (index < a.length) { if (index < a.length) {
action.accept(a[index++]); action.accept(a[index++]);
return true; return true;
...@@ -704,6 +744,12 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -704,6 +744,12 @@ public class SpliteratorTraversingAndSplittingTest {
return b -> new BoxingAdapter(b); return b -> new BoxingAdapter(b);
} }
@Test(dataProvider = "Spliterator.OfLong")
public void testLongNullPointerException(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining((LongConsumer) null));
executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance((LongConsumer) null));
}
@Test(dataProvider = "Spliterator.OfLong") @Test(dataProvider = "Spliterator.OfLong")
public void testLongForEach(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) { public void testLongForEach(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
testForEach(exp, s, longBoxingConsumer()); testForEach(exp, s, longBoxingConsumer());
...@@ -804,6 +850,8 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -804,6 +850,8 @@ public class SpliteratorTraversingAndSplittingTest {
@Override @Override
public boolean tryAdvance(DoubleConsumer action) { public boolean tryAdvance(DoubleConsumer action) {
if (action == null)
throw new NullPointerException();
if (index < a.length) { if (index < a.length) {
action.accept(a[index++]); action.accept(a[index++]);
return true; return true;
...@@ -856,6 +904,12 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -856,6 +904,12 @@ public class SpliteratorTraversingAndSplittingTest {
return b -> new BoxingAdapter(b); return b -> new BoxingAdapter(b);
} }
@Test(dataProvider = "Spliterator.OfDouble")
public void testDoubleNullPointerException(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining((DoubleConsumer) null));
executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance((DoubleConsumer) null));
}
@Test(dataProvider = "Spliterator.OfDouble") @Test(dataProvider = "Spliterator.OfDouble")
public void testDoubleForEach(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) { public void testDoubleForEach(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
testForEach(exp, s, doubleBoxingConsumer()); testForEach(exp, s, doubleBoxingConsumer());
...@@ -1057,8 +1111,8 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -1057,8 +1111,8 @@ public class SpliteratorTraversingAndSplittingTest {
} }
private static <T, S extends Spliterator<T>> void visit(int depth, int curLevel, private static <T, S extends Spliterator<T>> void visit(int depth, int curLevel,
List<T> dest, S spliterator, UnaryOperator<Consumer<T>> boxingAdapter, List<T> dest, S spliterator, UnaryOperator<Consumer<T>> boxingAdapter,
int rootCharacteristics, boolean useTryAdvance) { int rootCharacteristics, boolean useTryAdvance) {
if (curLevel < depth) { if (curLevel < depth) {
long beforeSize = spliterator.getExactSizeIfKnown(); long beforeSize = spliterator.getExactSizeIfKnown();
Spliterator<T> split = spliterator.trySplit(); Spliterator<T> split = spliterator.trySplit();
...@@ -1187,13 +1241,13 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -1187,13 +1241,13 @@ public class SpliteratorTraversingAndSplittingTest {
assertTrue(leftSplit.estimateSize() < parentEstimateSize, assertTrue(leftSplit.estimateSize() < parentEstimateSize,
String.format("Left split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); String.format("Left split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
assertTrue(parentAndRightSplit.estimateSize() < parentEstimateSize, assertTrue(parentAndRightSplit.estimateSize() < parentEstimateSize,
String.format("Right split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); String.format("Right split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
} }
else { else {
assertTrue(leftSplit.estimateSize() <= parentEstimateSize, assertTrue(leftSplit.estimateSize() <= parentEstimateSize,
String.format("Left split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); String.format("Left split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
assertTrue(parentAndRightSplit.estimateSize() <= parentEstimateSize, assertTrue(parentAndRightSplit.estimateSize() <= parentEstimateSize,
String.format("Right split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); String.format("Right split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
} }
long leftSize = leftSplit.getExactSizeIfKnown(); long leftSize = leftSplit.getExactSizeIfKnown();
...@@ -1254,4 +1308,22 @@ public class SpliteratorTraversingAndSplittingTest { ...@@ -1254,4 +1308,22 @@ public class SpliteratorTraversingAndSplittingTest {
}); });
return result; return result;
} }
private void executeAndCatch(Class<? extends Exception> expected, Runnable r) {
Exception caught = null;
try {
r.run();
}
catch (Exception e) {
caught = e;
}
assertNotNull(caught,
String.format("No Exception was thrown, expected an Exception of %s to be thrown",
expected.getName()));
assertTrue(expected.isInstance(caught),
String.format("Exception thrown %s not an instance of %s",
caught.getClass().getName(), expected.getName()));
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册