blob: fc911c5cce289f1e0d70bf86e606b99f0fc8192d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 Obeo.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.acceleo.common.utils;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
/**
* Array-based implementation of a double-ended queue.
* <p>
* This {@link Deque} accepts <code>null</code> values, however as the {@link java.util.Queue} interfaces
* specifies it, calls to {@link #peek()} or {@link #poll()} will both return <code>null</code> when this
* Queue is empty. Calls to {@link #peekFirst()} and {@link #peekLast()}, however, will throw
* {@link NoSuchElementException}s when the queue is empty.
* </p>
* <p>
* Most operations on the {@link CircularArrayDeque} execute in constant time. This includes
* {@link #addFirst(Object)}, {@link #addLast(Object)}, {@link #removeFirst()}, {@link #removeLast()} and
* {@link #get(int)}. Other random-access operations such as {@link #add(Object)} and {@link #remove(Object)}
* execute in amortized linear time, with the constant being lower than for the {@link java.util.ArrayList}.
* </p>
* <p>
* This implementation of a double-ended queue is backed by an array which size we'll always maintain to be a
* power of two. Adding and removing elements from both ends we'll end up moving the "head" and "tail"
* pointers by one to either the left or the right. This deque is considered empty when these two pointers are
* equal.
* </p>
* <p>
* Whenever "head" or "tail" go below 0 or above the underlying array's length, we'll use a modulo in order
* for it to circle through. As we know the length of the array is always a power of two, we're optimizing the
* modulo by using a binary mask (x % 2^y == x & (2^y - 1)).
* </p>
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
* @param <E>
* Type of the elements contained by this Deque.
* @since 3.0
*/
public final class CircularArrayDeque<E> extends AbstractList<E> implements Deque<E>, Externalizable, RandomAccess {
/** The default initial capacity of our Deques. */
private static final int DEFAULT_INITIAL_CAPACITY = 16;
/** Serial version UID, this will be used during deserialization. */
private static final long serialVersionUID = 5259962911151126606L;
/**
* Array in which the elements of this deque are stored. The capacity of a {@link CircularArrayDeque} is
* the length of this array, which will be maintained as a power of two. Note that the array will never be
* full as will double its size whenever we reach its full capacity.
*/
transient E[] data;
/** Index in the backing array of the first element of this deque. */
transient int head;
/** Index in the backing array of the last element of this deque. */
transient int tail;
/**
* Constructs an empty deque with an initial capacity of {@value #DEFAULT_INITIAL_CAPACITY} elements.
*/
@SuppressWarnings("unchecked")
public CircularArrayDeque() {
data = (E[])new Object[DEFAULT_INITIAL_CAPACITY];
}
/**
* Constructs a deque holding a copy of the given collection. The order of the elements in this deque will
* be the same as the given collection's iterator order.
*
* @param collection
* The collection whose elements are to be pushed on this deque.
*/
public CircularArrayDeque(Collection<? extends E> collection) {
initialize(collection.size());
addAll(collection);
}
/**
* Constructs an empty deque with an initial capacity sufficient to hold the given number of elements.
* <p>
* If the expected element count is negative, the initial capacity of this deque will be <code>4</code>.
* </p>
*
* @param elementCount
* The number of elements we expect to hold in this deque.
*/
public CircularArrayDeque(int elementCount) {
initialize(elementCount);
}
/**
* Get the closest power of two greater than <code>number</code>.
*
* @param number
* Number for which we seek the closest power of two.
* @return The closest power of two greater than <code>number</code>.
*/
private static int getNextPowerOfTwo(int number) {
// Checkstyle needs variables...
final int pow3 = 8;
final int pow4 = 16;
int powerOfTwo = number - 1;
powerOfTwo |= powerOfTwo >> 1;
powerOfTwo |= powerOfTwo >> 2;
powerOfTwo |= powerOfTwo >> 4;
powerOfTwo |= powerOfTwo >> pow3;
powerOfTwo |= powerOfTwo >> pow4;
powerOfTwo++;
return powerOfTwo;
}
/**
* {@inheritDoc}
*
* @see java.util.List#add(java.lang.Object)
*/
@Override
public boolean add(E element) {
addLast(element);
return true;
}
/**
* {@inheritDoc}
*
* @see java.util.List#add(int, java.lang.Object)
*/
@Override
public void add(int index, E element) {
int size = size();
if (index == 0) {
addFirst(element);
} else if (index == size) {
addLast(element);
} else {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(String.valueOf(index));
}
/*
* If we are close to head, we'll move elements to the left. If we are closer to tail, we'll move
* them to the right.
*/
final int mask = data.length - 1;
final int insertionIndex;
if (index > (size / 2)) {
insertionIndex = (head + index) & mask;
for (int i = tail; i != insertionIndex; i = (i - 1) & mask) {
data[i] = data[(i - 1) & mask];
}
tail = (tail + 1) & mask;
} else {
head = (head - 1) & mask;
insertionIndex = (head + index) & mask;
for (int i = head; i != insertionIndex; i = (i + 1) & mask) {
data[i] = data[(i + 1) & mask];
}
}
data[insertionIndex] = element;
modCount++;
if (head == tail) {
doubleCapacity();
}
}
}
/**
* {@inheritDoc}
*
* @see java.util.List#addAll(java.util.Collection)
*/
@Override
public boolean addAll(Collection<? extends E> collection) {
for (E element : collection) {
addLast(element);
}
return !collection.isEmpty();
}
/**
* {@inheritDoc}
*
* @see java.util.List#addAll(int, java.util.Collection)
*/
@Override
@SuppressWarnings("unchecked")
public boolean addAll(int index, Collection<? extends E> collection) {
int size = size();
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(String.valueOf(index));
}
if (collection.isEmpty()) {
return false;
}
ensureCapacity(size + collection.size());
if (index == 0) {
E[] newValues = (E[])new Object[collection.size()];
newValues = collection.toArray(newValues);
for (int i = newValues.length - 1; i >= 0; i--) {
addFirst(newValues[i]);
}
} else if (index == size) {
Iterator<? extends E> iterator = collection.iterator();
while (iterator.hasNext()) {
addLast(iterator.next());
}
} else {
/*
* If we are close to head, we'll move elements to the left. If we are closer to tail, we'll move
* them to the right.
*/
final int mask = data.length - 1;
int insertionIndex;
if (index > (size / 2)) {
insertionIndex = (head + index) & mask;
tail = (tail + collection.size()) & mask;
for (int i = tail; i != ((insertionIndex + collection.size()) & mask); i = (i - 1) & mask) {
data[(i - 1) & mask] = data[(i - 1 - collection.size()) & mask];
}
} else {
head = (head - collection.size()) & mask;
insertionIndex = (head + index) & mask;
for (int i = head; i != insertionIndex; i = (i + 1) & mask) {
data[i] = data[(i + collection.size()) & mask];
}
}
Iterator<? extends E> iterator = collection.iterator();
while (iterator.hasNext()) {
data[insertionIndex] = iterator.next();
modCount++;
insertionIndex = (insertionIndex + 1) & mask;
}
}
return true;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#addFirst(java.lang.Object)
*/
public void addFirst(E element) {
head = (head - 1) & (data.length - 1);
data[head] = element;
modCount++;
if (head == tail) {
doubleCapacity();
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#addLast(java.lang.Object)
*/
public void addLast(E element) {
data[tail] = element;
modCount++;
tail = (tail + 1) & (data.length - 1);
if (head == tail) {
doubleCapacity();
}
}
/**
* {@inheritDoc}
*
* @see java.util.List#clear()
*/
@Override
public void clear() {
modCount++;
final int mask = data.length - 1;
for (int i = head; i != tail; i = (i + 1) & mask) {
data[i] = null;
}
head = 0;
tail = 0;
}
/**
* {@inheritDoc}
*
* @see java.util.List#contains(java.lang.Object)
*/
@Override
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* {@inheritDoc}
*
* @see java.util.List#containsAll(java.util.Collection)
*/
@Override
public boolean containsAll(Collection<?> collection) {
for (Object element : collection) {
if (!contains(element)) {
return false;
}
}
return true;
}
/**
* {@inheritDoc}
*
* @see java.util.Queue#element()
*/
public E element() {
return getFirst();
}
/**
* {@inheritDoc}
*
* @see java.util.List#get(int)
*/
@Override
public E get(int index) {
rangeCheck(index);
return data[(head + index) & (data.length - 1)];
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#getFirst()
*/
public E getFirst() {
if (head == tail) {
throw new NoSuchElementException();
}
return data[head];
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#getLast()
*/
public E getLast() {
if (head == tail) {
throw new NoSuchElementException();
}
return data[(tail - 1) & data.length - 1];
}
/**
* {@inheritDoc}
*
* @see java.util.List#indexOf(java.lang.Object)
*/
@Override
public int indexOf(Object o) {
int result = -1;
final int mask = data.length - 1;
if (o == null) {
for (int i = head; i != tail; i = (i + 1) & mask) {
if (data[i] == null) {
result = (i - head) & mask;
break;
}
}
} else {
for (int i = head; i != tail; i = (i + 1) & mask) {
if (o.equals(data[i])) {
result = (i - head) & mask;
break;
}
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see java.util.List#isEmpty()
*/
@Override
public boolean isEmpty() {
return head == tail;
}
/**
* {@inheritDoc}
*
* @see java.util.List#iterator()
*/
@Override
public Iterator<E> iterator() {
return new DequeIterator();
}
/**
* {@inheritDoc}
*
* @see java.util.List#lastIndexOf(java.lang.Object)
*/
@Override
public int lastIndexOf(Object o) {
int result = -1;
final int mask = data.length - 1;
final int start = (tail - 1) & mask;
final int end = (head - 1) & mask;
if (o == null) {
for (int i = start; i != end; i = (i - 1) & mask) {
if (data[i] == null) {
result = (i - head) & mask;
break;
}
}
} else {
for (int i = start; i != end; i = (i - 1) & mask) {
if (o.equals(data[i])) {
result = (i - head) & mask;
break;
}
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see java.util.List#listIterator()
*/
@Override
public ListIterator<E> listIterator() {
return new DequeListIterator(0);
}
/**
* {@inheritDoc}
*
* @see java.util.List#listIterator(int)
*/
@Override
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size()) {
throw new IndexOutOfBoundsException(String.valueOf(index));
}
return new DequeListIterator(index);
}
/**
* {@inheritDoc}
*
* @see java.util.Queue#offer(java.lang.Object)
*/
public boolean offer(E element) {
addLast(element);
return true;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#offerAll(java.util.Collection)
*/
public void offerAll(Collection<? extends E> collection) {
addAll(collection);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#offerFirst(java.lang.Object)
*/
public void offerFirst(E element) {
addFirst(element);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#offerLast(java.lang.Object)
*/
public void offerLast(E element) {
addLast(element);
}
/**
* {@inheritDoc}
*
* @see java.util.Queue#peek()
*/
public E peek() {
if (head == tail) {
return null;
}
return getFirst();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#peekFirst()
*/
public E peekFirst() {
return getFirst();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#peekLast()
*/
public E peekLast() {
return getLast();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#poll()
*/
public E poll() {
if (head == tail) {
return null;
}
return removeFirst();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#pop()
*/
public E pop() {
return removeLast();
}
/**
* {@inheritDoc}
*
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
@SuppressWarnings("unchecked")
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
int size = in.readInt();
initialize(size);
head = 0;
tail = size;
for (int i = 0; i < size; i++) {
data[i] = (E)in.readObject();
}
}
/**
* {@inheritDoc}
*
* @see java.util.Queue#remove()
*/
public E remove() {
return removeFirst();
}
/**
* {@inheritDoc}
*
* @see java.util.List#remove(int)
*/
@Override
public E remove(int index) {
rangeCheck(index);
final int actualIndex = (head + index) & (data.length - 1);
E old = data[actualIndex];
deleteIndex(actualIndex);
return old;
}
/**
* {@inheritDoc}
*
* @see java.util.List#remove(java.lang.Object)
*/
@Override
public boolean remove(Object element) {
final int mask = data.length - 1;
boolean result = false;
if (element == null) {
for (int i = head; i != tail; i = (i + 1) & mask) {
if (data[i] == null) {
deleteIndex(i);
result = true;
break;
}
}
} else {
for (int i = head; i != tail; i = (i + 1) & mask) {
if (element.equals(data[i])) {
deleteIndex(i);
result = true;
break;
}
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see java.util.List#removeAll(java.util.Collection)
*/
@Override
public boolean removeAll(Collection<?> collection) {
final int mask = data.length - 1;
boolean result = false;
int[] indices = new int[collection.size()];
int cursor = 0;
for (Object element : collection) {
if (element == null) {
for (int i = head; i != tail; i = (i + 1) & mask) {
if (data[i] == null) {
indices[cursor++] = i;
break;
}
}
} else {
for (int i = head; i != tail; i = (i + 1) & mask) {
if (element.equals(data[i])) {
indices[cursor++] = i;
break;
}
}
}
}
if (cursor > 0) {
result = true;
if (indices.length == 1) {
deleteIndex(indices[0]);
} else {
delete(indices);
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#removeFirst()
*/
public E removeFirst() {
if (head == tail) {
throw new NoSuchElementException();
}
final int mask = data.length - 1;
final E result = data[head];
data[head] = null;
modCount++;
head = (head + 1) & mask;
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.common.utils.Deque#removeLast()
*/
public E removeLast() {
if (head == tail) {
throw new NoSuchElementException();
}
tail = (tail - 1) & (data.length - 1);
final E result = data[tail];
data[tail] = null;
modCount++;
return result;
}
/**
* {@inheritDoc}
*
* @see java.util.List#set(int, java.lang.Object)
*/
@Override
public E set(int index, E element) {
rangeCheck(index);
final int actualIndex = (head + index) & (data.length - 1);
E old = data[actualIndex];
data[actualIndex] = element;
return old;
}
/**
* {@inheritDoc}
*
* @see java.util.List#size()
*/
@Override
public int size() {
return (tail - head) & (data.length - 1);
}
/**
* {@inheritDoc}
*
* @see java.util.List#toArray()
*/
@Override
public Object[] toArray() {
final int size = size();
Object[] result = new Object[size];
if (head < tail) {
System.arraycopy(data, head, result, 0, size);
} else if (head != tail) {
int headLength = data.length - head;
// Copy all elements at the right of "head"...
System.arraycopy(data, head, result, 0, headLength);
// ...Then all elements at the left of "head"
System.arraycopy(data, 0, result, headLength, tail);
}
return result;
}
/**
* {@inheritDoc}
*
* @see java.util.List#toArray(T[])
*/
@Override
@SuppressWarnings("unchecked")
public <T extends Object> T[] toArray(T[] a) {
final int size = size();
final T[] temp;
if (a.length > size) {
temp = a;
} else {
temp = (T[])Array.newInstance(a.getClass().getComponentType(), size);
}
/*
* "head" could be located somewhere _after_ tail as we could have pushed elements on the back of our
* deque. Reorder them now.
*/
if (head < tail) {
System.arraycopy(data, head, temp, 0, size);
} else if (head != tail) {
int headLength = data.length - head;
// Copy all elements at the right of "head"...
System.arraycopy(data, head, temp, 0, headLength);
// ...Then all elements at the left of "head"
System.arraycopy(data, 0, temp, headLength, tail);
}
return temp;
}
/**
* {@inheritDoc}
*
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
public void writeExternal(ObjectOutput out) throws IOException {
final int mask = data.length - 1;
out.writeInt(size());
for (int i = head; i != tail; i = (i + 1) & mask) {
out.writeObject(data[i]);
}
}
/**
* Removes all of the values located at the given indices at once.
*
* @param indices
* Indices (in the actual array) of the elements that are to be deleted from this Queue.
*/
private void delete(int[] indices) {
final int mask = data.length - 1;
int cursor = 0;
while (cursor < indices.length
&& ((tail - indices[cursor]) & mask) > ((indices[cursor] - head) & mask)) {
cursor++;
}
final int median = cursor;
// All indices closer to tail than they are to head will trigger moves to the left
if (median > 0) {
deleteRightRotate(median - 1, indices);
}
if (median < indices.length) {
deleteLeftRotate(median, indices);
}
}
/**
* This will be used internally to delete the value of the given bucket.
*
* @param index
* The index (in the actual array) of the value we are to delete.
*/
private void deleteIndex(int index) {
final int mask = data.length - 1;
if (index == head) {
removeFirst();
} else if (index == ((tail - 1) & mask)) {
removeLast();
} else {
/*
* If we are close to head, we'll move elements to the right. If we are closer to tail, we'll move
* them to the left. Note that System.arrayCopy is way faster than iteration over the array.
*/
final int elementsAhead = (tail - index) & mask;
final int elementsBehind = (index - head) & mask;
// Are we closer to head?
if (elementsAhead > elementsBehind) {
if (head < index) {
modCount++;
System.arraycopy(data, head, data, head + 1, elementsBehind);
data[head] = null;
head = head + 1;
} else {
modCount++;
// Rotate all elements at the left of index by one to the right ...
System.arraycopy(data, 0, data, 1, index);
// ... copy the last element ...
data[0] = data[mask];
// ... and finally all elements from head to the last (-1) by one to the right
System.arraycopy(data, head, data, head + 1, mask - head);
data[head] = null;
head = (head + 1) & mask;
}
} else {
if (index < tail) {
modCount++;
System.arraycopy(data, index + 1, data, index, elementsAhead);
tail = tail - 1;
} else {
modCount++;
// Rotate all elements at the right of index by one to the left ...
System.arraycopy(data, index + 1, data, index, mask - index);
// ... copy the first element ...
data[mask] = data[0];
// ... and finally all elements at the left of tail by one to the right
System.arraycopy(data, 1, data, 0, tail);
tail = (tail - 1) & mask;
}
}
}
}
/**
* This will be used from {@link #delete(int[])} in order to remove the indices from the given array
* (starting from <code>startIndex</code>) while rotating {@link #data} elements to the left.
*
* @param startIndex
* Index from which to start removing values.
* @param indices
* Indices (in the actual array) of the elements that are to be deleted from this Queue.
*/
private void deleteLeftRotate(int startIndex, int[] indices) {
final int mask = data.length - 1;
// Take care of the indices corresponding to a removeLast
int cursor = indices.length - 1;
while (cursor >= startIndex && indices[cursor] == ((tail - 1) & mask)) {
removeLast();
cursor--;
}
// How many indices still to delete?
if (cursor == startIndex) {
// Only 1
modCount++;
tail = (tail - 1) & mask;
for (int i = indices[startIndex]; i != tail; i = (i + 1) & mask) {
data[i] = data[(i + 1) & mask];
}
data[tail] = null;
} else if (cursor > startIndex) {
tail = (tail - 1) & mask;
int gap = 1;
int fence = cursor;
cursor = startIndex + 1;
for (int i = indices[startIndex]; i != tail; i = (i + 1) & mask) {
while (cursor <= fence && ((i + gap) & mask) == indices[cursor]) {
cursor++;
gap++;
}
data[i] = data[(i + gap) & mask];
}
int oldTail = tail;
tail = (tail - (gap - 1)) & mask;
for (int i = oldTail; i != tail; i = (i - 1) & mask) {
data[i] = null;
modCount++;
}
}
}
/**
* This will be used from {@link #delete(int[])} in order to remove the indices from the given array
* (starting from <code>startIndex</code>) while rotating {@link #data} elements to the right.
*
* @param startIndex
* Index from which to start removing values.
* @param indices
* Indices (in the actual array) of the elements that are to be deleted from this Queue.
*/
private void deleteRightRotate(int startIndex, int[] indices) {
final int mask = data.length - 1;
// Take care of the indices corresponding to a removeLast
int cursor = 0;
while (cursor <= startIndex && indices[cursor] == head) {
removeFirst();
cursor++;
}
// How many indices still to delete?
if (cursor == startIndex) {
// Only 1
modCount++;
for (int i = indices[startIndex]; i != head; i = (i - 1) & mask) {
data[i] = data[(i - 1) & mask];
}
data[head] = null;
head = (head - 1) & mask;
} else if (cursor < startIndex) {
int gap = 1;
int fence = cursor;
cursor = startIndex - 1;
for (int i = indices[startIndex]; i != head; i = (i - 1) & mask) {
while (cursor >= fence && ((i - gap) & mask) == indices[cursor]) {
cursor--;
gap++;
}
data[i] = data[(i - gap) & mask];
}
int oldHead = head;
head = (head + gap) & mask;
for (int i = oldHead; i != head; i = (i + 1) & mask) {
data[i] = null;
modCount++;
}
}
}
/**
* Double the capacity of this deque.
*/
private void doubleCapacity() {
final int newCapacity = data.length << 1;
if (newCapacity < 0) {
throw new IndexOutOfBoundsException();
}
setCapacity(newCapacity);
}
/**
* This will ensure that the underlying array is big enough to hold the given number of elements.
*
* @param elementCount
* The number of elements we expect to hold in this deque.
*/
private void ensureCapacity(int elementCount) {
if (elementCount >= data.length) {
final int newCapacity = getNextPowerOfTwo(elementCount);
if (newCapacity < 0) {
throw new IndexOutOfBoundsException();
}
setCapacity(newCapacity);
}
}
/**
* Initializes the backing array to a capacity sufficient to hold the given number of elements.
*
* @param elementCount
* The number of elements we expect to hold in this deque.
*/
@SuppressWarnings("unchecked")
private void initialize(int elementCount) {
final int initialCapacity;
if (elementCount <= 1) {
initialCapacity = 4;
} else {
initialCapacity = getNextPowerOfTwo(elementCount);
}
if (initialCapacity < 0) {
throw new IndexOutOfBoundsException();
}
data = (E[])new Object[initialCapacity];
}
/**
* Checks that the given index is in the range of this deque. This will be used before any random access
* use.
*
* @param index
* The index we are to check.
* @throws IndexOutOfBoundsException
* Thrown if the given index is either negative or out of this deque's bounds.
*/
private void rangeCheck(int index) throws IndexOutOfBoundsException {
if (head == tail || index < 0 || index >= size()) {
throw new IndexOutOfBoundsException(String.valueOf(index));
}
}
/**
* Resizes the underlying array to the given new capacity.
*
* @param newCapacity
* New capacity to resize our underlying array to.
*/
@SuppressWarnings("unchecked")
private void setCapacity(int newCapacity) {
final int newTail;
if (head == tail) {
newTail = data.length;
} else {
newTail = size();
}
E[] temp = (E[])new Object[newCapacity];
int headLength = data.length - head;
// Copy all elements at the right of "head"...
System.arraycopy(data, head, temp, 0, headLength);
// ...Then all elements at the left of "head"
System.arraycopy(data, 0, temp, headLength, head);
data = temp;
head = 0;
tail = newTail;
}
/**
* Implementation of an {@link Iterator} for the {@link CircularArrayDeque}. It will allow iteration from
* the first element of the deque ({@link #head}) to the last ({@link #tail}).
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
private class DequeIterator implements Iterator<E> {
/** Keeps track of the value of {@link #modCount} this iterator expects. */
protected int expectedModCount = modCount;
/** Index of the last element returned by a call to {@link #next()}. */
protected int lastReturned = -1;
/** Index of the next element to be returned by this iterator. */
protected int next = head;
/** Improves visibility of the default constructor. */
DequeIterator() {
// Improves visibility of the default constructor.
}
/**
* {@inheritDoc}
*
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return next != tail;
}
/**
* {@inheritDoc}
*
* @see java.util.Iterator#next()
*/
public E next() {
checkComodification();
if (next == tail) {
throw new NoSuchElementException();
}
final E result = data[next];
lastReturned = next;
next = (next + 1) & (data.length - 1);
return result;
}
/**
* {@inheritDoc}
*
* @see java.util.Iterator#remove()
*/
public void remove() {
if (lastReturned == -1) {
throw new IllegalStateException();
}
checkComodification();
final boolean increment = next == head;
int deletionIndex = (lastReturned - head) & (data.length - 1);
CircularArrayDeque.this.remove(deletionIndex);
expectedModCount++;
final int mask = data.length - 1;
if (increment) {
next = (next + 1) & mask;
} else if (deletionIndex >= size() / 2 && size() > 1) {
next = (next - 1) & mask;
}
lastReturned = -1;
}
/**
* Checks for modification of the deque since the creation of this iterator.
*
* @throws ConcurrentModificationException
* Thrown if the deque this iterator traverses has been modified since the creation of the
* iterator.
*/
protected void checkComodification() throws ConcurrentModificationException {
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
}
}
/**
* Implementation of an {@link ListIterator} for the {@link CircularArrayDeque}. It will allow iteration
* from the first element of the deque ({@link #head}) to the last ({@link #tail}) and back.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
private class DequeListIterator extends DequeIterator implements ListIterator<E> {
/**
* Instantiates our list iterator given the index of the first element to be returned by a call to
* {@link #next()}.
*
* @param index
* Index of the first element to be returned from the list iterator (by a call to
* {@link #next()}).
*/
DequeListIterator(int index) {
next = (head + index) & (data.length - 1);
}
/**
* {@inheritDoc}
*
* @see java.util.ListIterator#add(java.lang.Object)
*/
public void add(E element) {
checkComodification();
final boolean increment = next != head;
CircularArrayDeque.this.add((next - head) & (data.length - 1), element);
expectedModCount++;
if (increment) {
next = (next + 1) & (data.length - 1);
}
lastReturned = -1;
}
/**
* {@inheritDoc}
*
* @see java.util.ListIterator#hasPrevious()
*/
public boolean hasPrevious() {
return next != head;
}
/**
* {@inheritDoc}
*
* @see java.util.ListIterator#nextIndex()
*/
public int nextIndex() {
return (next - head) & (data.length - 1);
}
/**
* {@inheritDoc}
*
* @see java.util.ListIterator#previous()
*/
public E previous() {
checkComodification();
final int mask = data.length - 1;
if (next == head) {
throw new NoSuchElementException();
}
next = (next - 1) & mask;
final E result = data[next];
lastReturned = next;
return result;
}
/**
* {@inheritDoc}
*
* @see java.util.ListIterator#previousIndex()
*/
public int previousIndex() {
return ((next - head) & (data.length - 1)) - 1;
}
/**
* {@inheritDoc}
*
* @see java.util.ListIterator#set(java.lang.Object)
*/
public void set(E element) {
if (lastReturned == -1) {
throw new IllegalStateException();
}
checkComodification();
CircularArrayDeque.this.set((lastReturned - head) & (data.length - 1), element);
}
}
}