diff --git a/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java b/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java
new file mode 100644
index 0000000000..a85fd30759
--- /dev/null
+++ b/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java
@@ -0,0 +1,525 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+import java.io.Serializable;
+
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.Iterator;
+
+import java.util.Deque;
+import java.util.concurrent.locks.Condition;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Decorates another {@link Deque} to support blocking operations that wait for the deque
+ * to become non-empty when retrieving an element, and wait for space to become available
+ * in the deque when storing an element if the decorated deque has a capacity limit.
+ *
+ * This deque exists to support blocking operations for the decorated deque. It is thread
+ * safe.
+ *
+ *
+ * @param the type of elements held in this deque
+ * @since 4.5
+ */
+public class BlockingDeque extends AbstractCollection implements Deque, Serializable {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 3961982897426944557L;
+
+ final Deque deque;
+ final ReentrantLock lock = new ReentrantLock();
+
+ /** Condition for waiting takes */
+ private final Condition notEmpty = lock.newCondition();
+
+ /** Condition for waiting puts */
+ private final Condition notFull = lock.newCondition();
+
+ /**
+ * Factory method to create a blocking deque.
+ *
+ * @param
+ * the type of the elements in the deque
+ * @param deque
+ * the deque to decorate, must not be null
+ * @return a new blocking Deque
+ * @throws NullPointerException
+ * if deque is null
+ */
+ public static BlockingDeque blockingDeque(final Deque deque) {
+ return new BlockingDeque<>(deque);
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param deque
+ * the deque to decorate, must not be null
+ * @throws NullPointerException
+ * if deque is null
+ */
+ public BlockingDeque(final Deque deque) {
+ if (deque == null) {
+ throw new NullPointerException();
+ }
+ this.deque = deque;
+ }
+
+ /**
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ */
+ @Override
+ public void addFirst(final E e) {
+ if (!offerFirst(e))
+ throw new IllegalStateException("Deque is full");
+ }
+
+ /**
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ */
+ @Override
+ public void addLast(final E e) {
+ if (!offerLast(e))
+ throw new IllegalStateException("Deque is full");
+ }
+
+ /**
+ * @return {@code true} if the element was added to this deque, else
+ * {@code false}
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ @Override
+ public boolean offerFirst(final E e) {
+ lock.lock();
+ try {
+ boolean r;
+ if (r = deque.offerFirst(e))
+ notEmpty.signal();
+ return r;
+ } catch (Exception ex) {
+ throw ex;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * @return {@code true} if the element was added to this deque, else
+ * {@code false}
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ @Override
+ public boolean offerLast(final E e) {
+ lock.lock();
+ try {
+ boolean r;
+ if (r = deque.offerLast(e))
+ notEmpty.signal();
+ return r;
+ } catch (Exception ex) {
+ throw ex;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Inserts the specified element at the front of this deque. It will be
+ * blocked and wait for space to become available if the deque is full.
+ *
+ * @param e the element to add
+ * @throws InterruptedException if interrupted while waiting
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ public void putFirst(final E e) throws InterruptedException {
+ lock.lock();
+ try {
+ while (!deque.offerFirst(e))
+ notFull.await();
+ notEmpty.signal();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Inserts the specified element at the end of this deque. It will be
+ * blocked and wait for space to become available if the deque is full.
+ *
+ * @param e the element to add
+ * @throws InterruptedException if interrupted while waiting
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ public void putLast(final E e) throws InterruptedException {
+ lock.lock();
+ try {
+ while (!deque.offerLast(e))
+ notFull.await();
+ notEmpty.signal();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public E removeFirst() {
+ lock.lock();
+ try {
+ E e;
+ if ((e = deque.removeFirst()) != null)
+ notFull.signal();
+ return e;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public E removeLast() {
+ lock.lock();
+ try {
+ E e;
+ if ((e = deque.removeLast()) != null)
+ notFull.signal();
+ return e;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Retrieves and removes the first element of this deque, waiting
+ * if necessary until an element becomes available.
+ *
+ * @return the head of this deque
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public E takeFirst() throws InterruptedException {
+ lock.lock();
+ try {
+ while (deque.size() == 0)
+ notEmpty.await();
+ E x = deque.removeFirst();
+ notFull.signal();
+ return x;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Retrieves and removes the last element of this deque, waiting
+ * if necessary until an element becomes available.
+ *
+ * @return the tail of this deque
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public E takeLast() throws InterruptedException {
+ lock.lock();
+ try {
+ while (deque.size() == 0)
+ notEmpty.await();
+ E x = deque.removeLast();
+ notFull.signal();
+ return x;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public E pollFirst() {
+ lock.lock();
+ try {
+ E e;
+ if ((e = deque.pollFirst()) != null)
+ notFull.signal();
+ return e;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public E pollLast() {
+ lock.lock();
+ try {
+ E e;
+ if ((e = deque.pollLast()) != null)
+ notFull.signal();
+ return e;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public E getFirst() {
+ lock.lock();
+ try {
+ return deque.getFirst();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public E getLast() {
+ lock.lock();
+ try {
+ return deque.getLast();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public E peekFirst() {
+ lock.lock();
+ try {
+ return deque.peekFirst();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public E peekLast() {
+ lock.lock();
+ try {
+ return deque.peekLast();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public boolean removeFirstOccurrence(final Object o) {
+ lock.lock();
+ try {
+ boolean r;
+ if (r = deque.removeFirstOccurrence(o))
+ notEmpty.signal();
+ return r;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public boolean removeLastOccurrence(final Object o) {
+ lock.lock();
+ try {
+ boolean r;
+ if (r = deque.removeLastOccurrence(o))
+ notEmpty.signal();
+ return r;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public boolean add(final E e) {
+ addLast(e);
+ return true;
+ }
+
+ @Override
+ public boolean offer(final E e) {
+ return offerLast(e);
+ }
+
+ @Override
+ public E remove() {
+ return removeFirst();
+ }
+
+ @Override
+ public E poll() {
+ return pollFirst();
+ }
+
+ @Override
+ public E element() {
+ return getFirst();
+ }
+
+ @Override
+ public E peek() {
+ return peekFirst();
+ }
+
+ @Override
+ public void push(final E e) {
+ addFirst(e);
+ }
+
+ @Override
+ public E pop() {
+ return removeFirst();
+ }
+
+ @Override
+ public boolean remove(final Object o) {
+ return removeFirstOccurrence(o);
+ }
+
+ @Override
+ public boolean containsAll(final Collection> c) {
+ lock.lock();
+ try {
+ return deque.containsAll(c);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public boolean addAll(final Collection extends E> c) {
+ lock.lock();
+ try {
+ boolean r;
+ if (r = deque.addAll(c))
+ notEmpty.signalAll();
+ return r;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public boolean removeAll(final Collection> c) {
+ lock.lock();
+ try {
+ boolean r;
+ if (r = deque.removeAll(c))
+ notFull.signalAll();
+ return r;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public boolean retainAll(final Collection> c) {
+ lock.lock();
+ try {
+ boolean r;
+ r = deque.retainAll(c);
+ if (r == true) {
+ notFull.signalAll();
+ }
+ return r;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public void clear() {
+ lock.lock();
+ try {
+ deque.clear();
+ notFull.signalAll();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public boolean contains(final Object o) {
+ lock.lock();
+ try {
+ return deque.contains(o);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public int size() {
+ lock.lock();
+ try {
+ return deque.size();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ lock.lock();
+ try {
+ return deque.isEmpty();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public Iterator iterator() {
+ return deque.iterator();
+ }
+
+ @Override
+ public Object[] toArray() {
+ lock.lock();
+ try {
+ return deque.toArray();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public T[] toArray(final T[] a) {
+ lock.lock();
+ try {
+ return deque.toArray(a);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public Iterator descendingIterator() {
+ return deque.descendingIterator();
+ }
+}
diff --git a/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java b/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java
new file mode 100644
index 0000000000..702c0ae45c
--- /dev/null
+++ b/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java
@@ -0,0 +1,636 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+
+import org.apache.commons.collections4.BoundedCollection;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.Collection;
+
+import java.util.Deque;
+import java.util.NoSuchElementException;
+import java.util.List;
+
+import java.util.ConcurrentModificationException;
+import java.util.Objects;
+import java.util.Iterator;
+/**
+ * CircularDeque is a deque with a fixed size that replaces its oldest
+ * element if full.
+ *
+ * The removal order of a {@link CircularDeque} is based on the
+ * insertion order; elements are removed in the same order in which they
+ * were added. The iteration order is the same as the removal order.
+ *
+ *
+ * The {@link #add(Object)}, {@link #remove()}, {@link #peek()}, {@link #poll},
+ * {@link #offer(Object)} operations all perform in constant time.
+ * All other operations perform in linear time or worse.
+ *
+ *
+ * This deque prevents null objects from being added.
+ *
+ *
+ * @param the type of elements in this collection
+ * @since 4.5
+ */
+public class CircularDeque extends AbstractCollection
+ implements Deque, BoundedCollection, Serializable {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 6013343683263686185L;
+
+ /** Underlying storage array. */
+ private transient E[] elements;
+
+ /** Array index of first (oldest) deque element. */
+ private transient int head = 0;
+
+ /**
+ * Index mod maxElements of the array position following the last deque
+ * element. Deque elements start at elements[head] and "wrap around"
+ * elements[maxElements-1], ending at elements[decrement(tail)].
+ * For example, elements = {c,a,b}, start=1, end=1 corresponds to
+ * the deque [a,b,c].
+ */
+ private transient int tail = 0;
+
+ /** Flag to indicate if the deque is currently full. */
+ private transient boolean full = false;
+
+ /** Capacity of the deque. */
+ private final int maxElements;
+
+ /**
+ * Constructor that creates a deque with the default size of 32.
+ */
+ public CircularDeque() {
+ this(32);
+ }
+
+ /**
+ * Constructor that creates a deque with the specified size.
+ *
+ * @param size the size of the deque (cannot be changed)
+ * @throws IllegalArgumentException if the size is < 1
+ */
+ @SuppressWarnings("unchecked")
+ public CircularDeque(final int size) {
+ if (size <= 0) {
+ throw new IllegalArgumentException("The size must be greater than 0");
+ }
+ elements = (E[]) new Object[size];
+ maxElements = elements.length;
+ }
+
+ /**
+ * Constructor that creates a deque from the specified collection.
+ * The collection size also sets the deque size.
+ *
+ * @param coll the collection to copy into the deque, may not be null
+ * @throws NullPointerException if the collection is null
+ */
+ public CircularDeque(final Collection extends E> coll) {
+ if (coll == null) {
+ throw new NullPointerException("Collection must not be null");
+ }
+
+ elements = (E[]) new Object[coll.size()];
+ maxElements = elements.length;
+
+ addAll(coll);
+ }
+
+ /**
+ * Write the deque out using a custom routine.
+ *
+ * @param out the output stream
+ * @throws IOException if an I/O error occurs while writing to the output stream
+ */
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeInt(size());
+ for (final E e : this) {
+ out.writeObject(e);
+ }
+ }
+
+ /**
+ * Read the deque in using a custom routine.
+ *
+ * @param in the input stream
+ * @throws IOException if an I/O error occurs while writing to the output stream
+ * @throws ClassNotFoundException if the class of a serialized object can not be found
+ */
+ @SuppressWarnings("unchecked")
+ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ elements = (E[]) new Object[maxElements];
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ elements[i] = (E) in.readObject();
+ }
+ head = 0;
+ full = size == maxElements;
+ if (full) {
+ tail = 0;
+ } else {
+ tail = size;
+ }
+ }
+
+ /**
+ * Returns true if this deque is empty; false otherwise.
+ *
+ * @return true if this deque is empty
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
+ * Returns {@code true} if the capacity limit of this deque has been reached,
+ * i.e. the number of elements stored in the deque equals its maximum size.
+ *
+ * @return {@code true} if the capacity limit has been reached, {@code false} otherwise
+ * @since 4.5
+ */
+ public boolean isAtFullCapacity() {
+ return size() == maxElements;
+ }
+
+ /**
+ * Increase the internal index.
+ *
+ * @param index the index to increment
+ * @return the updated index
+ */
+ private int increaseIndex(int index) {
+ index++;
+ if (index >= maxElements) {
+ index = 0;
+ }
+ return index;
+ }
+
+ /**
+ * Decrease the internal index.
+ *
+ * @param index the index to decrement
+ * @return the updated index
+ */
+ private int decreaseIndex(int index) {
+ index--;
+ if (index < 0) {
+ index = maxElements - 1;
+ }
+ return index;
+ }
+
+ /**
+ * Adds the given element before the head of this deque. If the deque is full,
+ * the tail element will be overridden so that a new element can be inserted.
+ *
+ * @param e the element to add
+ */
+ @Override
+ public void addFirst(final E e) {
+ if (isAtFullCapacity()) {
+ remove();
+ }
+
+ if (head == 0) {
+ head = maxElements - 1;
+ } else {
+ head--;
+ }
+
+ elements[head] = e;
+
+ if (tail == head) {
+ full = true;
+ }
+ }
+
+ /**
+ * Adds the given element at the tail of this deque. If the deque is full,
+ * the head element will be overridden so that a new element can be inserted.
+ *
+ * @param e the element to add
+ */
+ @Override
+ public void addLast(final E e) {
+ if (isAtFullCapacity()) {
+ removeFirst();
+ }
+
+ elements[tail++] = e;
+
+ if (tail >= maxElements) {
+ tail = 0;
+ }
+
+ if (tail == head) {
+ full = true;
+ }
+ }
+
+ @Override
+ public boolean offerFirst(final E e) {
+ addFirst(e);
+ return true;
+ }
+
+ @Override
+ public boolean offerLast(final E e) {
+ addLast(e);
+ return true;
+ }
+
+ @Override
+ public E removeFirst() {
+ if (isEmpty()) {
+ throw new NoSuchElementException("queue is empty");
+ }
+
+ final E element = elements[head];
+
+ elements[head++] = null;
+
+ if (head >= maxElements) {
+ head = 0;
+ }
+ full = false;
+
+ return element;
+ }
+
+ @Override
+ public E removeLast() {
+ if (isEmpty()) {
+ throw new NoSuchElementException("queue is empty");
+ }
+
+ final E element = elements[tail];
+ elements[tail--] = null;
+
+ if (tail < 0) {
+ tail = maxElements - 1;
+ }
+ full = false;
+
+ return element;
+ }
+
+ @Override
+ public E pollFirst() {
+ if (isEmpty()) {
+ return null;
+ }
+ return removeFirst();
+ }
+
+ @Override
+ public E pollLast() {
+ if (isEmpty()) {
+ return null;
+ }
+ return removeLast();
+ }
+
+ @Override
+ public E getFirst() {
+ if (isEmpty()) {
+ throw new NoSuchElementException("deque is empty");
+ }
+ return elements[head];
+ }
+
+ @Override
+ public E getLast() {
+ if (isEmpty()) {
+ throw new NoSuchElementException("deque is empty");
+ }
+
+ return elements[decreaseIndex(tail)];
+ }
+
+ @Override
+ public E peekFirst() {
+ if (isEmpty()) {
+ return null;
+ }
+ return elements[head];
+ }
+
+ @Override
+ public E peekLast() {
+ if (isEmpty()) {
+ return null;
+ }
+
+ return elements[decreaseIndex(tail)];
+ }
+
+ @Override
+ public boolean removeFirstOccurrence(final Object o) {
+ int i = head;
+ while (true) {
+ if (Objects.equals(o,elements[i])) {
+ delete(i);
+ return true;
+ }
+ if (i == decreaseIndex(tail)) {
+ return false;
+ }
+ i = increaseIndex(i);
+ }
+ }
+
+ @Override
+ public boolean removeLastOccurrence(final Object o) {
+ int i = decreaseIndex(tail);
+ while (true) {
+ if (Objects.equals(o,elements[i])) {
+ delete(i);
+ return true;
+ }
+ if (i == head) {
+ return false;
+ }
+ i = decreaseIndex(i);
+ }
+ }
+
+ @Override
+ public boolean add(final E e) {
+ if (isAtFullCapacity()) {
+ remove();
+ }
+
+ elements[tail++] = e;
+
+ if (tail >= maxElements) {
+ tail = 0;
+ }
+
+ if (tail == head) {
+ full = true;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean offer(final E e) {
+ return offerLast(e);
+ }
+
+ @Override
+ public E remove() {
+ return removeFirst();
+ }
+
+ @Override
+ public E poll() {
+ return pollFirst();
+ }
+
+ @Override
+ public E element() {
+ return getFirst();
+ }
+
+ @Override
+ public E peek() {
+ return peekFirst();
+ }
+
+ @Override
+ public void push(final E e) {
+ addFirst(e);
+ }
+
+ @Override
+ public E pop() {
+ return removeFirst();
+ }
+
+
+ /**
+ * Removes the element at the specified position in the elements array,
+ * adjusting head and tail as necessary. This can result in motion of
+ * elements backwards or forwards in the array.
+ *
+ *
This method is called delete rather than remove to emphasize
+ * that its semantics differ from those of {@link List#remove(int)}.
+ *
+ * @return true if elements moved backwards
+ */
+ private boolean delete(final int i) {
+ if (i == head) {
+ elements[i] = null;
+ head = increaseIndex(head);
+ full = false;
+ return true;
+ }
+
+ if (i == decreaseIndex(tail)) {
+ elements[i] = null;
+ tail = decreaseIndex(tail);
+ full = false;
+ return true;
+ }
+
+ final int front = i - head;
+ final int back = tail - i - 1;
+
+ if (head < tail) {
+ if (front < back) {
+ System.arraycopy(elements, head, elements, head + 1, front);
+ head++;
+ } else {
+ System.arraycopy(elements, i + 1, elements, i, back);
+ tail--;
+ }
+ } else if (head > tail) {
+ if (i > head) {
+ System.arraycopy(elements, head, elements, head + 1, front);
+ head++;
+ } else if (i < tail) {
+ System.arraycopy(elements, i + 1, elements, i, back);
+ tail--;
+ } else {
+ throw new ConcurrentModificationException();
+ }
+ }
+ full = false;
+ return true;
+ }
+
+ /**
+ * Returns the number of elements stored in the deque.
+ *
+ * @return this deque's size
+ */
+ @Override
+ public int size() {
+ int size = 0;
+
+ if (tail < head) {
+ size = maxElements - head + tail;
+ } else if (tail == head) {
+ size = full ? maxElements : 0;
+ } else {
+ size = tail - head;
+ }
+
+ return size;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * A {@code CircularDeque} can never be full, thus this returns always
+ * {@code false}.
+ *
+ * @return always returns {@code false}
+ */
+ @Override
+ public boolean isFull() {
+ return false;
+ }
+
+ @Override
+ public int maxSize() {
+ return maxElements;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new Iterator() {
+
+ private int index = head;
+ private int lastReturnedIndex = -1;
+ private boolean isFirst = full;
+
+ @Override
+ public boolean hasNext() {
+ return isFirst || index != tail;
+ }
+
+ @Override
+ public E next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ isFirst = false;
+ lastReturnedIndex = index;
+ index = increaseIndex(index);
+ return elements[lastReturnedIndex];
+ }
+
+ @Override
+ public void remove() {
+ if (lastReturnedIndex == -1) {
+ throw new IllegalStateException();
+ }
+
+ // First element can be removed quickly
+ if (lastReturnedIndex == head) {
+ CircularDeque.this.remove();
+ lastReturnedIndex = -1;
+ return;
+ }
+
+ int pos = lastReturnedIndex + 1;
+ if (head < lastReturnedIndex && pos < tail) {
+ // shift in one part
+ System.arraycopy(elements, pos, elements, lastReturnedIndex, tail - pos);
+ } else {
+ // Other elements require us to shift the subsequent elements
+ while (pos != tail) {
+ if (pos >= maxElements) {
+ elements[pos - 1] = elements[0];
+ pos = 0;
+ } else {
+ elements[decreaseIndex(pos)] = elements[pos];
+ pos = increaseIndex(pos);
+ }
+ }
+ }
+
+ lastReturnedIndex = -1;
+ tail = decreaseIndex(tail);
+ elements[tail] = null;
+ full = false;
+ index = decreaseIndex(index);
+ }
+
+ };
+ }
+
+
+ @Override
+ public Iterator descendingIterator() {
+ return new DescendingIterator();
+ }
+
+ private class DescendingIterator implements Iterator {
+ /*
+ * This class is nearly a mirror-image of DeqIterator, using
+ * tail instead of head for initial cursor, and head instead of
+ * tail for fence.
+ */
+ private int cursor = tail;
+ private int fence = head;
+ private int lastRet = -1;
+ private boolean isFull = full;
+
+ public boolean hasNext() {
+ return isFull || cursor != fence;
+ }
+
+ public E next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ cursor = decreaseIndex(cursor);
+ @SuppressWarnings("unchecked")
+ E result = (E) elements[cursor];
+ if (head != fence )
+ throw new ConcurrentModificationException();
+ lastRet = cursor;
+ return result;
+ }
+
+ public void remove() {
+ if (lastRet < 0)
+ throw new IllegalStateException();
+ if (!delete(lastRet)) {
+ cursor = increaseIndex(cursor);
+ fence = head;
+ }
+ lastRet = -1;
+ isFull = false;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java b/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java
new file mode 100644
index 0000000000..a02c5005e7
--- /dev/null
+++ b/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java
@@ -0,0 +1,217 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.collections4.collection.PredicatedCollection;
+
+import java.util.Deque;
+import java.util.Iterator;
+
+/**
+ * Decorates another {@link Deque} to validate that additions
+ * match a specified predicate.
+ *
+ * This deque exists to provide validation for the decorated deque.
+ * It is normally created to decorate an empty deque.
+ * If an object cannot be added to the deque, an IllegalArgumentException is thrown.
+ *
+ *
+ * One usage would be to ensure that no null entries are added to the deque.
+ *
+ *
+ * @param the type of elements held in this deque
+ * @since 4.5
+ */
+public class PredicatedDeque extends PredicatedCollection implements Deque {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 7027705675493389763L;
+
+ /**
+ * Factory method to create a predicated (validating) deque.
+ *
+ * If there are any elements already in the deque being decorated, they
+ * are validated.
+ *
+ * @param the type of the elements in the deque
+ * @param deque the deque to decorate, must not be null
+ * @param predicate the predicate to use for validation, must not be null
+ * @return a new predicated deque
+ * @throws NullPointerException if deque or predicate is null
+ * @throws IllegalArgumentException if the deque contains invalid elements
+ */
+ public static PredicatedDeque predicatedDeque(final Deque deque,
+ final Predicate super E> predicate) {
+ return new PredicatedDeque<>(deque, predicate);
+ }
+
+
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * If there are any elements already in the collection being decorated, they
+ * are validated.
+ *
+ * @param deque the collection to decorate, must not be null
+ * @param predicate the predicate to use for validation, must not be null
+ * @throws NullPointerException if collection or predicate is null
+ * @throws IllegalArgumentException if the collection contains invalid elements
+ */
+ protected PredicatedDeque(Deque deque, Predicate super E> predicate) {
+ super(deque, predicate);
+ }
+
+ /**
+ * Gets the deque being decorated.
+ *
+ * @return the decorated deque
+ */
+ @Override
+ protected Deque decorated() {
+ return (Deque) super.decorated();
+ }
+
+ /**
+ * Override to validate the object being added to ensure it matches
+ * the predicate.
+ *
+ * @param e the object being added
+ * @throws IllegalArgumentException if the add is invalid
+ */
+
+ @Override
+ public void addFirst(final E e) {
+ validate(e);
+ decorated().addFirst(e);
+ }
+
+ @Override
+ public void addLast(final E e) {
+ validate(e);
+ decorated().addLast(e);
+ }
+
+ @Override
+ public boolean offerFirst(final E e) {
+ validate(e);
+ return decorated().offerFirst(e);
+ }
+
+ @Override
+ public boolean offerLast(final E e) {
+ validate(e);
+ return decorated().offerLast(e);
+ }
+
+ @Override
+ public E removeFirst() {
+ return decorated().removeFirst();
+ }
+
+ @Override
+ public E removeLast() {
+ return decorated().removeLast();
+ }
+
+ @Override
+ public E pollFirst() {
+ return decorated().pollFirst();
+ }
+
+ @Override
+ public E pollLast() {
+ return decorated().pollLast();
+ }
+
+ @Override
+ public E getFirst() {
+ return decorated().getFirst();
+ }
+
+ @Override
+ public E getLast() {
+ return decorated().getLast();
+ }
+
+ @Override
+ public E peekFirst() {
+ return decorated().peekFirst();
+ }
+
+ @Override
+ public E peekLast() {
+ return decorated().peekLast();
+ }
+
+ @Override
+ public boolean removeFirstOccurrence(final Object o) {
+ validate((E) o);
+ return decorated().removeFirstOccurrence(o);
+ }
+
+ @Override
+ public boolean removeLastOccurrence(final Object o) {
+ validate((E) o);
+ return decorated().removeLastOccurrence(o);
+ }
+
+ @Override
+ public boolean offer(final E e) {
+ validate(e);
+ return decorated().offer(e);
+ }
+
+ @Override
+ public E remove() {
+ return decorated().remove();
+ }
+
+ @Override
+ public E poll() {
+ return decorated().poll();
+ }
+
+ @Override
+ public E element() {
+ return decorated().element();
+ }
+
+ @Override
+ public E peek() {
+ return decorated().peek();
+ }
+
+ @Override
+ public void push(final E e) {
+ validate(e);
+ decorated().push(e);
+ }
+
+ @Override
+ public E pop() {
+ return decorated().pop();
+ }
+
+ @Override
+ public Iterator descendingIterator() {
+ return decorated().descendingIterator();
+ }
+}
diff --git a/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java b/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java
new file mode 100644
index 0000000000..e750f4cd82
--- /dev/null
+++ b/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java
@@ -0,0 +1,261 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+import org.apache.commons.collections4.collection.SynchronizedCollection;
+import java.util.Deque;
+import java.util.Iterator;
+
+
+/**
+ * Decorates another {@link Deque} to synchronize its behaviour for a multi-threaded environment.
+ *
+ * Methods are synchronized, then forwarded to the decorated deque. Iterators must be separately synchronized around the
+ * loop.
+ *
+ *
+ * @param the type of the elements in the collection
+ * @since 4.5
+ */
+public class SynchronizedDeque extends SynchronizedCollection implements Deque {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 4565577797338001569L;
+
+ /**
+ * Factory method to create a synchronized deque.
+ *
+ * @param
+ * the type of the elements in the deque
+ * @param deque
+ * the deque to decorate, must not be null
+ * @return a new synchronized Deque
+ * @throws NullPointerException
+ * if deque is null
+ */
+ public static SynchronizedDeque synchronizedDeque(final Deque deque) {
+ return new SynchronizedDeque<>(deque);
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param deque
+ * the deque to decorate, must not be null
+ * @throws NullPointerException
+ * if deque is null
+ */
+ protected SynchronizedDeque(final Deque deque) {
+ super(deque);
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param deque
+ * the deque to decorate, must not be null
+ * @param lock
+ * the lock to use, must not be null
+ * @throws NullPointerException
+ * if deque or lock is null
+ */
+ protected SynchronizedDeque(final Deque deque, final Object lock) {
+ super(deque, lock);
+ }
+
+ /**
+ * Gets the deque being decorated.
+ *
+ * @return the decorated deque
+ */
+ @Override
+ protected Deque decorated() {
+ return (Deque) super.decorated();
+ }
+
+ @Override
+ public E element() {
+ synchronized (lock) {
+ return decorated().element();
+ }
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+ synchronized (lock) {
+ return decorated().equals(object);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ synchronized (lock) {
+ return decorated().hashCode();
+ }
+ }
+
+ @Override
+ public void addFirst(E e) {
+ synchronized (lock) {
+ decorated().addFirst(e);
+ }
+ }
+
+ @Override
+ public void addLast(E e) {
+ synchronized (lock) {
+ decorated().addLast(e);
+ }
+ }
+
+ @Override
+ public boolean offerFirst(E e) {
+ synchronized (lock) {
+ return decorated().offerFirst(e);
+ }
+ }
+
+ @Override
+ public boolean offerLast(E e) {
+ synchronized (lock) {
+ return decorated().offerLast(e);
+ }
+ }
+
+ @Override
+ public E removeFirst() {
+ synchronized (lock) {
+ return decorated().removeFirst();
+ }
+ }
+
+ @Override
+ public E removeLast() {
+ synchronized (lock) {
+ return decorated().removeLast();
+ }
+ }
+
+ @Override
+ public E pollFirst() {
+ synchronized (lock) {
+ return decorated().pollFirst();
+ }
+ }
+
+ @Override
+ public E pollLast() {
+ synchronized (lock) {
+ return decorated().pollLast();
+ }
+ }
+
+ @Override
+ public E getFirst() {
+ synchronized (lock) {
+ return decorated().getFirst();
+ }
+ }
+
+ @Override
+ public E getLast() {
+ synchronized (lock) {
+ return decorated().getLast();
+ }
+ }
+
+ @Override
+ public E peekFirst() {
+ synchronized (lock) {
+ return decorated().peekFirst();
+ }
+ }
+
+ @Override
+ public E peekLast() {
+ synchronized (lock) {
+ return decorated().peekLast();
+ }
+ }
+
+ @Override
+ public boolean removeFirstOccurrence(Object o) {
+ synchronized (lock) {
+ return decorated().removeFirstOccurrence(o);
+ }
+ }
+
+ @Override
+ public boolean removeLastOccurrence(Object o) {
+ synchronized (lock) {
+ return decorated().removeLastOccurrence(o);
+ }
+ }
+
+ @Override
+ public boolean offer(final E e) {
+ synchronized (lock) {
+ return decorated().offer(e);
+ }
+ }
+
+ @Override
+ public E peek() {
+ synchronized (lock) {
+ return decorated().peek();
+ }
+ }
+
+ @Override
+ public void push(E e) {
+ synchronized (lock) {
+ decorated().push(e);
+ }
+ }
+
+ @Override
+ public E pop() {
+ synchronized (lock) {
+ return decorated().pop();
+ }
+ }
+
+ @Override
+ public Iterator descendingIterator() {
+ synchronized (lock) {
+ return decorated().descendingIterator();
+ }
+ }
+
+ @Override
+ public E poll() {
+ synchronized (lock) {
+ return decorated().poll();
+ }
+ }
+
+ @Override
+ public E remove() {
+ synchronized (lock) {
+ return decorated().remove();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java b/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java
new file mode 100644
index 0000000000..dd480ebe5b
--- /dev/null
+++ b/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java
@@ -0,0 +1,223 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.collections4.collection.TransformedCollection;
+
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+
+/**
+ * Decorates another {@link Deque} to transform objects that are added.
+ *
+ * The add/offer methods are affected by this class.
+ * Thus objects must be removed or searched for using their transformed form.
+ * For example, if the transformation converts Strings to Integers, you must
+ * use the Integer form to remove objects.
+ *
+ *
+ * @param the type of elements held in this deque
+ * @since 4.5
+ */
+
+public class TransformedDeque extends TransformedCollection implements Deque {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 7959728067506831816L;
+
+ /**
+ * Factory method to create a transforming deque.
+ *
+ * If there are any elements already in the deque being decorated, they
+ * are NOT transformed.
+ * Contrast this with {@link #transformedDeque(Deque, Transformer)}.
+ *
+ * @param the type of the elements in the deque
+ * @param deque the deque to decorate, must not be null
+ * @param transformer the transformer to use for conversion, must not be null
+ * @return a new transformed Deque
+ * @throws NullPointerException if deque or transformer is null
+ */
+ public static TransformedDeque transformingDeque(final Deque deque,
+ final Transformer super E, ? extends E> transformer) {
+ return new TransformedDeque<>(deque, transformer);
+ }
+
+ /**
+ * Factory method to create a transformed deque that will transform
+ * existing contents of the specified deque.
+ *
+ * If there are any elements already in the deque being decorated, they
+ * will be transformed by this method.
+ * Contrast this with {@link #transformingDeque(Deque, Transformer)}.
+ *
+ * @param the type of the elements in the deque
+ * @param deque the deque to decorate, must not be null
+ * @param transformer the transformer to use for conversion, must not be null
+ * @return a new transformed Deque
+ * @throws NullPointerException if deque or transformer is null
+ * @since 4.0
+ */
+ public static TransformedDeque transformedDeque(final Deque deque,
+ final Transformer super E, ? extends E> transformer) {
+ // throws NullPointerException if deque or transformer is null
+ final TransformedDeque decorated = new TransformedDeque<>(deque, transformer);
+ if (deque.size() > 0) {
+ @SuppressWarnings("unchecked") // deque is type
+ final E[] values = (E[]) deque.toArray(); // NOPMD - false positive for generics
+ deque.clear();
+ for (final E value : values) {
+ decorated.decorated().add(transformer.transform(value));
+ }
+ }
+ return decorated;
+ }
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * If there are any elements already in the deque being decorated, they
+ * are NOT transformed.
+ *
+ * @param deque the deque to decorate, must not be null
+ * @param transformer the transformer to use for conversion, must not be null
+ * @throws NullPointerException if deque or transformer is null
+ */
+ protected TransformedDeque(final Deque deque, final Transformer super E, ? extends E> transformer) {
+ super(deque, transformer);
+ }
+
+ /**
+ * Gets the decorated deque.
+ *
+ * @return the decorated deque
+ */
+ protected Deque getDeque() {
+ return (Deque) decorated();
+ }
+
+ @Override
+ public void addFirst(E e) {
+ getDeque().addFirst(transform(e));
+ }
+
+ @Override
+ public void addLast(E e) {
+ getDeque().addLast(transform(e));
+ }
+
+ @Override
+ public boolean offerFirst(E e) {
+ return getDeque().offerFirst(transform(e));
+ }
+
+ @Override
+ public boolean offerLast(E e) {
+ return getDeque().offerLast(transform(e));
+ }
+
+ @Override
+ public E removeFirst() {
+ return getDeque().removeFirst();
+ }
+
+ @Override
+ public E removeLast() {
+ return getDeque().removeLast();
+ }
+
+ @Override
+ public E pollFirst() {
+ return getDeque().pollFirst();
+ }
+
+ @Override
+ public E pollLast() {
+ return getDeque().pollLast();
+ }
+
+ @Override
+ public E getFirst() {
+ return getDeque().getFirst();
+ }
+
+ @Override
+ public E getLast() {
+ return getDeque().getLast();
+ }
+
+ @Override
+ public E peekFirst() {
+ return getDeque().peekFirst();
+ }
+
+ @Override
+ public E peekLast() {
+ return getDeque().peekLast();
+ }
+
+ @Override
+ public boolean removeFirstOccurrence(Object o) {
+ return getDeque().removeFirstOccurrence(transform((E) o));
+ }
+
+ @Override
+ public boolean removeLastOccurrence(Object o) {
+ return getDeque().removeLastOccurrence(transform((E) o));
+ }
+
+ @Override
+ public boolean offer(E e) {
+ return getDeque().offer(transform(e));
+ }
+
+ @Override
+ public E remove() {
+ return getDeque().remove();
+ }
+
+ @Override
+ public E poll() {
+ return getDeque().poll();
+ }
+
+ @Override
+ public E element() {
+ return getDeque().element();
+ }
+
+ @Override
+ public E peek() {
+ return getDeque().peek();
+ }
+
+ @Override
+ public void push(E e) {
+ getDeque().push(transform(e));
+ }
+
+ @Override
+ public E pop() {
+ return getDeque().pop();
+ }
+
+ @Override
+ public Iterator descendingIterator() {
+ return getDeque().descendingIterator();
+ }
+}
diff --git a/src/main/java/org/apache/commons/collections4/deque/package-info.java b/src/main/java/org/apache/commons/collections4/deque/package-info.java
new file mode 100644
index 0000000000..99540598e1
--- /dev/null
+++ b/src/main/java/org/apache/commons/collections4/deque/package-info.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+/**
+ * This package contains implementations for the {@link java.util.Deque Deque} interface.
+ *
+ * The following implementations are provided in the package:
+ *
+ *
CircularDeque - implements a Deque with a fixed size that discards oldest when full
+ *
+ *
+ * The following decorators are provided in the package:
+ *
+ *
Predicated - ensures that only elements that are valid according to a predicate can be added
+ *
Transformed - transforms elements added to the queue
+ *
Blocking - ensures the collection is thread-safe and can be operated concurrently
+ *
Synchronized - ensures the collection is thread-safe
+ *
+ *
+ */
+package org.apache.commons.collections4.deque;
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java
new file mode 100644
index 0000000000..a5c62c2454
--- /dev/null
+++ b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java
@@ -0,0 +1,807 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+import org.apache.commons.collections4.collection.AbstractCollectionTest;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Arrays;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import java.util.LinkedList;
+import java.util.Deque;
+import java.util.NoSuchElementException;
+
+/**
+ * Abstract test class for {@link java.util.Deque} methods and contracts.
+ *
+ * To use, simply extend this class, and implement
+ * the {@link #makeObject} method.
+ *
+ * If your {@link Deque} fails one of these tests by design,
+ * you may still use this base set of cases. Simply override the
+ * test case (method) your {@link Deque} fails or override one of the
+ * protected methods from AbstractCollectionTest.
+ *
+ * @since 4.5
+ */
+public abstract class AbstractDequeTest extends AbstractCollectionTest {
+ /**
+ * JUnit constructor.
+ *
+ * @param testName the test class name
+ */
+ public AbstractDequeTest(final String testName) {
+ super(testName);
+ }
+
+ /**
+ * Returns true if the collections produced by
+ * {@link #makeObject()} and {@link #makeFullCollection()}
+ * support the set operation.
+ * Default implementation returns true. Override if your collection
+ * class does not support set.
+ */
+ public boolean isSetSupported() {
+ return true;
+ }
+
+
+ /**
+ * Verifies that the test deque implementation matches the confirmed deque
+ * implementation.
+ */
+ @Override
+ public void verify() {
+ super.verify();
+ final Iterator iterator1 = getCollection().iterator();
+ for (final E e : getConfirmed()) {
+ assertTrue(iterator1.hasNext());
+ final Object o1 = iterator1.next();
+ final Object o2 = e;
+ assertEquals(o1, o2);
+ }
+ }
+
+ /**
+ * Returns an empty {@link LinkedList}.
+ */
+ @Override
+ public Collection makeConfirmedCollection() {
+ final LinkedList list = new LinkedList<>();
+ return list;
+ }
+
+ /**
+ * Returns a full {@link LinkedList}.
+ */
+ @Override
+ public Collection makeConfirmedFullCollection() {
+ final LinkedList list = new LinkedList<>();
+ list.addAll(Arrays.asList(getFullElements()));
+ return list;
+ }
+
+ /**
+ * Returns {@link #makeObject()}.
+ *
+ * @return an empty deque to be used for testing
+ */
+ @Override
+ public abstract Deque makeObject();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Deque makeFullCollection() {
+ // only works if Deque supports optional "addAll(Collection)"
+ final Deque deque = makeObject();
+ deque.addAll(Arrays.asList(getFullElements()));
+ return deque;
+ }
+
+ /**
+ * Returns the {@link #collection} field cast to a {@link Deque}.
+ *
+ * @return the collection field as a Deque
+ */
+ @Override
+ public Deque getCollection() {
+ return (Deque) super.getCollection();
+ }
+
+ /**
+ * Tests {@link Deque#addFirst(E e)}.
+ */
+ public void testDequeAddFirst() {
+ if (!isAddSupported()) {
+ return;
+ }
+
+ final E[] elements = getFullElements();
+ resetEmpty();
+ int size = 0;
+
+ for (final E element : elements) {
+ getCollection().addFirst(element);
+ getConfirmed().add(element);
+ size++;
+ assertEquals("Deque size should grow after add", size, getCollection().size());
+ assertTrue("Deque should contain added element", getCollection().contains(element));
+ }
+ final Deque deque = makeObject();
+ for (int i = 0; i < size; i++) {
+ deque.push(getCollection().pop());
+ }
+ setCollection(deque);
+ verify();
+ }
+
+ /**
+ * Tests {@link Deque#addLast(E e)}.
+ */
+ public void testDequeAddLast() {
+ if (!isAddSupported()) {
+ return;
+ }
+
+ resetEmpty();
+ int size = 0;
+ final E[] elements = getFullElements();
+ for (final E element : elements) {
+ getCollection().addLast(element);
+ getConfirmed().add(element);
+ verify();
+ size++;
+ assertEquals("Deque size should grow after add", size, getCollection().size());
+ assertTrue("Deque should contain added element", getCollection().contains(element));
+ }
+ }
+
+ /**
+ * Tests {@link Deque#offerFirst(E e)}.
+ */
+ public void testDequeOfferFirst() {
+ if (!isAddSupported()) {
+ return;
+ }
+
+ resetEmpty();
+ final E[] elements = getFullElements();
+ int size = 0;
+ for (final E element : elements) {
+ final boolean r = getCollection().offerFirst(element);
+ getConfirmed().add(element);
+ if (r) {
+ size++;
+ }
+ assertEquals("Deque size should grow after add", size, getCollection().size());
+ assertTrue("Deque should contain added element", getCollection().contains(element));
+ }
+ final Deque deque = makeObject();
+ for (int i = 0; i < size; i++) {
+ deque.push(getCollection().pop());
+ }
+ setCollection(deque);
+ verify();
+ }
+
+ /**
+ * Tests {@link Deque#offerLast(E e)}.
+ */
+ public void testDequeOfferLast() {
+ if (!isAddSupported()) {
+ return;
+ }
+
+ resetEmpty();
+ int size = 0;
+ final E[] elements = getFullElements();
+ for (final E element : elements) {
+ final boolean r = getCollection().offerLast(element);
+ getConfirmed().add(element);
+ verify();
+ if (r) {
+ size++;
+ }
+ assertEquals("Deque size should grow after add", size, getCollection().size());
+ assertTrue("Deque should contain added element", getCollection().contains(element));
+ }
+ }
+
+ /**
+ * Tests {@link Deque#removeFirst()}.
+ */
+ public void testDequeRemoveFirst() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetEmpty();
+
+ try {
+ getCollection().removeFirst();
+ fail("Deque.removeFirst should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+
+ resetFull();
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ final E element = getCollection().removeFirst();
+ final boolean success = getConfirmed().remove(element);
+ assertTrue("removeFirst should return correct element", success);
+ verify();
+ }
+
+ try {
+ getCollection().element();
+ fail("Deque.removeFirst should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests {@link Deque#removeLast()}.
+ */
+ public void testDequeRemoveLast() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetEmpty();
+
+ try {
+ getCollection().removeLast();
+ fail("Deque.remove should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+
+ resetFull();
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ final E element = getCollection().removeLast();
+ LinkedList list = (LinkedList) getConfirmed();
+ list.removeLast();
+ verify();
+ }
+
+ try {
+ getCollection().element();
+ fail("Deque.remove should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests {@link Deque#pollFirst()}.
+ */
+ public void testDequePollFirst() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetEmpty();
+
+ E element = getCollection().pollFirst();
+ assertNull(element);
+
+ resetFull();
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ element = getCollection().pollFirst();
+ final boolean success = getConfirmed().remove(element);
+ assertTrue("poll should return correct element", success);
+ verify();
+ }
+
+ element = getCollection().pollFirst();
+ assertNull(element);
+ }
+
+ /**
+ * Tests {@link Deque#pollLast()}.
+ */
+ public void testDequePollLast() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetEmpty();
+
+ E element = getCollection().pollLast();
+ assertNull(element);
+
+ resetFull();
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ getCollection().pollLast();
+ LinkedList list = (LinkedList) getConfirmed();
+ list.removeLast();
+ verify();
+ }
+
+ element = getCollection().pollLast();
+ assertNull(element);
+ }
+
+ /**
+ * Tests {@link Deque#getFirst()}.
+ */
+ public void testDequeGetFirst() {
+ resetEmpty();
+
+ try {
+ getCollection().getFirst();
+ fail("Deque.element should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+
+ resetFull();
+
+ assertTrue(getConfirmed().contains(getCollection().getFirst()));
+
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ final E element = getCollection().getFirst();
+
+ if (!isNullSupported()) {
+ assertNotNull(element);
+ }
+
+ assertTrue(getConfirmed().contains(element));
+
+ getCollection().remove(element);
+ getConfirmed().remove(element);
+
+ verify();
+ }
+
+ try {
+ getCollection().getFirst();
+ fail("Deque.element should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests {@link Deque#getLast()}.
+ */
+ public void testDequeGetLast() {
+ resetEmpty();
+
+ try {
+ getCollection().getLast();
+ fail("Deque.element should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+
+ resetFull();
+
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ final E element = getCollection().getLast();
+
+ if (!isNullSupported()) {
+ assertNotNull(element);
+ }
+ assertTrue(getConfirmed().contains(element));
+ getCollection().remove(element);
+ getConfirmed().remove(element);
+ verify();
+ }
+
+ try {
+ getCollection().getLast();
+ fail("Deque.element should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests {@link Deque#peekFirst()}.
+ */
+ public void testDequePeekFirst() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetEmpty();
+
+ E element = getCollection().peekFirst();
+ assertNull(element);
+
+ resetFull();
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ element = getCollection().peekFirst();
+
+ if (!isNullSupported()) {
+ assertNotNull(element);
+ }
+
+ assertTrue(getConfirmed().contains(element));
+
+ getCollection().remove(element);
+ getConfirmed().remove(element);
+
+ verify();
+ }
+
+ element = getCollection().peekFirst();
+ assertNull(element);
+ }
+
+ /**
+ * Tests {@link Deque#peekLast()}.
+ */
+ public void testDequePeekLast() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetEmpty();
+
+ E element = getCollection().peekLast();
+ assertNull(element);
+
+ resetFull();
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ element = getCollection().peekLast();
+
+ if (!isNullSupported()) {
+ assertNotNull(element);
+ }
+
+ assertTrue(getConfirmed().contains(element));
+
+ getCollection().remove(element);
+ getConfirmed().remove(element);
+
+ verify();
+ }
+
+ element = getCollection().peekLast();
+ assertNull(element);
+ }
+ /**
+ * Tests {@link Deque#offer(Object)}.
+ */
+ public void testDequeOffer() {
+ if (!isAddSupported()) {
+ return;
+ }
+
+ resetEmpty();
+ int size = 0;
+ final E[] elements = getFullElements();
+ for (final E element : elements) {
+ final boolean r = getCollection().offer(element);
+ getConfirmed().add(element);
+ verify();
+ if (r) {
+ size++;
+ }
+ assertEquals("Deque size should grow after add", size, getCollection().size());
+ assertTrue("Deque should contain added element", getCollection().contains(element));
+ }
+ }
+
+ /**
+ * Tests {@link Deque#element()}.
+ */
+ public void testDequeElement() {
+ resetEmpty();
+
+ try {
+ getCollection().element();
+ fail("Deque.element should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+
+ resetFull();
+
+ assertTrue(getConfirmed().contains(getCollection().element()));
+
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ final E element = getCollection().element();
+
+ if (!isNullSupported()) {
+ assertNotNull(element);
+ }
+
+ assertTrue(getConfirmed().contains(element));
+
+ getCollection().remove(element);
+ getConfirmed().remove(element);
+
+ verify();
+ }
+
+ try {
+ getCollection().element();
+ fail("Deque.element should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests {@link Deque#peek()}.
+ */
+ public void testDequePeek() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetEmpty();
+
+ E element = getCollection().peek();
+ assertNull(element);
+
+ resetFull();
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ element = getCollection().peek();
+
+ if (!isNullSupported()) {
+ assertNotNull(element);
+ }
+
+ assertTrue(getConfirmed().contains(element));
+
+ getCollection().remove(element);
+ getConfirmed().remove(element);
+
+ verify();
+ }
+
+ element = getCollection().peek();
+ assertNull(element);
+ }
+
+ /**
+ * Tests {@link Deque#remove()}.
+ */
+ public void testDequeRemove() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetEmpty();
+
+ try {
+ getCollection().remove();
+ fail("Deque.remove should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+
+ resetFull();
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ final E element = getCollection().remove();
+ final boolean success = getConfirmed().remove(element);
+ assertTrue("remove should return correct element", success);
+ verify();
+ }
+
+ try {
+ getCollection().element();
+ fail("Deque.remove should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests {@link Deque#poll()}.
+ */
+ public void testDequePoll() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetEmpty();
+
+ E element = getCollection().poll();
+ assertNull(element);
+
+ resetFull();
+
+ final int max = getFullElements().length;
+ for (int i = 0; i < max; i++) {
+ element = getCollection().poll();
+ final boolean success = getConfirmed().remove(element);
+ assertTrue("poll should return correct element", success);
+ verify();
+ }
+
+ element = getCollection().poll();
+ assertNull(element);
+ }
+
+ /**
+ * Tests {@link Deque#removeFirstOccurrence(Object o)}.
+ */
+ public void testDequeRemoveFirstOccurrence() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetFull();
+
+ final E[] elements = getFullElements();
+ for (final E element : elements) {
+ final boolean success = getCollection().removeFirstOccurrence(element);
+ assertTrue("element should be removed successfully", success);
+ getConfirmed().remove(element);
+ verify();
+ }
+
+ try {
+ getCollection().element();
+ fail("Deque.remove should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests {@link Deque#removeLastOccurrence(Object o)}.
+ */
+ public void testDequeRemoveLastOccurrence() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ resetFull();
+
+ final E[] elements = getFullElements();
+ for (final E element : elements) {
+ final boolean success = getCollection().removeLastOccurrence(element);
+ assertTrue("element should be removed successfully", success);
+ final LinkedList list = (LinkedList) getConfirmed();
+ list.removeLastOccurrence(element);
+ verify();
+ }
+
+ try {
+ getCollection().element();
+ fail("Deque.remove should throw NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests {@link Deque#descendingIterator()}.
+ */
+ public void testDescendingIterator() {
+ if (!isAddSupported()) {
+ return;
+ }
+
+ resetEmpty();
+ final E[] elements = getFullElements();
+ getCollection().addAll(Arrays.asList(elements));
+ final Iterator iter = getCollection().descendingIterator();
+ final int max = getFullElements().length;
+ for (int i = max-1; i >= 0; i--) {
+ assertTrue(iter.hasNext());
+ final Object o1 = iter.next();
+ assertEquals(elements[i],o1);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testEmptyDequeSerialization() throws IOException, ClassNotFoundException {
+ final Deque deque = makeObject();
+ if (!(deque instanceof Serializable && isTestSerialization())) {
+ return;
+ }
+
+ final byte[] object = writeExternalFormToBytes((Serializable) deque);
+ final Deque deque2 = (Deque) readExternalFormFromBytes(object);
+
+ assertEquals("Both deques are empty", 0, deque.size());
+ assertEquals("Both deques are empty", 0, deque2.size());
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testFullDequeSerialization() throws IOException, ClassNotFoundException {
+ final Deque deque = makeFullCollection();
+ final int size = getFullElements().length;
+ if (!(deque instanceof Serializable && isTestSerialization())) {
+ return;
+ }
+
+ final byte[] object = writeExternalFormToBytes((Serializable) deque);
+ final Deque deque2 = (Deque) readExternalFormFromBytes(object);
+
+ assertEquals("Both deques are same size", size, deque.size());
+ assertEquals("Both deques are same size", size, deque2.size());
+ }
+
+ /**
+ * Compare the current serialized form of the Deque
+ * against the canonical version in SVN.
+ */
+ @SuppressWarnings("unchecked")
+ public void testEmptyDequeCompatibility() throws IOException, ClassNotFoundException {
+
+ // test to make sure the canonical form has been preserved
+ final Deque deque = makeObject();
+ if (deque instanceof Serializable && !skipSerializedCanonicalTests()
+ && isTestSerialization()) {
+ writeExternalFormToDisk((Serializable) deque, getCanonicalEmptyCollectionName(deque));
+ final Deque deque2 = (Deque) readExternalFormFromDisk(getCanonicalEmptyCollectionName(deque));
+ assertEquals("Deque is empty", 0, deque2.size());
+ }
+ }
+
+ /**
+ * Compare the current serialized form of the Deque
+ * against the canonical version in SVN.
+ */
+ @SuppressWarnings("unchecked")
+ public void testFullDequeCompatibility() throws IOException, ClassNotFoundException {
+
+ // test to make sure the canonical form has been preserved
+ final Deque deque = makeFullCollection();
+ if(deque instanceof Serializable && !skipSerializedCanonicalTests() && isTestSerialization()) {
+ writeExternalFormToDisk((Serializable) deque, getCanonicalFullCollectionName(deque));
+ final Deque deque2 = (Deque) readExternalFormFromDisk(getCanonicalFullCollectionName(deque));
+ assertEquals("Deques are not the right size", deque.size(), deque2.size());
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/collections4/deque/BlockingDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/BlockingDequeTest.java
new file mode 100644
index 0000000000..36aa8366b4
--- /dev/null
+++ b/src/test/java/org/apache/commons/collections4/deque/BlockingDequeTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.Objects;
+
+/**
+ * Extension of {@link AbstractDequeTest} for exercising the
+ * {@link BlockingDeque} implementation.
+ *
+ * @since 4.5
+ */
+public class BlockingDequeTest extends AbstractDequeTest {
+ /**
+ * JUnit constructor.
+ *
+ * @param testName the test class name
+ */
+ public BlockingDequeTest(String testName) {
+ super(testName);
+ }
+
+ @Override
+ public Deque makeObject() {
+ return new BlockingDeque(new LinkedList());
+ }
+
+ public void testPutFirst() {
+ if (!isAddSupported()) {
+ return;
+ }
+
+ BlockingDeque deque = (BlockingDeque) makeObject();
+
+ final E[] elements = getFullElements();
+ int size = 0;
+ try {
+ for (final E element : elements) {
+ deque.putFirst(element);
+ size++;
+ assertEquals("Deque size should grow after add", size, deque.size());
+ assertTrue("Deque should contain added element", Objects.equals(element, deque.getFirst()));
+ }
+
+ } catch (InterruptedException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testPutLast() {
+ if (!isAddSupported()) {
+ return;
+ }
+
+ BlockingDeque deque = (BlockingDeque) makeObject();
+
+ final E[] elements = getFullElements();
+ int size = 0;
+ try {
+ for (final E element : elements) {
+ deque.putLast(element);
+ size++;
+ assertEquals("Deque size should grow after add", size, deque.size());
+ assertTrue("Deque should contain added element", Objects.equals(element, deque.getLast()));
+ }
+
+ } catch (InterruptedException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testTakeFirst() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ BlockingDeque deque = new BlockingDeque(makeFullCollection());
+ int size = deque.size();
+
+ assertTrue("Deque should contain elements",size > 0);
+
+ final E[] elements = getFullElements();
+ try {
+ for (final E element : elements) {
+ E e = deque.takeFirst();
+ size--;
+ assertEquals("Deque size should decrease",size, deque.size());
+ assertTrue(Objects.equals(element, e));
+ }
+ assertEquals("Deque should be empty", 0, deque.size());
+
+ } catch (InterruptedException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testTakeLast() {
+ if (!isRemoveSupported()) {
+ return;
+ }
+
+ BlockingDeque deque = new BlockingDeque(makeFullCollection());
+ int size = deque.size();
+
+ assertTrue("Deque should contain elements",size > 0);
+
+ final E[] elements = getFullElements();
+ try {
+ for (int i = size-1; i >= 0; i--) {
+ E e = deque.takeLast();
+ assertEquals("Deque size should decrease", i, deque.size());
+ assertTrue(Objects.equals(elements[i], e));
+ }
+ assertEquals("Deque should be empty", 0, deque.size());
+
+ } catch (InterruptedException ex) {
+ fail(ex.getMessage());
+ }
+ }
+ @Override
+ public String getCompatibilityVersion() {
+ return "4.5";
+ }
+}
diff --git a/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java
new file mode 100644
index 0000000000..544d03d225
--- /dev/null
+++ b/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Extension of {@link AbstractDequeTest} for exercising the
+ * {@link CircularDeque} implementation.
+ *
+ * @since 4.5
+ */
+public class CircularDequeTest extends AbstractDequeTest {
+ /**
+ * JUnit constructor.
+ *
+ * @param testName the test class name
+ */
+ public CircularDequeTest(String testName) {
+ super(testName);
+ }
+
+ /**
+ * Returns an empty CircularDeque that won't overflow.
+ *
+ * @return an empty CircularDeque
+ */
+ @Override
+ public Deque makeObject() {
+ return new CircularDeque<>(100);
+ }
+
+ /**
+ * Tests that the removal operation actually removes the first element.
+ */
+ @SuppressWarnings("unchecked")
+ public void testCircularDequeCircular() {
+ final List list = new LinkedList<>();
+ list.add((E) "A");
+ list.add((E) "B");
+ list.add((E) "C");
+ final Deque deque = new CircularDeque<>(list);
+
+ assertEquals(true, deque.contains("A"));
+ assertEquals(true, deque.contains("B"));
+ assertEquals(true, deque.contains("C"));
+
+ deque.add((E) "D");
+
+ assertEquals(false, deque.contains("A"));
+ assertEquals(true, deque.contains("B"));
+ assertEquals(true, deque.contains("C"));
+ assertEquals(true, deque.contains("D"));
+
+ assertEquals("B", deque.peek());
+ assertEquals("B", deque.remove());
+ assertEquals("C", deque.remove());
+ assertEquals("D", deque.remove());
+ }
+
+ /**
+ * Tests that the constructor correctly throws an exception.
+ */
+ public void testConstructorException1() {
+ try {
+ new CircularDeque(0);
+ fail();
+ } catch (final IllegalArgumentException ex) {
+
+ }
+ }
+
+ /**
+ * Tests that the constructor correctly throws an exception.
+ */
+ public void testConstructorException2() {
+ try {
+ new CircularDeque(-20);
+ fail();
+ } catch (final IllegalArgumentException ex) {
+
+ }
+ }
+
+ /**
+ * Tests that the constructor correctly throws an exception.
+ */
+ public void testConstructorException3() {
+ try {
+ new CircularDeque(null);
+ fail();
+ } catch (final NullPointerException ex) {
+
+ }
+ }
+
+ /**
+ * Tests that the removal operation using method removeFirstOccurrence() and removeFirstOccurrence().
+ */
+ public void testRemoveElement() {
+ final List list = new LinkedList<>();
+ list.add((E) "A");
+ list.add((E) "B");
+ list.add((E) "C");
+ list.add((E) "D");
+ list.add((E) "E");
+ final Deque deque = new CircularDeque<>(list);
+ assertEquals(5, ((CircularDeque) deque).maxSize());
+
+ deque.addLast((E) "F");
+ deque.addLast((E) "G");
+ deque.addLast((E) "H");
+
+ assertEquals(false, deque.removeFirstOccurrence("A"));
+ assertEquals(false, deque.removeFirstOccurrence("B"));
+ assertEquals(false, deque.removeLastOccurrence("C"));
+ assertEquals("[D, E, F, G, H]", deque.toString());
+
+ assertEquals(true, deque.removeLastOccurrence("H"));
+ assertEquals("[D, E, F, G]", deque.toString());
+
+ assertEquals(true, deque.removeLastOccurrence("E"));
+ assertEquals("[D, F, G]", deque.toString());
+
+ assertEquals(true, deque.removeLastOccurrence("F"));
+ assertEquals("[D, G]", deque.toString());
+
+ assertEquals("D", deque.removeFirst());
+ assertEquals("G", deque.removeLast());
+ }
+
+ public void testDescendingIterator() {
+ final List list = new LinkedList<>();
+ list.add((E) "A");
+ list.add((E) "B");
+ list.add((E) "C");
+ list.add((E) "D");
+ list.add((E) "E");
+ final Deque deque = new CircularDeque<>(list);
+
+ final Iterator iterator = deque.descendingIterator();
+ while (iterator.hasNext()) {
+ iterator.next();
+ iterator.remove();
+ }
+ assertTrue(deque.isEmpty());
+ }
+
+ @Override
+ public String getCompatibilityVersion() {
+ return "4.5";
+ }
+}
diff --git a/src/test/java/org/apache/commons/collections4/deque/PredicatedDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/PredicatedDequeTest.java
new file mode 100644
index 0000000000..457746804f
--- /dev/null
+++ b/src/test/java/org/apache/commons/collections4/deque/PredicatedDequeTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+import java.util.Arrays;
+import java.util.Deque;
+
+import java.util.LinkedList;
+
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.collections4.functors.NotNullPredicate;
+import org.apache.commons.collections4.functors.TruePredicate;
+
+/**
+ * Extension of {@link AbstractDequeTest} for exercising the
+ * {@link PredicatedDeque} implementation.
+ *
+ * @since 4.5
+ */
+public class PredicatedDequeTest extends AbstractDequeTest {
+ /**
+ * JUnit constructor.
+ *
+ * @param testName the test class name
+ */
+ public PredicatedDequeTest(String testName) {
+ super(testName);
+ }
+
+ protected Predicate notNullPredicate = NotNullPredicate.notNullPredicate();
+
+ protected Predicate truePredicate = TruePredicate.truePredicate();
+
+
+ protected Deque decorateDeque(final Deque queue, final Predicate predicate) {
+ return PredicatedDeque.predicatedDeque(queue, predicate);
+ }
+
+ @Override
+ public Deque makeObject() {
+ return decorateDeque(new LinkedList(), truePredicate);
+ }
+
+ @Override
+ public Deque makeFullCollection() {
+ final Deque deque = new LinkedList<>();
+ deque.addAll(Arrays.asList(getFullElements()));
+ return decorateDeque(deque, truePredicate);
+ }
+
+ public Deque makeTestDeque() {
+ return decorateDeque(new LinkedList(), notNullPredicate);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testPredicatedDeque() {
+ final Deque deque = makeTestDeque();
+
+ assertNull(deque.peek());
+
+ try {
+ deque.addFirst(null);
+ fail("Deque.addFirst should throw IllegalArgumentException");
+ } catch (final IllegalArgumentException e) {
+ // expected
+ }
+
+ deque.addFirst((E) "one");
+ deque.addFirst((E) "two");
+
+ assertEquals("Deque get", "two", deque.pollFirst());
+ assertEquals("Deque get", "one", deque.pollFirst());
+ assertNull(deque.pollFirst());
+ }
+
+ @Override
+ public String getCompatibilityVersion() {
+ return "4.5";
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/collections4/deque/SynchronizedDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/SynchronizedDequeTest.java
new file mode 100644
index 0000000000..d5af329d6d
--- /dev/null
+++ b/src/test/java/org/apache/commons/collections4/deque/SynchronizedDequeTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+import junit.framework.Test;
+import org.apache.commons.collections4.BulkTest;
+import org.junit.Ignore;
+
+import java.util.Deque;
+import java.util.LinkedList;
+
+/**
+ * Extension of {@link AbstractDequeTest} for exercising the
+ * {@link SynchronizedDeque} implementation.
+ *
+ * @since 4.5
+ */
+public class SynchronizedDequeTest extends AbstractDequeTest {
+ /**
+ * JUnit constructor.
+ *
+ * @param testName the test class name
+ */
+ public SynchronizedDequeTest(String testName) {
+ super(testName);
+ }
+
+ public static Test suite() {
+ return BulkTest.makeSuite(SynchronizedDequeTest.class);
+ }
+
+ @Override
+ public Deque makeObject() {
+ return SynchronizedDeque.synchronizedDeque(new LinkedList());
+ }
+
+ @Override
+ public String getCompatibilityVersion() {
+ return "4.5";
+ }
+
+ @Ignore("Run once")
+ public void testCreate() throws Exception {
+ Deque deque = makeObject();
+ writeExternalFormToDisk((java.io.Serializable) deque, "src/test/resources/data/test/SynchronizedDeque.emptyCollection.version4.5.obj");
+ deque = makeFullCollection();
+ writeExternalFormToDisk((java.io.Serializable) deque, "src/test/resources/data/test/SynchronizedDeque.fullCollection.version4.5.obj");
+ }
+}
diff --git a/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java
new file mode 100644
index 0000000000..3cd86c8731
--- /dev/null
+++ b/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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 org.apache.commons.collections4.deque;
+
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.collections4.collection.TransformedCollectionTest;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.LinkedList;
+
+/**
+ * Extension of {@link AbstractDequeTest} for exercising the
+ * {@link TransformedDeque} implementation.
+ *
+ * @since 4.5
+ */
+public class TransformedDequeTest extends AbstractDequeTest {
+
+ /**
+ * JUnit constructor.
+ *
+ * @param testName the test class name
+ */
+ public TransformedDequeTest(final String testName) {
+ super(testName);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Deque makeObject() {
+ return TransformedDeque.transformingDeque(new LinkedList(),
+ (Transformer) TransformedCollectionTest.NOOP_TRANSFORMER);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Deque makeFullCollection() {
+ final Deque list = new LinkedList<>();
+ list.addAll(Arrays.asList(getFullElements()));
+ return TransformedDeque.transformingDeque(list, (Transformer) TransformedCollectionTest.NOOP_TRANSFORMER);
+ }
+
+ public void testTransformedDeque() {
+ final Deque