java.util.ArrayDeque Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.util;
/**
* An implementation of Deque, backed by an array.
*
* ArrayDeques have no size limit, can not contain null element, and they are
* not thread-safe.
*
* All optional operations are supported, and the elements can be any objects.
*
* @param
* the type of elements in this collection
*
* @since 1.6
*/
public class ArrayDeque extends AbstractCollection implements Deque {
private static final int DEFAULT_SIZE = 16;
private enum DequeStatus {
Empty, Normal, Full;
}
private transient DequeStatus status;
private transient int modCount;
// the pointer of the head element
private transient int front;
// the pointer of the "next" position of the tail element
private transient int rear;
private transient E[] elements;
@SuppressWarnings("hiding")
private class ArrayDequeIterator implements Iterator {
private int pos;
private final int expectedModCount;
private boolean canRemove;
@SuppressWarnings("synthetic-access")
ArrayDequeIterator() {
super();
pos = front;
expectedModCount = modCount;
canRemove = false;
}
@SuppressWarnings("synthetic-access")
public boolean hasNext() {
if (expectedModCount != modCount) {
return false;
}
return hasNextInternal();
}
private boolean hasNextInternal() {
// canRemove means "next" method is called, and the Full
// status can ensure that this method is not called just
// after "remove" method is call.(so, canRemove can keep
// true after "next" method called)
return (pos != rear)
|| ((status == DequeStatus.Full) && !canRemove);
}
@SuppressWarnings( { "synthetic-access", "unchecked" })
public E next() {
if (hasNextInternal()) {
E result = (E) elements[pos];
if (expectedModCount == modCount && null != result) {
canRemove = true;
pos = circularBiggerPos(pos);
return result;
}
throw new ConcurrentModificationException();
}
throw new NoSuchElementException();
}
@SuppressWarnings("synthetic-access")
public void remove() {
if (canRemove) {
int removedPos = circularSmallerPos(pos);
if (expectedModCount == modCount
&& null != elements[removedPos]) {
removeInternal(removedPos, true);
canRemove = false;
return;
}
throw new ConcurrentModificationException();
}
throw new IllegalStateException();
}
}
/*
* NOTES:descendingIterator is not fail-fast, according to the documentation
* and test case.
*/
@SuppressWarnings("hiding")
private class ReverseArrayDequeIterator implements Iterator {
private int pos;
private final int expectedModCount;
private boolean canRemove;
@SuppressWarnings("synthetic-access")
ReverseArrayDequeIterator() {
super();
expectedModCount = modCount;
pos = circularSmallerPos(rear);
canRemove = false;
}
@SuppressWarnings("synthetic-access")
public boolean hasNext() {
if (expectedModCount != modCount) {
return false;
}
return hasNextInternal();
}
private boolean hasNextInternal() {
// canRemove means "next" method is called, and the Full
// status can ensure that this method is not called just
// after "remove" method is call.(so, canRemove can keep
// true after "next" method called)
return (circularBiggerPos(pos) != front)
|| ((status == DequeStatus.Full) && !canRemove);
}
@SuppressWarnings( { "synthetic-access", "unchecked" })
public E next() {
if (hasNextInternal()) {
E result = (E) elements[pos];
canRemove = true;
pos = circularSmallerPos(pos);
return result;
}
throw new NoSuchElementException();
}
@SuppressWarnings("synthetic-access")
public void remove() {
if (canRemove) {
removeInternal(circularBiggerPos(pos), false);
canRemove = false;
return;
}
throw new IllegalStateException();
}
}
/**
* Constructs a new empty instance of ArrayDeque big enough for 16 elements.
*/
public ArrayDeque() {
this(DEFAULT_SIZE);
}
/**
* Constructs a new empty instance of ArrayDeque big enough for specified
* number of elements.
*
* @param minSize
* the smallest size of the ArrayDeque
*/
@SuppressWarnings("unchecked")
public ArrayDeque(final int minSize) {
int size = countInitSize(minSize);
elements = (E[]) new Object[size];
front = rear = 0;
status = DequeStatus.Empty;
modCount = 0;
}
/*
* count out the size for a new deque, and ensure that size >= minSize
*/
private int countInitSize(final int minSize) {
return -1;
}
/**
* Constructs a new instance of ArrayDeque containing the elements of the
* specified collection, with the order returned by the collection's
* iterator.
*
* @param c
* the source of the elements
* @throws NullPointerException
* if the collection is null
*/
@SuppressWarnings("unchecked")
public ArrayDeque(Collection c) {
elements = (E[]) new Object[countInitSize(c.size())];
front = rear = 0;
status = DequeStatus.Empty;
modCount = 0;
Iterator it = c.iterator();
while (it.hasNext()) {
addLastImpl(it.next());
}
}
/**
* {@inheritDoc}
*
* @param e
* the element
* @throws NullPointerException
* if the element is null
* @see java.util.Deque#addFirst(java.lang.Object)
*/
public void addFirst(E e) {
offerFirst(e);
}
/**
* {@inheritDoc}
*
* @param e
* the element
* @throws NullPointerException
* if the element is null
* @see java.util.Deque#addLast(java.lang.Object)
*/
public void addLast(E e) {
addLastImpl(e);
}
/**
* {@inheritDoc}
*
* @param e
* the element
* @return true
* @throws NullPointerException
* if the element is null
* @see java.util.Deque#offerFirst(java.lang.Object)
*/
public boolean offerFirst(E e) {
checkNull(e);
checkAndExpand();
front = circularSmallerPos(front);
elements[front] = e;
resetStatus(true);
modCount++;
return true;
}
/**
* {@inheritDoc}
*
* @param e
* the element
* @return true if the operation succeeds or false if it fails
* @throws NullPointerException
* if the element is null
* @see java.util.Deque#offerLast(java.lang.Object)
*/
public boolean offerLast(E e) {
return addLastImpl(e);
}
/**
* Inserts the element at the tail of the deque.
*
* @param e
* the element
* @return true if the operation succeeds or false if it fails.
* @throws NullPointerException
* if the element is null
* @see java.util.Queue#offer(java.lang.Object)
*/
public boolean offer(E e) {
return addLastImpl(e);
}
/**
* Inserts the element to the tail of the deque.
*
* @param e
* the element
* @return true
* @see java.util.AbstractCollection#add(java.lang.Object)
*/
@Override
public boolean add(E e) {
return addLastImpl(e);
}
/**
* {@inheritDoc}
*
* @param e
* the element to push
* @throws NullPointerException
* if the element is null
* @see java.util.Deque#push(java.lang.Object)
*/
public void push(E e) {
offerFirst(e);
}
/**
* {@inheritDoc}
*
* @return the head element
* @throws NoSuchElementException
* if the deque is empty
* @see java.util.Deque#removeFirst()
*/
public E removeFirst() {
checkEmpty();
return removePollFirstImpl();
}
/**
* Gets and removes the head element of this deque. This method throws an
* exception if the deque is empty.
*
* @return the head element
* @throws NoSuchElementException
* if the deque is empty
* @see java.util.Queue#remove()
*/
public E remove() {
return removeFirst();
}
/**
* {@inheritDoc}
*
* @return the head element
* @throws NoSuchElementException
* if the deque is empty
* @see java.util.Deque#pop()
*/
public E pop() {
return removeFirst();
}
/**
* {@inheritDoc}
*
* @return the tail element
* @throws NoSuchElementException
* if the deque is empty
* @see java.util.Deque#removeLast()
*/
public E removeLast() {
checkEmpty();
return removeLastImpl();
}
/**
* {@inheritDoc}
*
* @return the head element or null if the deque is empty
* @see java.util.Deque#pollFirst()
*/
public E pollFirst() {
return (status == DequeStatus.Empty) ? null : removePollFirstImpl();
}
/**
* Gets and removes the head element of this deque. This method returns null
* if the deque is empty.
*
* @return the head element or null if the deque is empty
* @see java.util.Queue#poll()
*/
public E poll() {
return pollFirst();
}
/**
* {@inheritDoc}
*
* @return the tail element or null if the deque is empty
* @see java.util.Deque#pollLast()
*/
public E pollLast() {
return (status == DequeStatus.Empty) ? null : removeLastImpl();
}
/**
* {@inheritDoc}
*
* @return the head element
* @throws NoSuchElementException
* if the deque is empty
* @see java.util.Deque#getFirst()
*/
public E getFirst() {
checkEmpty();
return elements[front];
}
/**
* Gets but does not remove the head element of this deque. It throws an
* exception if the deque is empty.
*
* @return the head element
* @throws NoSuchElementException
* if the deque is empty
* @see java.util.Queue#element()
*/
public E element() {
return getFirst();
}
/**
* {@inheritDoc}
*
* @return the tail element
* @throws NoSuchElementException
* if the deque is empty
* @see java.util.Deque#getLast()
*/
public E getLast() {
checkEmpty();
return elements[circularSmallerPos(rear)];
}
/**
* {@inheritDoc}
*
* @return the head element or null if the deque is empty
* @see java.util.Deque#peekFirst()
*/
public E peekFirst() {
return (status == DequeStatus.Empty) ? null : elements[front];
}
/**
* Gets but not removes the head element of this deque. This method returns
* null if the deque is empty.
*
* @return the head element or null if the deque is empty
* @see java.util.Queue#peek()
*/
public E peek() {
return (status == DequeStatus.Empty) ? null : elements[front];
}
/**
* {@inheritDoc}
*
* @return the tail element or null if the deque is empty
* @see java.util.Deque#peekLast()
*/
public E peekLast() {
return (status == DequeStatus.Empty) ? null
: elements[circularSmallerPos(rear)];
}
private void checkNull(E e) {
if (null == e) {
throw new NullPointerException();
}
}
private void checkEmpty() {
if (status == DequeStatus.Empty) {
throw new NoSuchElementException();
}
}
private int circularSmallerPos(int current) {
return (current - 1 < 0) ? (elements.length - 1) : current - 1;
}
private int circularBiggerPos(int current) {
return (current + 1 >= elements.length) ? 0 : current + 1;
}
@SuppressWarnings("unchecked")
/*
* If array of elements is full, there will be a new bigger array to store
* the elements.
*/
private void checkAndExpand() {
if (status != DequeStatus.Full) {
return;
}
if (Integer.MAX_VALUE == elements.length) {
throw new IllegalStateException();
}
int length = elements.length;
int newLength = length << 1;
// bigger than Integer.MAX_VALUE
if (newLength < 0) {
newLength = Integer.MAX_VALUE;
}
E[] newElements = (E[]) new Object[newLength];
System.arraycopy(elements, front, newElements, 0, length - front);
System.arraycopy(elements, 0, newElements, length - front, front);
front = 0;
rear = length;
status = DequeStatus.Normal;
elements = newElements;
}
/**
* Resets the status after adding or removing operation.
*
* @param adding
* if the method is called after an "adding" operation
*/
private void resetStatus(boolean adding) {
if (front == rear) {
status = adding ? DequeStatus.Full : DequeStatus.Empty;
} else {
status = DequeStatus.Normal;
}
}
private boolean addLastImpl(E e) {
checkNull(e);
checkAndExpand();
elements[rear] = e;
rear = circularBiggerPos(rear);
resetStatus(true);
modCount++;
return true;
}
private E removePollFirstImpl() {
E element = elements[front];
elements[front] = null;
front = circularBiggerPos(front);
resetStatus(false);
modCount++;
return element;
}
private E removeLastImpl() {
int last = circularSmallerPos(rear);
E element = elements[last];
elements[last] = null;
rear = last;
resetStatus(false);
modCount++;
return element;
}
/**
* {@inheritDoc}
*
* @param obj
* the element to be removed
* @return true if the operation succeeds or false if the deque does not
* contain the element
* @see java.util.Deque#removeFirstOccurrence(java.lang.Object)
*/
public boolean removeFirstOccurrence(Object obj) {
return removeFirstOccurrenceImpl(obj);
}
/**
* Removes the first equivalent element of the specified object. If the
* deque does not contain the element, it is unchanged and returns false.
*
* @param obj
* the element to be removed
* @return true if the operation succeeds or false if the deque does not
* contain the element
* @see java.util.AbstractCollection#remove(java.lang.Object)
*/
@Override
public boolean remove(Object obj) {
return removeFirstOccurrenceImpl(obj);
}
/**
* {@inheritDoc}
*
* @param obj
* the element to be removed
* @return true if the operation succeeds or false if the deque does not
* contain the element.
* @see java.util.Deque#removeLastOccurrence(java.lang.Object)
*/
public boolean removeLastOccurrence(final Object obj) {
if (null != obj) {
Iterator iter = descendingIterator();
while (iter.hasNext()) {
if (iter.next().equals(obj)) {
iter.remove();
return true;
}
}
}
return false;
}
private boolean removeFirstOccurrenceImpl(final Object obj) {
if (null != obj) {
Iterator iter = iterator();
while (iter.hasNext()) {
if (iter.next().equals(obj)) {
iter.remove();
return true;
}
}
}
return false;
}
/*
* Removes the element in the cursor position and shifts front elements to
* fill the gap if frontShift is true, shifts rear elements otherwise.
*
*/
private void removeInternal(final int current, final boolean frontShift) {
int cursor = current;
if (frontShift) {
while (cursor != front) {
int next = circularSmallerPos(cursor);
elements[cursor] = elements[next];
cursor = next;
}
front = circularBiggerPos(front);
} else {
while (cursor != rear) {
int next = circularBiggerPos(cursor);
elements[cursor] = elements[next];
cursor = next;
}
rear = circularSmallerPos(rear);
}
elements[cursor] = null;
resetStatus(false);
}
/**
* Returns the size of the deque.
*
* @return the size of the deque
* @see java.util.AbstractCollection#size()
*/
@Override
public int size() {
if (status == DequeStatus.Full) {
return elements.length;
}
return (front <= rear) ? (rear - front)
: (rear + elements.length - front);
}
/**
* Returns true if the deque has no elements.
*
* @return true if the deque has no elements, false otherwise
* @see java.util.AbstractCollection#isEmpty()
*/
@Override
public boolean isEmpty() {
return 0 == size();
}
/**
* Returns true if the specified element is in the deque.
*
* @param obj
* the element
* @return true if the element is in the deque, false otherwise
* @see java.util.AbstractCollection#contains(java.lang.Object)
*/
@SuppressWarnings("cast")
@Override
public boolean contains(final Object obj) {
if (null != obj) {
Iterator it = new ArrayDequeIterator();
while (it.hasNext()) {
if (obj.equals((E) it.next())) {
return true;
}
}
}
return false;
}
/**
* Empty the deque.
*
* @see java.util.AbstractCollection#clear()
*/
@SuppressWarnings("cast")
@Override
public void clear() {
if (status != DequeStatus.Empty) {
int cursor = front;
do {
elements[cursor] = null;
cursor = circularBiggerPos(cursor);
} while (cursor != rear);
status = DequeStatus.Empty;
}
front = rear = 0;
modCount = 0;
}
/**
* Returns the iterator of the deque. The elements will be ordered from head
* to tail.
*
* @return the iterator
* @see java.util.AbstractCollection#iterator()
*/
@SuppressWarnings("synthetic-access")
@Override
public Iterator iterator() {
return new ArrayDequeIterator();
}
/**
* {@inheritDoc}
*
* @return the reverse order Iterator
* @see java.util.Deque#descendingIterator()
*/
public Iterator descendingIterator() {
return new ReverseArrayDequeIterator();
}
}