All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.collections4.list.AbstractLinkedList Maven / Gradle / Ivy

There is a newer version: 5.0.70
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.collections4.list;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import org.apache.commons.collections4.OrderedIterator;

/**
 * An abstract implementation of a linked list which provides numerous points for
 * subclasses to override.
 * 

* Overridable methods are provided to change the storage node and to change how * nodes are added to and removed. Hopefully, all you need for unusual subclasses * is here. * * @since 3.0 */ public abstract class AbstractLinkedList implements List { /* * Implementation notes: * - a standard circular doubly-linked list * - a marker node is stored to mark the start and the end of the list * - node creation and removal always occurs through createNode() and * removeNode(). * - a modification count is kept, with the same semantics as * {@link java.util.LinkedList}. * - respects {@link AbstractList#modCount} */ /** * A {@link Node} which indicates the start and end of the list and does not * hold a value. The value of next is the first item in the * list. The value of of previous is the last item in the list. */ transient Node header; /** The size of the list */ transient int size; /** Modification count for iterators */ transient int modCount; /** * Constructor that does nothing intended for deserialization. *

* If this constructor is used by a serializable subclass then the init() * method must be called. */ protected AbstractLinkedList() { super(); } /** * Constructs a list copying data from the specified collection. * * @param coll the collection to copy */ protected AbstractLinkedList(final Collection coll) { super(); init(); addAll(coll); } /** * The equivalent of a default constructor, broken out so it can be called * by any constructor and by readObject. * Subclasses which override this method should make sure they call super, * so the list is initialised properly. */ protected void init() { header = createHeaderNode(); } //----------------------------------------------------------------------- @Override public int size() { return size; } @Override public boolean isEmpty() { return size() == 0; } @Override public E get(final int index) { final Node node = getNode(index, false); return node.getValue(); } //----------------------------------------------------------------------- @Override public Iterator iterator() { return listIterator(); } @Override public ListIterator listIterator() { return new LinkedListIterator<>(this, 0); } @Override public ListIterator listIterator(final int fromIndex) { return new LinkedListIterator<>(this, fromIndex); } //----------------------------------------------------------------------- @Override public int indexOf(final Object value) { int i = 0; for (Node node = header.next; node != header; node = node.next) { if (isEqualValue(node.getValue(), value)) { return i; } i++; } return -1; } @Override public int lastIndexOf(final Object value) { int i = size - 1; for (Node node = header.previous; node != header; node = node.previous) { if (isEqualValue(node.getValue(), value)) { return i; } i--; } return -1; } @Override public boolean contains(final Object value) { return indexOf(value) != -1; } @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } //----------------------------------------------------------------------- @Override public Object[] toArray() { return toArray(new Object[size]); } @Override @SuppressWarnings("unchecked") public T[] toArray(T[] array) { // Extend the array if needed if (array.length < size) { final Class componentType = array.getClass().getComponentType(); array = (T[]) Array.newInstance(componentType, size); } // Copy the values into the array int i = 0; for (Node node = header.next; node != header; node = node.next, i++) { array[i] = (T) node.getValue(); } // Set the value after the last value to null if (array.length > size) { array[size] = null; } return array; } /** * Gets a sublist of the main list. * * @param fromIndexInclusive the index to start from * @param toIndexExclusive the index to end at * @return the new sublist */ @Override public List subList(final int fromIndexInclusive, final int toIndexExclusive) { return new LinkedSubList<>(this, fromIndexInclusive, toIndexExclusive); } //----------------------------------------------------------------------- @Override public boolean add(final E value) { addLast(value); return true; } @Override public void add(final int index, final E value) { final Node node = getNode(index, true); addNodeBefore(node, value); } @Override public boolean addAll(final Collection coll) { return addAll(size, coll); } @Override public boolean addAll(final int index, final Collection coll) { final Node node = getNode(index, true); for (final E e : coll) { addNodeBefore(node, e); } return true; } //----------------------------------------------------------------------- @Override public E remove(final int index) { final Node node = getNode(index, false); final E oldValue = node.getValue(); removeNode(node); return oldValue; } @Override public boolean remove(final Object value) { for (Node node = header.next; node != header; node = node.next) { if (isEqualValue(node.getValue(), value)) { removeNode(node); return true; } } return false; } /** * {@inheritDoc} *

* This implementation iterates over the elements of this list, checking each element in * turn to see if it's contained in coll. If it's contained, it's removed * from this list. As a consequence, it is advised to use a collection type for * coll that provides a fast (e.g. O(1)) implementation of * {@link Collection#contains(Object)}. */ @Override public boolean removeAll(final Collection coll) { boolean modified = false; final Iterator it = iterator(); while (it.hasNext()) { if (coll.contains(it.next())) { it.remove(); modified = true; } } return modified; } //----------------------------------------------------------------------- /** * {@inheritDoc} *

* This implementation iterates over the elements of this list, checking each element in * turn to see if it's contained in coll. If it's not contained, it's removed * from this list. As a consequence, it is advised to use a collection type for * coll that provides a fast (e.g. O(1)) implementation of * {@link Collection#contains(Object)}. */ @Override public boolean retainAll(final Collection coll) { boolean modified = false; final Iterator it = iterator(); while (it.hasNext()) { if (coll.contains(it.next()) == false) { it.remove(); modified = true; } } return modified; } @Override public E set(final int index, final E value) { final Node node = getNode(index, false); final E oldValue = node.getValue(); updateNode(node, value); return oldValue; } @Override public void clear() { removeAllNodes(); } //----------------------------------------------------------------------- public E getFirst() { final Node node = header.next; if (node == header) { throw new NoSuchElementException(); } return node.getValue(); } public E getLast() { final Node node = header.previous; if (node == header) { throw new NoSuchElementException(); } return node.getValue(); } public boolean addFirst(final E o) { addNodeAfter(header, o); return true; } public boolean addLast(final E o) { addNodeBefore(header, o); return true; } public E removeFirst() { final Node node = header.next; if (node == header) { throw new NoSuchElementException(); } final E oldValue = node.getValue(); removeNode(node); return oldValue; } public E removeLast() { final Node node = header.previous; if (node == header) { throw new NoSuchElementException(); } final E oldValue = node.getValue(); removeNode(node); return oldValue; } //----------------------------------------------------------------------- @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (obj instanceof List == false) { return false; } final List other = (List) obj; if (other.size() != size()) { return false; } final ListIterator it1 = listIterator(); final ListIterator it2 = other.listIterator(); while (it1.hasNext() && it2.hasNext()) { final Object o1 = it1.next(); final Object o2 = it2.next(); if (!(o1 == null ? o2 == null : o1.equals(o2))) { return false; } } return !(it1.hasNext() || it2.hasNext()); } @Override public int hashCode() { int hashCode = 1; for (final E e : this) { hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode()); } return hashCode; } @Override public String toString() { if (size() == 0) { return "[]"; } final StringBuilder buf = new StringBuilder(16 * size()); buf.append('['); final Iterator it = iterator(); boolean hasNext = it.hasNext(); while (hasNext) { final Object value = it.next(); buf.append(value == this ? "(this Collection)" : value); hasNext = it.hasNext(); if (hasNext) { buf.append(", "); } } buf.append(']'); return buf.toString(); } //----------------------------------------------------------------------- /** * Compares two values for equals. * This implementation uses the equals method. * Subclasses can override this to match differently. * * @param value1 the first value to compare, may be null * @param value2 the second value to compare, may be null * @return true if equal */ protected boolean isEqualValue(final Object value1, final Object value2) { return value1 == value2 || (value1 == null ? false : value1.equals(value2)); } /** * Updates the node with a new value. * This implementation sets the value on the node. * Subclasses can override this to record the change. * * @param node node to update * @param value new value of the node */ protected void updateNode(final Node node, final E value) { node.setValue(value); } /** * Creates a new node with previous, next and element all set to null. * This implementation creates a new empty Node. * Subclasses can override this to create a different class. * * @return newly created node */ protected Node createHeaderNode() { return new Node<>(); } /** * Creates a new node with the specified properties. * This implementation creates a new Node with data. * Subclasses can override this to create a different class. * * @param value value of the new node * @return a new node containing the value */ protected Node createNode(final E value) { return new Node<>(value); } /** * Creates a new node with the specified object as its * value and inserts it before node. *

* This implementation uses {@link #createNode(Object)} and * {@link #addNode(AbstractLinkedList.Node,AbstractLinkedList.Node)}. * * @param node node to insert before * @param value value of the newly added node * @throws NullPointerException if node is null */ protected void addNodeBefore(final Node node, final E value) { final Node newNode = createNode(value); addNode(newNode, node); } /** * Creates a new node with the specified object as its * value and inserts it after node. *

* This implementation uses {@link #createNode(Object)} and * {@link #addNode(AbstractLinkedList.Node,AbstractLinkedList.Node)}. * * @param node node to insert after * @param value value of the newly added node * @throws NullPointerException if node is null */ protected void addNodeAfter(final Node node, final E value) { final Node newNode = createNode(value); addNode(newNode, node.next); } /** * Inserts a new node into the list. * * @param nodeToInsert new node to insert * @param insertBeforeNode node to insert before * @throws NullPointerException if either node is null */ protected void addNode(final Node nodeToInsert, final Node insertBeforeNode) { nodeToInsert.next = insertBeforeNode; nodeToInsert.previous = insertBeforeNode.previous; insertBeforeNode.previous.next = nodeToInsert; insertBeforeNode.previous = nodeToInsert; size++; modCount++; } /** * Removes the specified node from the list. * * @param node the node to remove * @throws NullPointerException if node is null */ protected void removeNode(final Node node) { node.previous.next = node.next; node.next.previous = node.previous; size--; modCount++; } /** * Removes all nodes by resetting the circular list marker. */ protected void removeAllNodes() { header.next = header; header.previous = header; size = 0; modCount++; } /** * Gets the node at a particular index. * * @param index the index, starting from 0 * @param endMarkerAllowed whether or not the end marker can be returned if * startIndex is set to the list's size * @return the node at the given index * @throws IndexOutOfBoundsException if the index is less than 0; equal to * the size of the list and endMakerAllowed is false; or greater than the * size of the list */ protected Node getNode(final int index, final boolean endMarkerAllowed) throws IndexOutOfBoundsException { // Check the index is within the bounds if (index < 0) { throw new IndexOutOfBoundsException("Couldn't get the node: " + "index (" + index + ") less than zero."); } if (!endMarkerAllowed && index == size) { throw new IndexOutOfBoundsException("Couldn't get the node: " + "index (" + index + ") is the size of the list."); } if (index > size) { throw new IndexOutOfBoundsException("Couldn't get the node: " + "index (" + index + ") greater than the size of the " + "list (" + size + ")."); } // Search the list and get the node Node node; if (index < size / 2) { // Search forwards node = header.next; for (int currentIndex = 0; currentIndex < index; currentIndex++) { node = node.next; } } else { // Search backwards node = header; for (int currentIndex = size; currentIndex > index; currentIndex--) { node = node.previous; } } return node; } //----------------------------------------------------------------------- /** * Creates an iterator for the sublist. * * @param subList the sublist to get an iterator for * @return a new iterator on the given sublist */ protected Iterator createSubListIterator(final LinkedSubList subList) { return createSubListListIterator(subList, 0); } /** * Creates a list iterator for the sublist. * * @param subList the sublist to get an iterator for * @param fromIndex the index to start from, relative to the sublist * @return a new list iterator on the given sublist */ protected ListIterator createSubListListIterator(final LinkedSubList subList, final int fromIndex) { return new LinkedSubListIterator<>(subList, fromIndex); } //----------------------------------------------------------------------- /** * Serializes the data held in this object to the stream specified. *

* The first serializable subclass must call this method from * writeObject. * * @param outputStream the stream to write the object to * @throws IOException if anything goes wrong */ protected void doWriteObject(final ObjectOutputStream outputStream) throws IOException { // Write the size so we know how many nodes to read back outputStream.writeInt(size()); for (final E e : this) { outputStream.writeObject(e); } } /** * Deserializes the data held in this object to the stream specified. *

* The first serializable subclass must call this method from * readObject. * * @param inputStream the stream to read the object from * @throws IOException if any error occurs while reading from the stream * @throws ClassNotFoundException if a class read from the stream can not be loaded */ @SuppressWarnings("unchecked") protected void doReadObject(final ObjectInputStream inputStream) throws IOException, ClassNotFoundException { init(); final int size = inputStream.readInt(); for (int i = 0; i < size; i++) { add((E) inputStream.readObject()); } } //----------------------------------------------------------------------- /** * A node within the linked list. *

* From Commons Collections 3.1, all access to the value property * is via the methods on this class. */ protected static class Node { /** A pointer to the node before this node */ protected Node previous; /** A pointer to the node after this node */ protected Node next; /** The object contained within this node */ protected E value; /** * Constructs a new header node. */ protected Node() { super(); previous = this; next = this; } /** * Constructs a new node. * * @param value the value to store */ protected Node(final E value) { super(); this.value = value; } /** * Constructs a new node. * * @param previous the previous node in the list * @param next the next node in the list * @param value the value to store */ protected Node(final Node previous, final Node next, final E value) { super(); this.previous = previous; this.next = next; this.value = value; } /** * Gets the value of the node. * * @return the value * @since 3.1 */ protected E getValue() { return value; } /** * Sets the value of the node. * * @param value the value * @since 3.1 */ protected void setValue(final E value) { this.value = value; } /** * Gets the previous node. * * @return the previous node * @since 3.1 */ protected Node getPreviousNode() { return previous; } /** * Sets the previous node. * * @param previous the previous node * @since 3.1 */ protected void setPreviousNode(final Node previous) { this.previous = previous; } /** * Gets the next node. * * @return the next node * @since 3.1 */ protected Node getNextNode() { return next; } /** * Sets the next node. * * @param next the next node * @since 3.1 */ protected void setNextNode(final Node next) { this.next = next; } } //----------------------------------------------------------------------- /** * A list iterator over the linked list. */ protected static class LinkedListIterator implements ListIterator, OrderedIterator { /** The parent list */ protected final AbstractLinkedList parent; /** * The node that will be returned by {@link #next()}. If this is equal * to {@link AbstractLinkedList#header} then there are no more values to return. */ protected Node next; /** * The index of {@link #next}. */ protected int nextIndex; /** * The last node that was returned by {@link #next()} or {@link * #previous()}. Set to null if {@link #next()} or {@link * #previous()} haven't been called, or if the node has been removed * with {@link #remove()} or a new node added with {@link #add(Object)}. * Should be accessed through {@link #getLastNodeReturned()} to enforce * this behaviour. */ protected Node current; /** * The modification count that the list is expected to have. If the list * doesn't have this count, then a * {@link java.util.ConcurrentModificationException} may be thrown by * the operations. */ protected int expectedModCount; /** * Create a ListIterator for a list. * * @param parent the parent list * @param fromIndex the index to start at * @throws IndexOutOfBoundsException if fromIndex is less than 0 or greater than the size of the list */ protected LinkedListIterator(final AbstractLinkedList parent, final int fromIndex) throws IndexOutOfBoundsException { super(); this.parent = parent; this.expectedModCount = parent.modCount; this.next = parent.getNode(fromIndex, true); this.nextIndex = fromIndex; } /** * Checks the modification count of the list is the value that this * object expects. * * @throws ConcurrentModificationException If the list's modification * count isn't the value that was expected. */ protected void checkModCount() { if (parent.modCount != expectedModCount) { throw new ConcurrentModificationException(); } } /** * Gets the last node returned. * * @return the last node returned * @throws IllegalStateException If {@link #next()} or {@link #previous()} haven't been called, * or if the node has been removed with {@link #remove()} or a new node added with {@link #add(Object)}. */ protected Node getLastNodeReturned() throws IllegalStateException { if (current == null) { throw new IllegalStateException(); } return current; } @Override public boolean hasNext() { return next != parent.header; } @Override public E next() { checkModCount(); if (!hasNext()) { throw new NoSuchElementException("No element at index " + nextIndex + "."); } final E value = next.getValue(); current = next; next = next.next; nextIndex++; return value; } @Override public boolean hasPrevious() { return next.previous != parent.header; } @Override public E previous() { checkModCount(); if (!hasPrevious()) { throw new NoSuchElementException("Already at start of list."); } next = next.previous; final E value = next.getValue(); current = next; nextIndex--; return value; } @Override public int nextIndex() { return nextIndex; } @Override public int previousIndex() { // not normally overridden, as relative to nextIndex() return nextIndex() - 1; } @Override public void remove() { checkModCount(); if (current == next) { // remove() following previous() next = next.next; parent.removeNode(getLastNodeReturned()); } else { // remove() following next() parent.removeNode(getLastNodeReturned()); nextIndex--; } current = null; expectedModCount++; } @Override public void set(final E obj) { checkModCount(); getLastNodeReturned().setValue(obj); } @Override public void add(final E obj) { checkModCount(); parent.addNodeBefore(next, obj); current = null; nextIndex++; expectedModCount++; } } //----------------------------------------------------------------------- /** * A list iterator over the linked sub list. */ protected static class LinkedSubListIterator extends LinkedListIterator { /** The parent list */ protected final LinkedSubList sub; protected LinkedSubListIterator(final LinkedSubList sub, final int startIndex) { super(sub.parent, startIndex + sub.offset); this.sub = sub; } @Override public boolean hasNext() { return nextIndex() < sub.size; } @Override public boolean hasPrevious() { return previousIndex() >= 0; } @Override public int nextIndex() { return super.nextIndex() - sub.offset; } @Override public void add(final E obj) { super.add(obj); sub.expectedModCount = parent.modCount; sub.size++; } @Override public void remove() { super.remove(); sub.expectedModCount = parent.modCount; sub.size--; } } //----------------------------------------------------------------------- /** * The sublist implementation for AbstractLinkedList. */ protected static class LinkedSubList extends AbstractList { /** The main list */ AbstractLinkedList parent; /** Offset from the main list */ int offset; /** Sublist size */ int size; /** Sublist modCount */ int expectedModCount; protected LinkedSubList(final AbstractLinkedList parent, final int fromIndex, final int toIndex) { if (fromIndex < 0) { throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); } if (toIndex > parent.size()) { throw new IndexOutOfBoundsException("toIndex = " + toIndex); } if (fromIndex > toIndex) { throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); } this.parent = parent; this.offset = fromIndex; this.size = toIndex - fromIndex; this.expectedModCount = parent.modCount; } @Override public int size() { checkModCount(); return size; } @Override public E get(final int index) { rangeCheck(index, size); checkModCount(); return parent.get(index + offset); } @Override public void add(final int index, final E obj) { rangeCheck(index, size + 1); checkModCount(); parent.add(index + offset, obj); expectedModCount = parent.modCount; size++; LinkedSubList.this.modCount++; } @Override public E remove(final int index) { rangeCheck(index, size); checkModCount(); final E result = parent.remove(index + offset); expectedModCount = parent.modCount; size--; LinkedSubList.this.modCount++; return result; } @Override public boolean addAll(final Collection coll) { return addAll(size, coll); } @Override public boolean addAll(final int index, final Collection coll) { rangeCheck(index, size + 1); final int cSize = coll.size(); if (cSize == 0) { return false; } checkModCount(); parent.addAll(offset + index, coll); expectedModCount = parent.modCount; size += cSize; LinkedSubList.this.modCount++; return true; } @Override public E set(final int index, final E obj) { rangeCheck(index, size); checkModCount(); return parent.set(index + offset, obj); } @Override public void clear() { checkModCount(); final Iterator it = iterator(); while (it.hasNext()) { it.next(); it.remove(); } } @Override public Iterator iterator() { checkModCount(); return parent.createSubListIterator(this); } @Override public ListIterator listIterator(final int index) { rangeCheck(index, size + 1); checkModCount(); return parent.createSubListListIterator(this, index); } @Override public List subList(final int fromIndexInclusive, final int toIndexExclusive) { return new LinkedSubList<>(parent, fromIndexInclusive + offset, toIndexExclusive + offset); } protected void rangeCheck(final int index, final int beyond) { if (index < 0 || index >= beyond) { throw new IndexOutOfBoundsException("Index '" + index + "' out of bounds for size '" + size + "'"); } } protected void checkModCount() { if (parent.modCount != expectedModCount) { throw new ConcurrentModificationException(); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy