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

org.jgrapht.util.DoublyLinkedList Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2018-2023, by Timofey Chudakov and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.util;

import org.jgrapht.alg.util.*;

import java.util.*;
import java.util.function.*;

/**
 * {@code DoublyLinkedList} implements a doubly linked {@link List} data structure, that exposes its
 * {@link ListNode ListNodes} where the data is stored in.
 * 

* An element holding {@code ListNode} can be removed or added to a {@code DoublyLinkedList} in * constant time O(1). Other methods that operate on {@code ListNodes} directly also have constant * runtime. This is also the case for methods that operate on the first(head) and last(tail) node or * element. Random access methods have a runtime O(n) that is linearly dependent on the size of the * {@code DoublyLinkedList}. *

*

* A {@code DoublyLinkedList} supports {@code null} elements but does not support * {@code null ListNodes}. This class is not thread safe and needs to be synchronized externally if * modified by concurrent threads. *

*

* The iterators over this list have a fail-fast behavior meaning that they throw a * {@link ConcurrentModificationException} after they detect a structural modification of the list, * that they're not responsible for. *

*

* This class is similar to {@link LinkedList}. The general difference is that the {@code ListNodes} * of this {@code List} are accessible and can be removed or added directly. To ensure the integrity * of the {@code List} nodes of this List have a reference to the List they belong to. This * increases the memory occupied by this list implementation compared to {@code LinkedList} for the * same elements. Instances of {@code LinkedList.Node} have three references each (the element, next * and previous), instances of {@code DoublyLinkedList.ListNode} have four (the element, next, * previous and the list). *

* * @param the list element type * @author Timofey Chudakov * @author Hannes Wellmann */ public class DoublyLinkedList extends AbstractSequentialList implements Deque { /** The first element of the list, {@code null} if this list is empty. */ private ListNodeImpl head = null; private int size; private ListNodeImpl tail() { return head.prev; } /** * {@inheritDoc} */ @Override public boolean isEmpty() { return head == null; } /** * {@inheritDoc} */ @Override public int size() { return size; } /** * {@inheritDoc} */ @Override public void clear() { if (!isEmpty()) { ListNodeImpl node = head; do { ListNodeImpl next = node.next; boolean removed = removeListNode(node); // clears all links of removed node assert removed; node = next; } while (node != head); head = null; assert size == 0; } } // internal modification methods /** * Adds the given {@link ListNode} to this {@code List}. *

* Sets the {@code list} reference of {@code node} to this list, increases this lists * {@code size} and {@code modcount} by one. *

* * @param node the node to add to this list * @throws IllegalArgumentException if {@code node} is already contained in this or another * {@code DoublyLinkedList} */ private void addListNode(ListNodeImpl node) { // call this before any modification of this list is done if (node.list != null) { String list = (node.list == this) ? "this" : "other"; throw new IllegalArgumentException( "Node <" + node + "> already contained in " + list + " list"); } node.list = this; size++; modCount++; } /** * Atomically moves all {@link ListNode ListNodes} from {@code list} to this list as if each * node was removed with {@link #removeListNode(ListNodeImpl)} from {@code list} and * subsequently added to this list by {@link #addListNode(ListNodeImpl)}. */ private void moveAllListNodes(DoublyLinkedList list) { // call this before any modification of this list is done for (ListNodeIteratorImpl it = list.new ListNodeIteratorImpl(0); it.hasNext();) { ListNodeImpl node = it.nextNode(); assert node.list == list; node.list = this; } size += list.size; list.size = 0; modCount++; list.modCount++; } /** * Removes the given {@link ListNode} from this {@code List}, if it is contained in this * {@code List}. *

* If {@code node} is contained in this list, sets the {@code list}, {@code next} and * {@code prev} reference of {@code node} to {@code null} decreases this list's {@code size} and * increases the {@code modcount} by one. *

* * @param node the node to remove from this list * @return true if {@code node} was removed from this list, else false */ private boolean removeListNode(ListNodeImpl node) { // call this before any modification of this list is done if (node.list == this) { node.list = null; node.next = null; node.prev = null; size--; modCount++; return true; } return false; } /** * Establishes the links between the given {@link ListNodeImpl nodes} in such a way that the * {@code predecessor} is linked before the {@code successor}. * * @param predecessor the first node linked before the other * @param successor the second node linked after the other */ private void link(ListNodeImpl predecessor, ListNodeImpl successor) { predecessor.next = successor; successor.prev = predecessor; } /** Insert non null {@code node} before non null {@code successor} into the list. */ private void linkBefore(ListNodeImpl node, ListNodeImpl successor) { addListNode(node); link(successor.prev, node); link(node, successor); } /** Insert non null {@code node} as last node into the list. */ private void linkLast(ListNodeImpl node) { if (isEmpty()) { // node will be the first and only one addListNode(node); link(node, node); // self link head = node; } else { linkBefore(node, head); } } /** Insert non null {@code list} before node at {@code index} into the list. */ private void linkListIntoThisBefore(int index, DoublyLinkedList list) { int previousSize = size; moveAllListNodes(list); // link list's node into this list if (previousSize == 0) { head = list.head; // head and tail already linked together } else { ListNodeImpl refNode = (index == previousSize) ? head : getNodeAt(index); ListNodeImpl listTail = list.tail(); link(refNode.prev, list.head); // changes list.tail() link(listTail, refNode); if (index == 0) { head = list.head; } } // clear list but do not call list.clear(), since their nodes are still used list.head = null; } /** Remove the non null {@code node} from the list. */ private boolean unlink(ListNodeImpl node) { ListNodeImpl prev = node.prev; ListNodeImpl next = node.next; if (removeListNode(node)) { // clears prev and next of node if (size == 0) { head = null; } else { // list is circular, don't have to worry about null values link(prev, next); if (head == node) { head = next; } } return true; } return false; } // ---------------------------------------------------------------------------- // public modification and access methods // ListNode methods: // Base methods to access, add and remove nodes to/from this list. // Used by all public methods if possible /** * Inserts the specified {@link ListNode node} at the specified position in this list. *

* This method has a linear runtime complexity O(n) that depends linearly on the distance of the * index to the nearest end. Adding {@code node} as first or last takes only constant time O(1). *

* * @param index index at which the specified {@code node} is to be inserted * @param node the node to add * @throws IndexOutOfBoundsException if the index is out of range * ({@code index < 0 || index > size()}) * @throws IllegalArgumentException if {@code node} is already part of this or another * {@code DoublyLinkedList} * @throws NullPointerException if {@code node} is {@code null} */ public void addNode(int index, ListNode node) { ListNodeImpl nodeImpl = (ListNodeImpl) node; if (index == size) { // also true if this is empty linkLast(nodeImpl); } else { ListNodeImpl successor = index == 0 ? head : getNodeAt(index); linkBefore(nodeImpl, successor); if (head == successor) { head = nodeImpl; } } } /** * Inserts the specified {@link ListNode node} at the front of this list. *

* This method has constant runtime complexity O(1). *

* * @param node the node to add * @throws IllegalArgumentException if {@code node} is already part of this or another * {@code DoublyLinkedList} * @throws NullPointerException if {@code node} is {@code null} */ public void addNodeFirst(ListNode node) { addNode(0, node); } /** * Inserts the specified {@link ListNode node} at the end of this list. *

* This method has constant runtime complexity O(1). *

* * @param node the node to add * @throws IllegalArgumentException if {@code node} is already part of this or another * {@code DoublyLinkedList} * @throws NullPointerException if {@code node} is {@code null} */ public void addNodeLast(ListNode node) { addNode(size, node); } /** * Inserts the specified {@link ListNode node} before the specified {@code successor} in this * list. *

* This method has constant runtime complexity O(1). *

* * @param node the node to add * @param successor {@code ListNode} before which the {@code node} is inserted * @throws IllegalArgumentException if {@code node} is already contained in this or another * {@code DoublyLinkedList} or {@code successor} is not contained in this list * @throws NullPointerException if {@code successor} or {@code node} is {@code null} */ public void addNodeBefore(ListNode node, ListNode successor) { ListNodeImpl successorImpl = (ListNodeImpl) successor; ListNodeImpl nodeImpl = (ListNodeImpl) node; if (successorImpl.list != this) { throw new IllegalArgumentException("Node <" + successorImpl + "> not in this list"); } linkBefore(nodeImpl, successorImpl); if (head == successorImpl) { head = nodeImpl; } } /** * Returns the first {@link ListNode node} of this list. *

* This method has constant runtime complexity O(1). *

* * @return the first {@code ListNode} of this list * @throws NoSuchElementException if this list is empty */ public ListNode getFirstNode() { if (isEmpty()) { throw new NoSuchElementException(); } return head; } /** * Returns the last {@link ListNode node} of this list. *

* This method has constant runtime complexity O(1). *

* * @return the last {@code ListNode} of this list * @throws NoSuchElementException if this list is empty */ public ListNode getLastNode() { if (isEmpty()) { throw new NoSuchElementException(); } return tail(); } /** * Returns the {@link ListNode node} at the specified position in this list. *

* This method has linear runtime complexity O(n). *

* * @param index index of the {@code ListNode} to return * @return the {@code ListNode} at the specified position in this list * @throws IndexOutOfBoundsException if the index is out of range * ({@code index < 0 || index >= size()}) */ public ListNode getNode(int index) { return getNodeAt(index); } /** * Returns the {@link ListNodeImpl node} at the specified position in this list. * * @param index index of the {@code ListNodeImpl} to return * @return the {@code ListNode} at the specified position in this list * @throws IndexOutOfBoundsException if the index is out of range * ({@code index < 0 || index >= size()}) */ private ListNodeImpl getNodeAt(int index) { if (index < 0 || size <= index) { throw new IndexOutOfBoundsException("Index: " + index); } ListNodeImpl node; if (index < size / 2) { node = head; for (int i = 0; i < index; i++) { node = node.next; } } else { node = tail(); for (int i = size - 1; index < i; i--) { node = node.prev; } } return node; } /** * Returns the index of the specified {@link ListNode node} in this list, or -1 if this list * does not contain the {@code node}. *

* More formally, returns the index {@code i} such that {@code node == getNode(i)}, or -1 if * there is no such index. Because a {@code ListNode} is contained in at most one list exactly * once, the returned index (if not -1) is the only occurrence of that {@code node}. *

*

* This method has linear runtime complexity O(n) to find {@code node} but returns in constant * time O(1) if {@code node} is not {@link #containsNode(ListNode) contained} in this list. *

* * @param node the node to search for * @return the index of the specified {@code node} in this list, or -1 if this list does not * contain {@code node} * @throws NullPointerException if {@code node} is {@code null} */ public int indexOfNode(ListNode node) { if (!containsNode(node)) { return -1; } ListNodeImpl current = head; for (int i = 0; i < size; i++) { if (current == node) { return i; } current = current.next; } // should never happen: throw new IllegalStateException("Node contained in list not found: " + node); } /** * Returns true if this {@code DoublyLinkedList} contains the specified {@link ListNode}. *

* This method has constant runtime complexity O(1). *

* * @param node the node whose presence in this {@code DoublyLinkedList} is to be tested * @return true if this {@code DoublyLinkedList} contains the {@link ListNode} * @throws NullPointerException if {@code node} is {@code null} */ public boolean containsNode(ListNode node) { return ((ListNodeImpl) node).list == this; } /** * Removes the {@link ListNode node} from this list. Returns true if {@code node} was in this * list and is now removed. If {@code node} is not contained in this list, the list is left * unchanged. *

* This method has constant runtime complexity O(1). *

* * @param node the node to remove from this list * @return true if node was removed from this list * @throws NullPointerException if {@code node} is {@code null} */ public boolean removeNode(ListNode node) { return unlink((ListNodeImpl) node); } /** * Returns the first {@link ListNode node} holding the specified {@code element} in this list. * More formally, returns the first {@code ListNode} such that * {@code Objects.equals(element, node.getValue())}, or {@code null} if there is no such node. *

* This method has linear runtime complexity O(n). *

* * @param element the element whose {@code ListNode} is to return * @return the first {@code ListNode} holding the {@code element} or null if no node was found */ public ListNode nodeOf(Object element) { return searchNode(() -> head, n -> n.next, element).getFirst(); } /** * Returns the last {@link ListNode node} holding the specified {@code element} in this list. * More formally, returns the last {@code ListNode} such that * {@code Objects.equals(element, node.getValue())}, or {@code null} if there is no such node. *

* This method has linear runtime complexity O(n). *

* * @param element the element whose {@code ListNode} is to return * @return the last {@code ListNode} holding the {@code element} or null if no node was found */ public ListNode lastNodeOf(Object element) { return searchNode(this::tail, n -> n.prev, element).getFirst(); } /** * Returns a {@link Pair} of the first encountered {@link ListNode} in this list, whose * {@code value} is equal to the given {@code element}, and its index. Or if this list does not * contain such node a Pair of {@code null} and {@code -1}; *

* The search starts at the node supplied by {@code first} and advances in the direction induced * by the specified {@code next} operator. *

* * @param first supplier of the first node to check if this list is not empty * @param next {@code Function} to get from the current node the next node to check * @param element the element for that the first node with equal value is searched. * @return a {@link Pair} of the first encountered {@code ListNode} holding a {@code value} * equal to {@code element} and its index, or if no such node was found a * {@code Pair.of(null, -1)} */ private Pair, Integer> searchNode( Supplier> first, UnaryOperator> next, Object element) { if (!isEmpty()) { int index = 0; ListNodeImpl firstNode = first.get(); ListNodeImpl node = firstNode; do { if (Objects.equals(node.value, element)) { return Pair.of(node, index); } index++; node = next.apply(node); } while (node != firstNode); } return Pair.of(null, -1); } /** * Inserts the specified element at the front of this list. Returns the {@link ListNode} * allocated to store the {@code value}. The returned {@code ListNode} is the new head of the * list. *

* This method is equivalent to {@link #addFirst(Object)} but returns the allocated * {@code ListNode}. *

* * @param element the element to add * @return the {@code ListNode} allocated to store the {@code value} */ public ListNode addElementFirst(E element) { ListNode node = new ListNodeImpl<>(element); addNode(0, node); return node; } /** * Inserts the specified element at the end of this list. Returns the {@link ListNode} allocated * to store the {@code value}. The returned {@code ListNode} is the new tail of the list. *

* This method is equivalent to {@link #addLast(Object)} but returns the allocated * {@code ListNode}. *

* * @param element the element to add * @return the {@code ListNode} allocated to store the {@code value} */ public ListNode addElementLast(E element) { ListNode node = new ListNodeImpl<>(element); addNode(size, node); return node; } /** * Inserts the specified element before the specified {@link ListNode successor} in this list. * Returns the {@code ListNode} allocated to store the {@code value}. * * @param successor {@code ListNode} before which the node holding {@code value} is inserted * @param element the element to add * @return the {@code ListNode} allocated to store the {@code value} * @throws IllegalArgumentException if {@code successor} is not contained in this list * @throws NullPointerException if {@code successor} is {@code null} */ public ListNode addElementBeforeNode(ListNode successor, E element) { ListNode node = new ListNodeImpl<>(element); addNodeBefore(node, successor); return node; } // List methods (shortcut for most commonly used methods to avoid iterator creation) /** * {@inheritDoc} */ @Override public void add(int index, E element) { if (index == size) { // also true if this is empty addElementLast(element); } else { addElementBeforeNode(getNode(index), element); } } /** * {@inheritDoc} */ @Override public E get(int index) { return getNodeAt(index).value; } /** * {@inheritDoc} */ @Override public E remove(int index) { ListNode node = getNode(index); removeNode(node); return node.getValue(); } // Deque methods /** * {@inheritDoc} */ @Override public void addFirst(E e) { addElementFirst(e); } /** * {@inheritDoc} */ @Override public void addLast(E e) { addElementLast(e); } /** * {@inheritDoc} */ @Override public boolean offerFirst(E e) { addElementFirst(e); return true; } /** * {@inheritDoc} */ @Override public boolean offerLast(E e) { addElementLast(e); return true; } /** * {@inheritDoc} */ @Override public E removeFirst() { if (isEmpty()) { throw new NoSuchElementException(); } ListNode node = head; removeNode(node); // changes head return node.getValue(); } /** * {@inheritDoc} */ @Override public E removeLast() { if (isEmpty()) { throw new NoSuchElementException(); } ListNode node = tail(); removeNode(node); // changes tail return node.getValue(); } /** * {@inheritDoc} */ @Override public E pollFirst() { if (isEmpty()) { return null; } ListNode node = head; removeNode(node); // changes head return node.getValue(); } /** * {@inheritDoc} */ @Override public E pollLast() { if (isEmpty()) { return null; } ListNode node = tail(); removeNode(node); // changes tail() return node.getValue(); } /** * {@inheritDoc} */ @Override public E getFirst() { return getFirstNode().getValue(); } /** * {@inheritDoc} */ @Override public E getLast() { return getLastNode().getValue(); } /** * {@inheritDoc} */ @Override public E peekFirst() { return isEmpty() ? null : getFirst(); } /** * {@inheritDoc} */ @Override public E peekLast() { return isEmpty() ? null : getLast(); } /** * {@inheritDoc} */ @Override public boolean removeFirstOccurrence(Object o) { ListNode node = nodeOf(o); if (node != null) { removeNode(node); return true; } return false; } /** * {@inheritDoc} */ @Override public boolean removeLastOccurrence(Object o) { ListNode node = lastNodeOf(o); if (node != null) { removeNode(node); return true; } return false; } // Queue methods /** * {@inheritDoc} */ @Override public boolean offer(E e) { return offerLast(e); } /** * {@inheritDoc} */ @Override public E remove() { return removeFirst(); } /** * {@inheritDoc} */ @Override public E poll() { return pollFirst(); } /** * {@inheritDoc} */ @Override public E element() { return getFirst(); } /** * {@inheritDoc} */ @Override public E peek() { return peekFirst(); } // Stack methods /** * {@inheritDoc} */ @Override public void push(E e) { addFirst(e); } /** * {@inheritDoc} */ @Override public E pop() { return removeFirst(); } // special bulk methods /** * Inverts the list. For instance, calling this method on the list $(a,b,c,\dots,x,y,z)$ will * result in the list $(z,y,x,\dots,c,b,a)$. This method does only pointer manipulation, meaning * that all the list nodes allocated for the previously added elements are valid after this * method finishes. */ public void invert() { if (size < 2) { return; } ListNodeImpl newHead = tail(); ListNodeImpl current = head; do { ListNodeImpl next = current.next; current.next = current.prev; current.prev = next; current = next; } while (current != head); head = newHead; ++modCount; } /** * Moves all {@link ListNode ListNodes} of the given {@code sourceList} to this list and inserts * them all before the node previously at the given position. All the {@code nodes} of * {@code movedList} are moved to this list. When this method terminates this list contains all * nodes of {@code movedList} and {@code movedList} is empty. * * @param index index of the first element of {@code list} in this {@code list} after it was * added * @param movedList the {@code DoublyLinkedList} to move to this one * @throws NullPointerException if {@code movedList} is {@code null} */ public void moveFrom(int index, DoublyLinkedList movedList) { linkListIntoThisBefore(index, movedList); } /** * Appends the {@code movedList} to the end of this list. All the elements from * {@code movedList} are transferred to this list, i.e. the {@code list} is empty after calling * this method. * * @param movedList the {@code DoublyLinkedList} to append to this one * @throws NullPointerException if {@code movedList} is {@code null} */ public void append(DoublyLinkedList movedList) { moveFrom(size, movedList); } /** * Prepends the {@code movedList} to the beginning of this list. All the elements from * {@code movedList} are transferred to this list, i.e. the {@code movedList} is empty after * calling this method. * * @param movedList the {@code DoublyLinkedList} to prepend to this one * @throws NullPointerException if {@code movedList} is {@code null} */ public void prepend(DoublyLinkedList movedList) { moveFrom(0, movedList); } // ---------------------------------------------------------------------------- // (List)Iterators /** * Returns a {@link NodeIterator} that starts at the first {@link ListNode} of this list that is * equal to the specified {@code firstElement}, iterates in forward direction over the end of * this list until the first node. *

* The first call to {@link NodeIterator#nextNode()} returns the first {@code node} that holds a * value such that {@code Objects.equals(node.getValue, firstElement)} returns {@code true}. The * returned {@code NodeIterator} iterates in forward direction returning the respective next * element in subsequent calls to {@code next(Node)}. The returned iterator ignores the actual * bounds of this {@code DoublyLinkedList} and iterates until the node before the first one is * reached. Its {@link NodeIterator#hasNext() hasNext()} returns {@code false} if the next node * would be the first one. *

* * @param firstElement the element equal to the first {@code next()} * @return a circular {@code NodeIterator} iterating forward from {@code firstElement} */ public NodeIterator circularIterator(E firstElement) { ListNodeImpl startNode = (ListNodeImpl) nodeOf(firstElement); if (startNode == null) { throw new NoSuchElementException(); } return new ListNodeIteratorImpl(0, startNode); } /** * Returns a {@link NodeIterator} that starts at the first {@link ListNode} of this list that is * equal to the specified {@code firstElement}, iterates in reverse direction over the end of * this list until the first node. *

* The first call to {@link NodeIterator#nextNode()} returns the first {@code node} that holds a * value such that {@code Objects.equals(node.getValue, firstElement)} returns {@code true}. The * returned {@code NodeIterator} iterates in reverse direction returning the respective previous * element in subsequent calls to {@code next(Node)}. The returned iterator ignores the actual * bounds of this {@code DoublyLinkedList} and iterates until the node before the first one is * reached. Its {@link NodeIterator#hasNext() hasNext()} returns {@code false} if the next node * would be the first one. *

* * @param firstElement the element equal to the first {@code next()} * @return a circular {@code NodeIterator} iterating backwards from {@code firstElement} */ public NodeIterator reverseCircularIterator(E firstElement) { ListNodeImpl startNode = (ListNodeImpl) nodeOf(firstElement); if (startNode == null) { throw new NoSuchElementException(); } return reverseIterator(new ListNodeIteratorImpl(size, startNode.next)); } /** * {@inheritDoc} */ @Override public NodeIterator descendingIterator() { return reverseIterator(listIterator(size)); } /** * {@inheritDoc} */ @Override public NodeIterator iterator() { return listIterator(); } /** * {@inheritDoc} */ @Override public ListNodeIterator listIterator() { return listIterator(0); } /** * {@inheritDoc} */ @Override public ListNodeIterator listIterator(int index) { return new ListNodeIteratorImpl(index); } /** * Returns a {@link ListNodeIterator} over the elements in this list (in proper sequence) * starting with the first {@link ListNode} whose value is equal to the specified * {@code element}. * * @param element the first element to be returned from the list iterator (by a call to the * {@code next} method) * @return a list iterator over the elements in this list (in proper sequence) * @throws NoSuchElementException if {@code element} is not in the list */ public ListNodeIterator listIterator(E element) { Pair, Integer> startPair = searchNode(() -> head, n -> n.next, element); ListNodeImpl startNode = startPair.getFirst(); int startIndex = startPair.getSecond(); if (startNode == null) { throw new NoSuchElementException(); } return new ListNodeIteratorImpl(startIndex, startNode); } /** * An extension of the {@link Iterator} interface for {@link DoublyLinkedList DoublyLinkedLists} * exposing their {@link ListNode ListNodes}. * * @param the list element type */ public interface NodeIterator extends Iterator { /** * {@inheritDoc} */ @Override default E next() { return nextNode().getValue(); } /** * Returns the next {@link ListNode} in the list and advances the cursor position. * * @return the next {@code ListNode} * @see ListIterator#next() */ ListNode nextNode(); } /** * An extension of the {@link ListIterator} interface for {@link DoublyLinkedList * DoublyLinkedLists} exposing their {@link ListNode ListNodes}. * * @param the list element type */ public interface ListNodeIterator extends ListIterator, NodeIterator { /** * {@inheritDoc} */ @Override default E next() { return nextNode().getValue(); } /** * {@inheritDoc} */ @Override default E previous() { return previousNode().getValue(); } /** * Returns the previous {@link ListNode} in the list and moves the cursor position * backwards. * * @return the previous {@code ListNode} * @see ListIterator#previous() */ ListNode previousNode(); } /** * An implementation of the {@link DoublyLinkedList.ListNodeIterator} interface. */ private class ListNodeIteratorImpl implements ListNodeIterator { /** Index in this list of the ListNode returned next. */ private int nextIndex; /** ListNode this iterator will return next. Null if this list is empty. */ private ListNodeImpl next; /** ListNode this iterator returned last. */ private ListNodeImpl last = null; /** * The number of modifications the list have had at the moment when this iterator was * created */ private int expectedModCount = modCount; private ListNodeIteratorImpl(int startIndex) { this.nextIndex = startIndex; if (startIndex == size) { this.next = isEmpty() ? null : head; } else { this.next = getNodeAt(startIndex); } } private ListNodeIteratorImpl(int startIndex, ListNodeImpl startNode) { this.nextIndex = startIndex; this.next = startNode; } /** * {@inheritDoc} */ @Override public boolean hasNext() { return nextIndex < size; } /** * {@inheritDoc} */ @Override public boolean hasPrevious() { return nextIndex > 0; } /** * {@inheritDoc} */ @Override public int nextIndex() { return nextIndex; } /** * {@inheritDoc} */ @Override public int previousIndex() { return nextIndex - 1; } /** * {@inheritDoc} */ @Override public ListNodeImpl nextNode() { checkForComodification(); if (!hasNext()) { throw new NoSuchElementException(); } last = next; next = next.next; nextIndex++; return last; } /** * {@inheritDoc} */ @Override public ListNode previousNode() { checkForComodification(); if (!hasPrevious()) { throw new NoSuchElementException(); } last = next = next.prev; nextIndex--; return last; } /** * {@inheritDoc} */ @Override public void add(E e) { checkForComodification(); if (nextIndex == size) { addElementLast(e); // sets head to new node of e if was empty if (size == 1) { // was empty next = head; // jump over head threshold, so cursor is at the end } } else { addElementBeforeNode(next, e); } last = null; nextIndex++; expectedModCount++; } /** * {@inheritDoc} */ @Override public void set(E e) { if (last == null) { throw new IllegalStateException(); } checkForComodification(); // replace node returned last with a new node holding e ListNode nextNode = last.next; boolean wasLast = last == tail(); removeNode(last); if (wasLast) { // or the sole node last = (ListNodeImpl) addElementLast(e); } else { last = (ListNodeImpl) addElementBeforeNode(nextNode, e); } expectedModCount += 2; // because of unlink and add } /** * {@inheritDoc} */ @Override public void remove() { if (last == null) { throw new IllegalStateException(); } checkForComodification(); ListNodeImpl lastsNext = last.next; removeNode(last); if (next == last) { // previousNode() called before // removed element after cursor (which would have been next) next = lastsNext; } else { // nextNode() called before // removed element before cursor (next is unaffected but the index decreases) nextIndex--; } last = null; expectedModCount++; } /** * Verifies that the list structure hasn't been changed since the iteration started */ private void checkForComodification() { if (expectedModCount != modCount) { throw new ConcurrentModificationException(); } } } /** * Returns a {@link NodeIterator} that iterates in reverse order, assuming the cursor of the * specified {@link ListNodeIterator} is behind the tail of the list. */ private static NodeIterator reverseIterator(ListNodeIterator listIterator) { return new NodeIterator() { /** * {@inheritDoc} */ @Override public boolean hasNext() { return listIterator.hasPrevious(); } /** * {@inheritDoc} */ @Override public ListNode nextNode() { return listIterator.previousNode(); } /** * {@inheritDoc} */ @Override public void remove() { listIterator.remove(); } }; } /** * Container for the elements stored in a {@link DoublyLinkedList}. *

* A {@link ListNode} is either contained exactly once in exactly one {@code DoublyLinkedList} * or contained in no {@code DoublyLinkedList}. *

* * @param the type of the element stored in this node */ public interface ListNode { /** * Returns the immutable value this {@code ListNode} contains. * * @return the value this list node contains */ V getValue(); /** * Returns the next node in the list structure with respect to this node * * @return the next node in the list structure with respect to this node */ ListNode getNext(); /** * Returns the previous node in the list structure with respect to this node * * @return the previous node in the list structure with respect to this node */ ListNode getPrev(); } /** * The default {@link ListNode} implementation that enables checks and enforcement of a single * container list policy. */ private static class ListNodeImpl implements ListNode { private final V value; private DoublyLinkedList list = null; private ListNodeImpl next = null; private ListNodeImpl prev = null; /** * Creates new list node * * @param value the value this list node stores */ ListNodeImpl(V value) { this.value = value; } /** * {@inheritDoc} */ @Override public String toString() { if (list == null) { return " - " + value + " - "; // not in a list } else { return prev.value + " -> " + value + " -> " + next.value; } } /** * {@inheritDoc} */ @Override public V getValue() { return value; } /** * {@inheritDoc} */ @Override public ListNodeImpl getNext() { return next; } /** * {@inheritDoc} */ @Override public ListNodeImpl getPrev() { return prev; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy