From 150061b444ed98e34f7199cc252276e737ac0c5d Mon Sep 17 00:00:00 2001 From: dota17 Date: Fri, 1 Nov 2019 11:27:18 +0800 Subject: [PATCH 1/8] Remove the parens in the error message in CircularFifoQueue --- .../apache/commons/collections4/queue/CircularFifoQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/collections4/queue/CircularFifoQueue.java b/src/main/java/org/apache/commons/collections4/queue/CircularFifoQueue.java index f2bef6c898..d74288deb9 100644 --- a/src/main/java/org/apache/commons/collections4/queue/CircularFifoQueue.java +++ b/src/main/java/org/apache/commons/collections4/queue/CircularFifoQueue.java @@ -267,7 +267,7 @@ public E get(final int index) { final int sz = size(); if (index < 0 || index >= sz) { throw new NoSuchElementException( - String.format("The specified index (%1$d) is outside the available range [0, %2$d)", + String.format("The specified index %1$d is outside the available range [0, %2$d)", Integer.valueOf(index), Integer.valueOf(sz))); } From 459e8c7f315b9b36084c017febae732e9366b7ea Mon Sep 17 00:00:00 2001 From: dota17 Date: Thu, 7 Nov 2019 18:38:47 +0800 Subject: [PATCH 2/8] Add TransformedDeque --- .../collections4/deque/TransformedDeque.java | 223 +++++ .../collections4/deque/AbstractDequeTest.java | 774 ++++++++++++++++++ .../deque/TransformedDequeTest.java | 104 +++ 3 files changed, 1101 insertions(+) create mode 100644 src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java create mode 100644 src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java create mode 100644 src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java 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..d2843c328f --- /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 = -7901091318986132033L; + + /** + * 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 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 transformer) { + // throws IAE 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 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/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..c954eead9e --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java @@ -0,0 +1,774 @@ +/* + * 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.*; + +/** + * 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(E e) { + if (!isAddSupported()) { + return; + } + + final E[] elements = getFullElements(); + for (final E element : elements) { + resetEmpty(); + getCollection().addFirst(element); + getConfirmed().add(element); + verify(); + assertEquals("Deque size is 1 after first add", 1, getCollection().size()); + } + + resetEmpty(); + int size = 0; + for (final E element : elements) { + getCollection().addFirst(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#addLast(E e)}. + */ + public void testDequeAddLast(E e) { + if (!isAddSupported()) { + return; + } + + final E[] elements = getFullElements(); + for (final E element : elements) { + resetEmpty(); + getCollection().addLast(element); + getConfirmed().add(element); + verify(); + assertEquals("Deque size is 1 after first add", 1, getCollection().size()); + } + + resetEmpty(); + int size = 0; + 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(E e) { + if (!isAddSupported()) { + return; + } + + final E[] elements = getFullElements(); + for (final E element : elements) { + resetEmpty(); + getCollection().offerFirst(element); + getConfirmed().add(element); + verify(); + assertEquals("Deque size is 1 after first add", 1, getCollection().size()); + } + + resetEmpty(); + int size = 0; + 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#offerLast(E e)}. + */ + public void testDequeOfferLast(E e) { + if (!isAddSupported()) { + return; + } + + final E[] elements = getFullElements(); + for (final E element : elements) { + resetEmpty(); + final boolean r = getCollection().offerLast(element); + getConfirmed().add(element); + verify(); + assertTrue("Empty deque changed after add", r); + assertEquals("Deque size is 1 after first add", 1, getCollection().size()); + } + + resetEmpty(); + int size = 0; + 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.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().removeFirst(); + 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#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++) { + element = 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; + } + + final E[] elements = getFullElements(); + for (final E element : elements) { + resetEmpty(); + final boolean r = getCollection().offer(element); + getConfirmed().add(element); + verify(); + assertTrue("Empty deque changed after add", r); + assertEquals("Deque size is 1 after first add", 1, getCollection().size()); + } + + resetEmpty(); + int size = 0; + 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); + } + + @SuppressWarnings("unchecked") + public void testEmptyDequeSerialization() throws IOException, ClassNotFoundException { + final Deque deque = makeObject(); + if (!(deque instanceof Serializable && isTestSerialization())) { + return; + } + + final byte[] objekt = writeExternalFormToBytes((Serializable) deque); + final Deque deque2 = (Deque) readExternalFormFromBytes(objekt); + + 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[] objekt = writeExternalFormToBytes((Serializable) deque); + final Deque deque2 = (Deque) readExternalFormFromBytes(objekt); + + 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 { + /** + * Create canonical objects with this code + Deque deque = makeEmptyDeque(); + if (!(deque instanceof Serializable)) return; + + writeExternalFormToDisk((Serializable) deque, getCanonicalEmptyCollectionName(deque)); + */ + + // 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 { + /** + * Create canonical objects with this code + Deque deque = makeFullDeque(); + if (!(deque instanceof Serializable)) return; + + writeExternalFormToDisk((Serializable) deque, getCanonicalFullCollectionName(deque)); + */ + + // 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/TransformedDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java new file mode 100644 index 0000000000..eaf0f35c82 --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java @@ -0,0 +1,104 @@ +/* + * 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; + + +public class TransformedDequeTest extends AbstractDequeTest { + + /** + * JUnit constructor. + * + * @param testName the test class name + */ + public TransformedDequeTest(final String testName) { + super(testName); + } + + @Override + public Deque makeConfirmedCollection() { + return new LinkedList<>(); + } + + @Override + public Deque makeConfirmedFullCollection() { + final Deque list = new LinkedList<>(); + list.addAll(Arrays.asList(getFullElements())); + return list; + } + + @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 deque = TransformedDeque.transformingDeque(new LinkedList<>(), + TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER); + assertEquals(0, deque.size()); + final Object[] elements = new Object[] { "1", "3", "5", "7", "2", "4", "6" }; + for (int i = 0; i < elements.length; i++) { + deque.add(elements[i]); + assertEquals(i + 1, deque.size()); + assertEquals(true, deque.contains(Integer.valueOf((String) elements[i]))); + assertEquals(false, deque.contains(elements[i])); + } + + assertEquals(false, deque.remove(elements[0])); + assertEquals(true, deque.remove(Integer.valueOf((String) elements[0]))); + + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testTransformedDeque_decorateTransform() { + final Deque originalDeque = new LinkedList(); + final Object[] elements = new Object[] {"1", "3", "5", "7", "2", "4", "6"}; + Collections.addAll(originalDeque, elements); + final Deque deque = TransformedDeque.transformedDeque(originalDeque, + TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER); + assertEquals(elements.length, deque.size()); + for (final Object el : elements) { + assertEquals(true, deque.contains(Integer.valueOf((String) el))); + assertEquals(false, deque.contains(el)); + } + + assertEquals(false, deque.remove(elements[0])); + assertEquals(true, deque.remove(Integer.valueOf((String) elements[0]))); + } + + @Override + public String getCompatibilityVersion() { + return "4.5"; + } +} From faafcabf7e0309c4515c1a80993665aa8f7b3744 Mon Sep 17 00:00:00 2001 From: dota17 Date: Wed, 13 Nov 2019 15:58:13 +0800 Subject: [PATCH 3/8] Add test cases and correct some mistakes for TransformedDeque --- .../collections4/deque/AbstractDequeTest.java | 178 ++++++++++-------- .../deque/TransformedDequeTest.java | 12 -- 2 files changed, 102 insertions(+), 88 deletions(-) diff --git a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java index c954eead9e..a9540e4cc8 100644 --- a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java +++ b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java @@ -111,7 +111,6 @@ public Deque makeFullCollection() { return deque; } - //----------------------------------------------------------------------- /** * Returns the {@link #collection} field cast to a {@link Deque}. * @@ -125,51 +124,41 @@ public Deque getCollection() { /** * Tests {@link Deque#addFirst(E e)}. */ - public void testDequeAddFirst(E e) { + public void testDequeAddFirst() { if (!isAddSupported()) { return; } final E[] elements = getFullElements(); - for (final E element : elements) { - resetEmpty(); - getCollection().addFirst(element); - getConfirmed().add(element); - verify(); - assertEquals("Deque size is 1 after first add", 1, getCollection().size()); - } - resetEmpty(); int size = 0; + for (final E element : elements) { getCollection().addFirst(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)); } + 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(E e) { + public void testDequeAddLast() { if (!isAddSupported()) { return; } - final E[] elements = getFullElements(); - for (final E element : elements) { - resetEmpty(); - getCollection().addLast(element); - getConfirmed().add(element); - verify(); - assertEquals("Deque size is 1 after first add", 1, getCollection().size()); - } - resetEmpty(); int size = 0; + final E[] elements = getFullElements(); for (final E element : elements) { getCollection().addLast(element); getConfirmed().add(element); @@ -183,52 +172,42 @@ public void testDequeAddLast(E e) { /** * Tests {@link Deque#offerFirst(E e)}. */ - public void testDequeOfferFirst(E e) { + public void testDequeOfferFirst() { if (!isAddSupported()) { return; } - final E[] elements = getFullElements(); - for (final E element : elements) { - resetEmpty(); - getCollection().offerFirst(element); - getConfirmed().add(element); - verify(); - assertEquals("Deque size is 1 after first add", 1, getCollection().size()); - } - resetEmpty(); + final E[] elements = getFullElements(); int size = 0; for (final E element : elements) { - getCollection().addLast(element); + final boolean r = getCollection().offerFirst(element); getConfirmed().add(element); - verify(); - size++; + 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(E e) { + public void testDequeOfferLast() { if (!isAddSupported()) { return; } - final E[] elements = getFullElements(); - for (final E element : elements) { - resetEmpty(); - final boolean r = getCollection().offerLast(element); - getConfirmed().add(element); - verify(); - assertTrue("Empty deque changed after add", r); - assertEquals("Deque size is 1 after first add", 1, getCollection().size()); - } - resetEmpty(); int size = 0; + final E[] elements = getFullElements(); for (final E element : elements) { final boolean r = getCollection().offerLast(element); getConfirmed().add(element); @@ -253,7 +232,7 @@ public void testDequeRemoveFirst() { try { getCollection().removeFirst(); - fail("Deque.remove should throw NoSuchElementException"); + fail("Deque.removeFirst should throw NoSuchElementException"); } catch (final NoSuchElementException e) { // expected } @@ -264,13 +243,13 @@ public void testDequeRemoveFirst() { for (int i = 0; i < max; i++) { final E element = getCollection().removeFirst(); final boolean success = getConfirmed().remove(element); - assertTrue("remove should return correct element", success); + assertTrue("removeFirst should return correct element", success); verify(); } try { getCollection().element(); - fail("Deque.remove should throw NoSuchElementException"); + fail("Deque.removeFirst should throw NoSuchElementException"); } catch (final NoSuchElementException e) { // expected } @@ -355,7 +334,7 @@ public void testDequePollLast() { final int max = getFullElements().length; for (int i = 0; i < max; i++) { - element = getCollection().pollLast(); + getCollection().pollLast(); LinkedList list=(LinkedList) getConfirmed(); list.removeLast(); verify(); @@ -436,12 +415,9 @@ public void testDequeGetLast() { if (!isNullSupported()) { assertNotNull(element); } - assertTrue(getConfirmed().contains(element)); - getCollection().remove(element); getConfirmed().remove(element); - verify(); } @@ -530,18 +506,9 @@ public void testDequeOffer() { return; } - final E[] elements = getFullElements(); - for (final E element : elements) { - resetEmpty(); - final boolean r = getCollection().offer(element); - getConfirmed().add(element); - verify(); - assertTrue("Empty deque changed after add", r); - assertEquals("Deque size is 1 after first add", 1, getCollection().size()); - } - resetEmpty(); int size = 0; + final E[] elements = getFullElements(); for (final E element : elements) { final boolean r = getCollection().offer(element); getConfirmed().add(element); @@ -696,6 +663,79 @@ public void testDequePoll() { 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(); @@ -731,13 +771,6 @@ public void testFullDequeSerialization() throws IOException, ClassNotFoundExcept */ @SuppressWarnings("unchecked") public void testEmptyDequeCompatibility() throws IOException, ClassNotFoundException { - /** - * Create canonical objects with this code - Deque deque = makeEmptyDeque(); - if (!(deque instanceof Serializable)) return; - - writeExternalFormToDisk((Serializable) deque, getCanonicalEmptyCollectionName(deque)); - */ // test to make sure the canonical form has been preserved final Deque deque = makeObject(); @@ -755,13 +788,6 @@ && isTestSerialization()) { */ @SuppressWarnings("unchecked") public void testFullDequeCompatibility() throws IOException, ClassNotFoundException { - /** - * Create canonical objects with this code - Deque deque = makeFullDeque(); - if (!(deque instanceof Serializable)) return; - - writeExternalFormToDisk((Serializable) deque, getCanonicalFullCollectionName(deque)); - */ // test to make sure the canonical form has been preserved final Deque deque = makeFullCollection(); diff --git a/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java index eaf0f35c82..b9a67b2333 100644 --- a/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java +++ b/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java @@ -36,18 +36,6 @@ public TransformedDequeTest(final String testName) { super(testName); } - @Override - public Deque makeConfirmedCollection() { - return new LinkedList<>(); - } - - @Override - public Deque makeConfirmedFullCollection() { - final Deque list = new LinkedList<>(); - list.addAll(Arrays.asList(getFullElements())); - return list; - } - @Override @SuppressWarnings("unchecked") public Deque makeObject() { From d92ac530aaaadecd4f3625ad429fdf94af9777b1 Mon Sep 17 00:00:00 2001 From: dota17 Date: Wed, 13 Nov 2019 19:43:09 +0800 Subject: [PATCH 4/8] Fix some typo --- .../commons/collections4/deque/AbstractDequeTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java index a9540e4cc8..a124ae3938 100644 --- a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java +++ b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java @@ -335,7 +335,7 @@ public void testDequePollLast() { final int max = getFullElements().length; for (int i = 0; i < max; i++) { getCollection().pollLast(); - LinkedList list=(LinkedList) getConfirmed(); + LinkedList list = (LinkedList) getConfirmed(); list.removeLast(); verify(); } @@ -666,7 +666,7 @@ public void testDequePoll() { /** * Tests {@link Deque#removeFirstOccurrence(Object o)}. */ - public void testDequeRemoveFirstOccurrence(){ + public void testDequeRemoveFirstOccurrence() { if (!isRemoveSupported()) { return; } @@ -692,7 +692,7 @@ public void testDequeRemoveFirstOccurrence(){ /** * Tests {@link Deque#removeLastOccurrence(Object o)}. */ - public void testDequeRemoveLastOccurrence(){ + public void testDequeRemoveLastOccurrence() { if (!isRemoveSupported()) { return; } @@ -719,7 +719,7 @@ public void testDequeRemoveLastOccurrence(){ /** * Tests {@link Deque#descendingIterator()}. */ - public void testDescendingIterator(){ + public void testDescendingIterator() { if (!isAddSupported()) { return; } From 138898ce6227bb614b33fc091dbfd0a1fd6911c7 Mon Sep 17 00:00:00 2001 From: dota17 Date: Thu, 14 Nov 2019 10:52:25 +0800 Subject: [PATCH 5/8] Fix some typo1 --- .../collections4/deque/AbstractDequeTest.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java index a124ae3938..5cb33b28b3 100644 --- a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java +++ b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java @@ -20,7 +20,14 @@ import java.io.IOException; import java.io.Serializable; -import java.util.*; +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. @@ -277,7 +284,7 @@ public void testDequeRemoveLast() { final int max = getFullElements().length; for (int i = 0; i < max; i++) { final E element = getCollection().removeLast(); - LinkedList list=(LinkedList) getConfirmed(); + LinkedList list = (LinkedList) getConfirmed(); list.removeLast(); verify(); } @@ -703,7 +710,7 @@ public void testDequeRemoveLastOccurrence() { for (final E element : elements) { final boolean success = getCollection().removeLastOccurrence(element); assertTrue("element should be removed successfully", success); - final LinkedList list=(LinkedList) getConfirmed(); + final LinkedList list = (LinkedList) getConfirmed(); list.removeLastOccurrence(element); verify(); } From d682042aaa06754d74b79d74d5ea0447ad54bc99 Mon Sep 17 00:00:00 2001 From: dota17 Date: Fri, 29 Nov 2019 15:27:28 +0800 Subject: [PATCH 6/8] Add some files for Deque --- .../collections4/deque/BlockingDeque.java | 521 ++++++++++++++++++ .../collections4/deque/CircularDeque.java | 440 +++++++++++++++ .../collections4/deque/PredicatedDeque.java | 213 +++++++ .../collections4/deque/SynchronizedDeque.java | 261 +++++++++ .../collections4/deque/BlockingDequeTest.java | 140 +++++ .../deque/PredicatedDequeTest.java | 95 ++++ .../deque/SynchronizedDequeTest.java | 63 +++ .../deque/TransformedDequeTest.java | 7 +- 8 files changed, 1739 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java create mode 100644 src/main/java/org/apache/commons/collections4/deque/CircularDeque.java create mode 100644 src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java create mode 100644 src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java create mode 100644 src/test/java/org/apache/commons/collections4/deque/BlockingDequeTest.java create mode 100644 src/test/java/org/apache/commons/collections4/deque/PredicatedDequeTest.java create mode 100644 src/test/java/org/apache/commons/collections4/deque/SynchronizedDequeTest.java 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..9cda31f380 --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java @@ -0,0 +1,521 @@ +/* + * 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 { + 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)) == true) + 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)) == true) + 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)) == true) + notEmpty.signal(); + return r; + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeLastOccurrence(final Object o) { + lock.lock(); + try { + boolean r; + if ((r = deque.removeLastOccurrence(o)) == true) + 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 c) { + lock.lock(); + try { + boolean r; + if ((r = deque.addAll(c)) == true) + notEmpty.signalAll(); + return r; + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeAll(final Collection c) { + lock.lock(); + try { + boolean r; + if ((r = deque.removeAll(c)) == true) + 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..cc8e148ca4 --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java @@ -0,0 +1,440 @@ +/* + * 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.*; + +/** + * 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 { + + /** 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 coll) { + this(coll.size()); + 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 indexIncrease(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 mask = elements.length - 1; + int i = head; + Object x; + while ((x = elements[i]) != null) { + if (Objects.equals(o,x)) { + delete(i); + ArrayDeque; + return true; + } + i = (i + 1) & mask; + } + return false; + } + + @Override + public boolean removeLastOccurrence(final Object o) { + return false; + } + + @Override + public boolean offer(final E e) { + return false; + } + + @Override + public E remove() { + return null; + } + + @Override + public E poll() { + return null; + } + + @Override + public E element() { + return null; + } + + @Override + public E peek() { + return null; + } + + @Override + public void push(final E e) { + + } + + @Override + public E pop() { + return null; + } + + /** + * 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 0; + } + + @Override + public Iterator iterator() { + return null; + } + + @Override + public Iterator descendingIterator() { + return null; + } +} 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..003d0d6b2f --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java @@ -0,0 +1,213 @@ +/* + * 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. + *

+ *
Deque deque = PredicatedDeque.predicatedDeque(new UnboundedFifoDeque(), NotNullPredicate.INSTANCE);
+ * + * @param the type of elements held in this deque + * @since 4.5 + */ +public class PredicatedDeque extends PredicatedCollection implements Deque { + /** + * 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 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 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..f2e2a959ce --- /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 = 1L; + + /** + * 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/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/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 index b9a67b2333..3cd86c8731 100644 --- a/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java +++ b/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java @@ -24,7 +24,12 @@ 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 { /** From c9500bb6bdc4bc73a087fea322ebf9b05458c951 Mon Sep 17 00:00:00 2001 From: dota17 Date: Fri, 6 Dec 2019 16:17:27 +0800 Subject: [PATCH 7/8] Add CircularDeque --- .../collections4/deque/CircularDeque.java | 235 ++++++++++++++++-- .../collections4/deque/package-info.java | 34 +++ .../collections4/deque/CircularDequeTest.java | 170 +++++++++++++ 3 files changed, 418 insertions(+), 21 deletions(-) create mode 100644 src/main/java/org/apache/commons/collections4/deque/package-info.java create mode 100644 src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java diff --git a/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java b/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java index cc8e148ca4..a867e59210 100644 --- a/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java +++ b/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java @@ -23,8 +23,16 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.*; +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. @@ -99,7 +107,13 @@ public CircularDeque(final int size) { * @throws NullPointerException if the collection is null */ public CircularDeque(final Collection coll) { - this(coll.size()); + if (coll == null) { + throw new NullPointerException("Collection must not be null"); + } + + elements = (E[]) new Object[coll.size()]; + maxElements = elements.length; + addAll(coll); } @@ -167,7 +181,7 @@ public boolean isAtFullCapacity() { * @param index the index to increment * @return the updated index */ - private int indexIncrease(int index) { + private int increaseIndex(int index) { index++; if (index >= maxElements) { index = 0; @@ -336,58 +350,138 @@ public E peekLast() { @Override public boolean removeFirstOccurrence(final Object o) { - int mask = elements.length - 1; int i = head; - Object x; - while ((x = elements[i]) != null) { - if (Objects.equals(o,x)) { + while (true) { + if (Objects.equals(o,elements[i])) { delete(i); - ArrayDeque; return true; } - i = (i + 1) & mask; + if (i == decreaseIndex(tail)) { + return false; + } + i = increaseIndex(i); } - return false; } @Override public boolean removeLastOccurrence(final Object o) { - return false; + 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 false; + return offerLast(e); } @Override public E remove() { - return null; + return removeFirst(); } @Override public E poll() { - return null; + return pollFirst(); } @Override public E element() { - return null; + return getFirst(); } @Override public E peek() { - return null; + return peekFirst(); } @Override public void push(final E e) { - + addFirst(e); } @Override public E pop() { - return null; + 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; } /** @@ -425,16 +519,115 @@ public boolean isFull() { @Override public int maxSize() { - return 0; + return maxElements; } @Override public Iterator iterator() { - return null; + 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 null; + 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/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/CircularDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java new file mode 100644 index 0000000000..8175aeda29 --- /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); + } catch (final IllegalArgumentException ex) { + return; + } + fail(); + } + + /** + * Tests that the constructor correctly throws an exception. + */ + public void testConstructorException2() { + try { + new CircularDeque(-20); + } catch (final IllegalArgumentException ex) { + return; + } + fail(); + } + + /** + * Tests that the constructor correctly throws an exception. + */ + public void testConstructorException3() { + try { + new CircularDeque(null); + } catch (final NullPointerException ex) { + return; + } + fail(); + } + + /** + * 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"; + } +} From 896c4b7dd8737913cdae101c222941f3c96c93f6 Mon Sep 17 00:00:00 2001 From: dota17 Date: Fri, 20 Dec 2019 15:22:53 +0800 Subject: [PATCH 8/8] Add support for deque interface --- .../collections4/deque/BlockingDeque.java | 16 ++++++++++------ .../collections4/deque/CircularDeque.java | 5 ++++- .../collections4/deque/PredicatedDeque.java | 4 ++++ .../collections4/deque/SynchronizedDeque.java | 2 +- .../collections4/deque/TransformedDeque.java | 4 ++-- .../collections4/deque/AbstractDequeTest.java | 8 ++++---- .../collections4/deque/CircularDequeTest.java | 12 ++++++------ 7 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java b/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java index 9cda31f380..a85fd30759 100644 --- a/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java +++ b/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java @@ -40,6 +40,10 @@ * @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(); @@ -116,7 +120,7 @@ public boolean offerFirst(final E e) { lock.lock(); try { boolean r; - if ((r = deque.offerFirst(e)) == true) + if (r = deque.offerFirst(e)) notEmpty.signal(); return r; } catch (Exception ex) { @@ -139,7 +143,7 @@ public boolean offerLast(final E e) { lock.lock(); try { boolean r; - if ((r = deque.offerLast(e)) == true) + if (r = deque.offerLast(e)) notEmpty.signal(); return r; } catch (Exception ex) { @@ -330,7 +334,7 @@ public boolean removeFirstOccurrence(final Object o) { lock.lock(); try { boolean r; - if ((r = deque.removeFirstOccurrence(o)) == true) + if (r = deque.removeFirstOccurrence(o)) notEmpty.signal(); return r; } finally { @@ -343,7 +347,7 @@ public boolean removeLastOccurrence(final Object o) { lock.lock(); try { boolean r; - if ((r = deque.removeLastOccurrence(o)) == true) + if (r = deque.removeLastOccurrence(o)) notEmpty.signal(); return r; } finally { @@ -412,7 +416,7 @@ public boolean addAll(final Collection c) { lock.lock(); try { boolean r; - if ((r = deque.addAll(c)) == true) + if (r = deque.addAll(c)) notEmpty.signalAll(); return r; } finally { @@ -425,7 +429,7 @@ public boolean removeAll(final Collection c) { lock.lock(); try { boolean r; - if ((r = deque.removeAll(c)) == true) + if (r = deque.removeAll(c)) notFull.signalAll(); return r; } finally { diff --git a/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java b/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java index a867e59210..702c0ae45c 100644 --- a/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java +++ b/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java @@ -56,6 +56,9 @@ public class CircularDeque extends AbstractCollection implements Deque, BoundedCollection, Serializable { + /** Serialization version */ + private static final long serialVersionUID = 6013343683263686185L; + /** Underlying storage array. */ private transient E[] elements; @@ -469,7 +472,7 @@ private boolean delete(final int i) { System.arraycopy(elements, i + 1, elements, i, back); tail--; } - } else if (head >tail) { + } else if (head > tail) { if (i > head) { System.arraycopy(elements, head, elements, head + 1, front); head++; diff --git a/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java b/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java index 003d0d6b2f..a02c5005e7 100644 --- a/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java +++ b/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java @@ -40,6 +40,10 @@ * @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. *

diff --git a/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java b/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java index f2e2a959ce..e750f4cd82 100644 --- a/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java +++ b/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java @@ -34,7 +34,7 @@ public class SynchronizedDeque extends SynchronizedCollection implements Deque { /** Serialization version */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 4565577797338001569L; /** * Factory method to create a synchronized deque. diff --git a/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java b/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java index d2843c328f..dd480ebe5b 100644 --- a/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java +++ b/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java @@ -39,7 +39,7 @@ public class TransformedDeque extends TransformedCollection implements Deque { /** Serialization version */ - private static final long serialVersionUID = -7901091318986132033L; + private static final long serialVersionUID = 7959728067506831816L; /** * Factory method to create a transforming deque. @@ -76,7 +76,7 @@ public static TransformedDeque transformingDeque(final Deque deque, */ public static TransformedDeque transformedDeque(final Deque deque, final Transformer transformer) { - // throws IAE if deque or transformer is null + // throws NullPointerException if deque or transformer is null final TransformedDeque decorated = new TransformedDeque<>(deque, transformer); if (deque.size() > 0) { @SuppressWarnings("unchecked") // deque is type diff --git a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java index 5cb33b28b3..a5c62c2454 100644 --- a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java +++ b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java @@ -750,8 +750,8 @@ public void testEmptyDequeSerialization() throws IOException, ClassNotFoundExcep return; } - final byte[] objekt = writeExternalFormToBytes((Serializable) deque); - final Deque deque2 = (Deque) readExternalFormFromBytes(objekt); + 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()); @@ -765,8 +765,8 @@ public void testFullDequeSerialization() throws IOException, ClassNotFoundExcept return; } - final byte[] objekt = writeExternalFormToBytes((Serializable) deque); - final Deque deque2 = (Deque) readExternalFormFromBytes(objekt); + 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()); diff --git a/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java index 8175aeda29..544d03d225 100644 --- a/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java +++ b/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java @@ -81,10 +81,10 @@ public void testCircularDequeCircular() { public void testConstructorException1() { try { new CircularDeque(0); + fail(); } catch (final IllegalArgumentException ex) { - return; + } - fail(); } /** @@ -93,10 +93,10 @@ public void testConstructorException1() { public void testConstructorException2() { try { new CircularDeque(-20); + fail(); } catch (final IllegalArgumentException ex) { - return; + } - fail(); } /** @@ -105,10 +105,10 @@ public void testConstructorException2() { public void testConstructorException3() { try { new CircularDeque(null); + fail(); } catch (final NullPointerException ex) { - return; + } - fail(); } /**