--- jsr166/src/main/java/util/ArrayDeque.java 2016/10/18 00:33:05 1.77 +++ jsr166/src/main/java/util/ArrayDeque.java 2016/10/27 18:46:23 1.94 @@ -93,7 +93,7 @@ public class ArrayDeque extends Abstr private void grow(int needed) { // overflow-conscious code // checkInvariants(); - int oldCapacity = elements.length; + final int oldCapacity = elements.length; int newCapacity; // Double size if small; else grow by 50% int jump = (oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1); @@ -115,8 +115,7 @@ public class ArrayDeque extends Abstr /** Capacity calculation for edge conditions, especially overflow. */ private int newCapacity(int needed, int jump) { - int oldCapacity = elements.length; - int minCapacity; + final int oldCapacity = elements.length, minCapacity; if ((minCapacity = oldCapacity + needed) - MAX_ARRAY_SIZE > 0) { if (minCapacity < 0) throw new IllegalStateException("Sorry, deque too big"); @@ -134,9 +133,9 @@ public class ArrayDeque extends Abstr * to ensure that it can hold at least the given number of elements. * * @param minCapacity the desired minimum capacity - * @since 9 + * @since TBD */ - public void ensureCapacity(int minCapacity) { + /* public */ void ensureCapacity(int minCapacity) { if (minCapacity > elements.length) grow(minCapacity - elements.length); // checkInvariants(); @@ -145,9 +144,9 @@ public class ArrayDeque extends Abstr /** * Minimizes the internal storage of this collection. * - * @since 9 + * @since TBD */ - public void trimToSize() { + /* public */ void trimToSize() { if (size < elements.length) { elements = toArray(); head = 0; @@ -187,63 +186,64 @@ public class ArrayDeque extends Abstr Object[] elements = c.toArray(); // defend against c.toArray (incorrectly) not returning Object[] // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) + size = elements.length; if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, size, Object[].class); for (Object obj : elements) Objects.requireNonNull(obj); - size = elements.length; this.elements = elements; } /** - * Returns the array index of the last element. - * May return invalid index -1 if there are no elements. + * Increments i, mod modulus. + * Precondition and postcondition: 0 <= i < modulus. */ - final int tail() { - return add(head, size - 1, elements.length); + static final int inc(int i, int modulus) { + if (++i >= modulus) i = 0; + return i; } /** - * Adds i and j, mod modulus. - * Precondition and postcondition: 0 <= i < modulus, 0 <= j <= modulus. + * Decrements i, mod modulus. + * Precondition and postcondition: 0 <= i < modulus. */ - static final int add(int i, int j, int modulus) { - if ((i += j) - modulus >= 0) i -= modulus; + static final int dec(int i, int modulus) { + if (--i < 0) i = modulus - 1; return i; } /** - * Increments i, mod modulus. - * Precondition and postcondition: 0 <= i < modulus. + * Adds i and j, mod modulus. + * Precondition and postcondition: 0 <= i < modulus, 0 <= j <= modulus. */ - static final int inc(int i, int modulus) { - if (++i == modulus) i = 0; + static final int add(int i, int j, int modulus) { + if ((i += j) - modulus >= 0) i -= modulus; return i; } /** - * Decrements i, mod modulus. - * Precondition and postcondition: 0 <= i < modulus. + * Returns the array index of the last element. + * May return invalid index -1 if there are no elements. */ - static final int dec(int i, int modulus) { - if (--i < 0) i += modulus; - return i; + final int tail() { + return add(head, size - 1, elements.length); } /** * Returns element at array index i. */ @SuppressWarnings("unchecked") - final E elementAt(int i) { + private E elementAt(int i) { return (E) elements[i]; } /** * A version of elementAt that checks for null elements. * This check doesn't catch all possible comodifications, - * but does catch ones that corrupt traversal. + * but does catch ones that corrupt traversal. It's a little + * surprising that javac allows this abuse of generics. */ - E checkedElementAt(Object[] elements, int i) { + static final E nonNullElementAt(Object[] elements, int i) { @SuppressWarnings("unchecked") E e = (E) elements[i]; if (e == null) throw new ConcurrentModificationException(); @@ -264,11 +264,16 @@ public class ArrayDeque extends Abstr // checkInvariants(); Objects.requireNonNull(e); Object[] elements; - int capacity, s = size; - while (s == (capacity = (elements = this.elements).length)) + int capacity, h; + final int s; + if ((s = size) == (capacity = (elements = this.elements).length)) { grow(1); - elements[head = dec(head, capacity)] = e; + capacity = (elements = this.elements).length; + } + if ((h = head - 1) < 0) h = capacity - 1; + elements[head = h] = e; size = s + 1; + // checkInvariants(); } /** @@ -283,11 +288,15 @@ public class ArrayDeque extends Abstr // checkInvariants(); Objects.requireNonNull(e); Object[] elements; - int capacity, s = size; - while (s == (capacity = (elements = this.elements).length)) + int capacity; + final int s; + if ((s = size) == (capacity = (elements = this.elements).length)) { grow(1); + capacity = (elements = this.elements).length; + } elements[add(head, s, capacity)] = e; size = s + 1; + // checkInvariants(); } /** @@ -301,23 +310,13 @@ public class ArrayDeque extends Abstr * @throws NullPointerException if the specified collection or any * of its elements are null */ - @Override public boolean addAll(Collection c) { + final int s = size, needed = c.size() - (elements.length - s); + if (needed > 0) + grow(needed); + c.forEach((e) -> addLast(e)); // checkInvariants(); - Object[] a, elements; - int len, capacity, s = size; - if ((len = (a = c.toArray()).length) == 0) - return false; - while ((capacity = (elements = this.elements).length) - s < len) - grow(len - (capacity - s)); - int i = add(head, s, capacity); - for (Object x : a) { - Objects.requireNonNull(x); - elements[i] = x; - i = inc(i, capacity); - size++; - } - return true; + return size > s; } /** @@ -349,10 +348,10 @@ public class ArrayDeque extends Abstr */ public E removeFirst() { // checkInvariants(); - E x = pollFirst(); - if (x == null) + E e = pollFirst(); + if (e == null) throw new NoSuchElementException(); - return x; + return e; } /** @@ -360,21 +359,22 @@ public class ArrayDeque extends Abstr */ public E removeLast() { // checkInvariants(); - E x = pollLast(); - if (x == null) + E e = pollLast(); + if (e == null) throw new NoSuchElementException(); - return x; + return e; } public E pollFirst() { // checkInvariants(); - final int s, h; - if ((s = size) == 0) + int s, h; + if ((s = size) <= 0) return null; final Object[] elements = this.elements; @SuppressWarnings("unchecked") E e = (E) elements[h = head]; elements[h] = null; - head = inc(h, elements.length); + if (++h >= elements.length) h = 0; + head = h; size = s - 1; return e; } @@ -382,7 +382,7 @@ public class ArrayDeque extends Abstr public E pollLast() { // checkInvariants(); final int s, tail; - if ((s = size) == 0) + if ((s = size) <= 0) return null; final Object[] elements = this.elements; @SuppressWarnings("unchecked") @@ -397,27 +397,34 @@ public class ArrayDeque extends Abstr */ public E getFirst() { // checkInvariants(); - if (size == 0) throw new NoSuchElementException(); + if (size <= 0) throw new NoSuchElementException(); return elementAt(head); } /** * @throws NoSuchElementException {@inheritDoc} */ + @SuppressWarnings("unchecked") public E getLast() { // checkInvariants(); - if (size == 0) throw new NoSuchElementException(); - return elementAt(tail()); + final int s; + if ((s = size) <= 0) throw new NoSuchElementException(); + final Object[] elements = this.elements; + return (E) elements[add(head, s - 1, elements.length)]; } public E peekFirst() { // checkInvariants(); - return (size == 0) ? null : elementAt(head); + return (size <= 0) ? null : elementAt(head); } + @SuppressWarnings("unchecked") public E peekLast() { // checkInvariants(); - return (size == 0) ? null : elementAt(tail()); + final int s; + if ((s = size) <= 0) return null; + final Object[] elements = this.elements; + return (E) elements[add(head, s - 1, elements.length)]; } /** @@ -433,15 +440,19 @@ public class ArrayDeque extends Abstr * @return {@code true} if the deque contained the specified element */ public boolean removeFirstOccurrence(Object o) { - // checkInvariants(); if (o != null) { final Object[] elements = this.elements; final int capacity = elements.length; - for (int k = size, i = head; --k >= 0; i = inc(i, capacity)) { - if (o.equals(elements[i])) { - delete(i); - return true; - } + int i, end, to, todo; + todo = (end = (i = head) + size) + - (to = (capacity - end >= 0) ? end : capacity); + for (;; to = todo, i = 0, todo = 0) { + for (; i < to; i++) + if (o.equals(elements[i])) { + delete(i); + return true; + } + if (todo == 0) break; } } return false; @@ -463,12 +474,15 @@ public class ArrayDeque extends Abstr if (o != null) { final Object[] elements = this.elements; final int capacity = elements.length; - for (int k = size, i = add(head, k - 1, capacity); - --k >= 0; i = dec(i, capacity)) { - if (o.equals(elements[i])) { - delete(i); - return true; - } + int i, to, end, todo; + todo = (to = ((end = (i = tail()) - size) >= -1) ? end : -1) - end; + for (;; to = (i = capacity - 1) - todo, todo = 0) { + for (; i > to; i--) + if (o.equals(elements[i])) { + delete(i); + return true; + } + if (todo == 0) break; } } return false; @@ -616,7 +630,7 @@ public class ArrayDeque extends Abstr System.arraycopy(elements, h, elements, h + 1, front - (i + 1)); } elements[h] = null; - head = inc(h, capacity); + if ((head = (h + 1)) >= capacity) head = 0; size--; // checkInvariants(); return false; @@ -689,59 +703,71 @@ public class ArrayDeque extends Abstr DeqIterator() { cursor = head; } - int advance(int i, int modulus) { - return inc(i, modulus); - } - - void doRemove() { - if (delete(lastRet)) - // if left-shifted, undo advance in next() - cursor = dec(cursor, elements.length); - } - public final boolean hasNext() { return remaining > 0; } - public final E next() { - if (remaining == 0) + public E next() { + if (remaining <= 0) throw new NoSuchElementException(); - E e = checkedElementAt(elements, cursor); + final Object[] elements = ArrayDeque.this.elements; + E e = nonNullElementAt(elements, cursor); lastRet = cursor; - cursor = advance(cursor, elements.length); + if (++cursor >= elements.length) cursor = 0; remaining--; return e; } + void postDelete(boolean leftShifted) { + if (leftShifted) + if (--cursor < 0) cursor = elements.length - 1; + } + public final void remove() { if (lastRet < 0) throw new IllegalStateException(); - doRemove(); + postDelete(delete(lastRet)); lastRet = -1; } - public final void forEachRemaining(Consumer action) { - Objects.requireNonNull(action); - final Object[] elements = ArrayDeque.this.elements; - final int capacity = elements.length; - int k = remaining; - remaining = 0; - for (int i = cursor; --k >= 0; i = advance(i, capacity)) - action.accept(checkedElementAt(elements, i)); + public void forEachRemaining(Consumer action) { + final int k; + if ((k = remaining) > 0) { + remaining = 0; + ArrayDeque.forEachRemaining(action, elements, cursor, k); + if ((lastRet = cursor + k - 1) >= elements.length) + lastRet -= elements.length; + } } } private class DescendingIterator extends DeqIterator { DescendingIterator() { cursor = tail(); } - @Override int advance(int i, int modulus) { - return dec(i, modulus); + public final E next() { + if (remaining <= 0) + throw new NoSuchElementException(); + final Object[] elements = ArrayDeque.this.elements; + E e = nonNullElementAt(elements, cursor); + lastRet = cursor; + if (--cursor < 0) cursor = elements.length - 1; + remaining--; + return e; } - @Override void doRemove() { - if (!delete(lastRet)) - // if right-shifted, undo advance in next - cursor = inc(cursor, elements.length); + void postDelete(boolean leftShifted) { + if (!leftShifted) + if (++cursor >= elements.length) cursor = 0; + } + + public final void forEachRemaining(Consumer action) { + final int k; + if ((k = remaining) > 0) { + remaining = 0; + forEachRemainingDescending(action, elements, cursor, k); + if ((lastRet = cursor - (k - 1)) < 0) + lastRet += elements.length; + } } } @@ -798,22 +824,19 @@ public class ArrayDeque extends Abstr } public void forEachRemaining(Consumer action) { - Objects.requireNonNull(action); - final Object[] elements = ArrayDeque.this.elements; - final int capacity = elements.length; - int k = remaining(); + final int k = remaining(); // side effect! remaining = 0; - for (int i = cursor; --k >= 0; i = inc(i, capacity)) - action.accept(checkedElementAt(elements, i)); + ArrayDeque.forEachRemaining(action, elements, cursor, k); } public boolean tryAdvance(Consumer action) { Objects.requireNonNull(action); - if (remaining() == 0) + final int k; + if ((k = remaining()) <= 0) return false; - action.accept(checkedElementAt(elements, cursor)); - cursor = inc(cursor, elements.length); - remaining--; + action.accept(nonNullElementAt(elements, cursor)); + if (++cursor >= elements.length) cursor = 0; + remaining = k - 1; return true; } @@ -829,37 +852,79 @@ public class ArrayDeque extends Abstr } } - @Override + @SuppressWarnings("unchecked") public void forEach(Consumer action) { - // checkInvariants(); Objects.requireNonNull(action); final Object[] elements = this.elements; final int capacity = elements.length; - for (int k = size, i = head; --k >= 0; i = inc(i, capacity)) - action.accept(elementAt(i)); + int i, end, to, todo; + todo = (end = (i = head) + size) + - (to = (capacity - end >= 0) ? end : capacity); + for (;; to = todo, i = 0, todo = 0) { + for (; i < to; i++) + action.accept((E) elements[i]); + if (todo == 0) break; + } // checkInvariants(); } /** + * Calls action on remaining elements, starting at index i and + * traversing in ascending order. A variant of forEach that also + * checks for concurrent modification, for use in iterators. + */ + static void forEachRemaining( + Consumer action, Object[] elements, int i, int remaining) { + Objects.requireNonNull(action); + final int capacity = elements.length; + int end, to, todo; + todo = (end = i + remaining) + - (to = (capacity - end >= 0) ? end : capacity); + for (;; to = todo, i = 0, todo = 0) { + for (; i < to; i++) + action.accept(nonNullElementAt(elements, i)); + if (todo == 0) break; + } + } + + static void forEachRemainingDescending( + Consumer action, Object[] elements, int i, int remaining) { + Objects.requireNonNull(action); + final int capacity = elements.length; + int end, to, todo; + todo = (to = ((end = i - remaining) >= -1) ? end : -1) - end; + for (;; to = (i = capacity - 1) - todo, todo = 0) { + for (; i > to; i--) + action.accept(nonNullElementAt(elements, i)); + if (todo == 0) break; + } + } + + /** * Replaces each element of this deque with the result of applying the * operator to that element, as specified by {@link List#replaceAll}. * * @param operator the operator to apply to each element - * @since 9 + * @since TBD */ - public void replaceAll(UnaryOperator operator) { + /* public */ void replaceAll(UnaryOperator operator) { Objects.requireNonNull(operator); final Object[] elements = this.elements; final int capacity = elements.length; - for (int k = size, i = head; --k >= 0; i = inc(i, capacity)) - elements[i] = operator.apply(elementAt(i)); + int i, end, to, todo; + todo = (end = (i = head) + size) + - (to = (capacity - end >= 0) ? end : capacity); + for (;; to = todo, i = 0, todo = 0) { + for (; i < to; i++) + elements[i] = operator.apply(elementAt(i)); + if (todo == 0) break; + } // checkInvariants(); } /** * @throws NullPointerException {@inheritDoc} */ - @Override public boolean removeIf(Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); @@ -868,7 +933,6 @@ public class ArrayDeque extends Abstr /** * @throws NullPointerException {@inheritDoc} */ - @Override public boolean removeAll(Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); @@ -877,7 +941,6 @@ public class ArrayDeque extends Abstr /** * @throws NullPointerException {@inheritDoc} */ - @Override public boolean retainAll(Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); @@ -890,26 +953,29 @@ public class ArrayDeque extends Abstr final int capacity = elements.length; int i = head, j = i, remaining = size, deleted = 0; try { - for (; remaining > 0; remaining--, i = inc(i, capacity)) { + for (; remaining > 0; remaining--) { @SuppressWarnings("unchecked") E e = (E) elements[i]; if (filter.test(e)) deleted++; else { if (j != i) elements[j] = e; - j = inc(j, capacity); + if (++j >= capacity) j = 0; } + if (++i >= capacity) i = 0; } return deleted > 0; } catch (Throwable ex) { - for (; remaining > 0; - remaining--, i = inc(i, capacity), j = inc(j, capacity)) - elements[j] = elements[i]; + if (deleted > 0) + for (; remaining > 0; remaining--) { + elements[j] = elements[i]; + if (++i >= capacity) i = 0; + if (++j >= capacity) j = 0; + } throw ex; } finally { size -= deleted; - for (; --deleted >= 0; j = inc(j, capacity)) - elements[j] = null; + clearSlice(elements, j, deleted); // checkInvariants(); } } @@ -926,9 +992,15 @@ public class ArrayDeque extends Abstr if (o != null) { final Object[] elements = this.elements; final int capacity = elements.length; - for (int k = size, i = head; --k >= 0; i = inc(i, capacity)) - if (o.equals(elements[i])) - return true; + int i, end, to, todo; + todo = (end = (i = head) + size) + - (to = (capacity - end >= 0) ? end : capacity); + for (;; to = todo, i = 0, todo = 0) { + for (; i < to; i++) + if (o.equals(elements[i])) + return true; + if (todo == 0) break; + } } return false; } @@ -955,21 +1027,23 @@ public class ArrayDeque extends Abstr * The deque will be empty after this call returns. */ public void clear() { - final Object[] elements = this.elements; - final int capacity = elements.length; - final int h = this.head; - final int s = size; - if (capacity - h >= s) - Arrays.fill(elements, h, h + s, null); - else { - Arrays.fill(elements, h, capacity, null); - Arrays.fill(elements, 0, s - capacity + h, null); - } + clearSlice(elements, head, size); size = head = 0; // checkInvariants(); } /** + * Nulls out count elements, starting at array index from. + */ + private static void clearSlice(Object[] elements, int from, int count) { + final int capacity = elements.length, end = from + count; + final int leg = (capacity - end >= 0) ? end : capacity; + Arrays.fill(elements, from, leg, null); + if (leg != end) + Arrays.fill(elements, 0, end - capacity, null); + } + + /** * Returns an array containing all of the elements in this deque * in proper sequence (from first to last element). * @@ -983,11 +1057,23 @@ public class ArrayDeque extends Abstr * @return an array containing all of the elements in this deque */ public Object[] toArray() { - final int head = this.head; - final int firstLeg; - Object[] a = Arrays.copyOfRange(elements, head, head + size); - if ((firstLeg = elements.length - head) < size) - System.arraycopy(elements, 0, a, firstLeg, size - firstLeg); + return toArray(Object[].class); + } + + private T[] toArray(Class klazz) { + final Object[] elements = this.elements; + final int capacity = elements.length; + final int head = this.head, end = head + size; + final T[] a; + if (end >= 0) { + a = Arrays.copyOfRange(elements, head, end, klazz); + } else { + // integer overflow! + a = Arrays.copyOfRange(elements, 0, size, klazz); + System.arraycopy(elements, head, a, 0, capacity - head); + } + if (end - capacity > 0) + System.arraycopy(elements, 0, a, capacity - head, end - capacity); return a; } @@ -1029,20 +1115,18 @@ public class ArrayDeque extends Abstr */ @SuppressWarnings("unchecked") public T[] toArray(T[] a) { + final int size = this.size; + if (size > a.length) + return toArray((Class) a.getClass()); final Object[] elements = this.elements; - final int head = this.head; - final int firstLeg; - boolean wrap = (firstLeg = elements.length - head) < size; - if (size > a.length) { - a = (T[]) Arrays.copyOfRange(elements, head, head + size, - a.getClass()); - } else { - System.arraycopy(elements, head, a, 0, wrap ? firstLeg : size); - if (size < a.length) - a[size] = null; - } - if (wrap) - System.arraycopy(elements, 0, a, firstLeg, size - firstLeg); + final int capacity = elements.length; + final int head = this.head, end = head + size; + final int front = (capacity - end >= 0) ? size : capacity - head; + System.arraycopy(elements, head, a, 0, front); + if (front != size) + System.arraycopy(elements, 0, a, capacity - head, end - capacity); + if (size < a.length) + a[size] = null; return a; } @@ -1085,8 +1169,14 @@ public class ArrayDeque extends Abstr // Write out elements in order. final Object[] elements = this.elements; final int capacity = elements.length; - for (int k = size, i = head; --k >= 0; i = inc(i, capacity)) - s.writeObject(elements[i]); + int i, end, to, todo; + todo = (end = (i = head) + size) + - (to = (capacity - end >= 0) ? end : capacity); + for (;; to = todo, i = 0, todo = 0) { + for (; i < to; i++) + s.writeObject(elements[i]); + if (todo == 0) break; + } } /** @@ -1109,17 +1199,16 @@ public class ArrayDeque extends Abstr } /** debugging */ - private void checkInvariants() { + void checkInvariants() { try { int capacity = elements.length; - assert size >= 0 && size <= capacity; - assert head >= 0 && ((capacity == 0 && head == 0 && size == 0) - || head < capacity); - assert size == 0 - || (elements[head] != null && elements[tail()] != null); - assert size == capacity - || (elements[dec(head, capacity)] == null - && elements[inc(tail(), capacity)] == null); + // assert size >= 0 && size <= capacity; + // assert head >= 0; + // assert capacity == 0 || head < capacity; + // assert size == 0 || elements[head] != null; + // assert size == 0 || elements[tail()] != null; + // assert size == capacity || elements[dec(head, capacity)] == null; + // assert size == capacity || elements[inc(tail(), capacity)] == null; } catch (Throwable t) { System.err.printf("head=%d size=%d capacity=%d%n", head, size, elements.length);