gnu.trove.TLinkedList Maven / Gradle / Ivy
Show all versions of trove4j Show documentation
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001, Eric D. Friedman All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
///////////////////////////////////////////////////////////////////////////////
package gnu.trove;
import java.io.*;
import java.util.AbstractSequentialList;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
* A LinkedList implementation which holds instances of type
* TLinkable.
*
* Using this implementation allows you to get java.util.LinkedList
* behavior (a doubly linked list, with Iterators that support insert
* and delete operations) without incurring the overhead of creating
* Node wrapper objects for every element in your list.
*
* The requirement to achieve this time/space gain is that the
* Objects stored in the List implement the TLinkable
* interface.
*
* The limitations are that you cannot put the same object into
* more than one list or more than once in the same list. You must
* also ensure that you only remove objects that are actually in the
* list. That is, if you have an object A and lists l1 and l2, you
* must ensure that you invoke List.remove(A) on the correct list. It
* is also forbidden to invoke List.remove() with an unaffiliated
* TLinkable (one that belongs to no list): this will destroy the list
* you invoke it on.
*
*
* Created: Sat Nov 10 15:25:10 2001
*
*
* @author Eric D. Friedman
* @version $Id: TLinkedList.java,v 1.12 2007/11/01 16:08:14 robeden Exp $
* @see gnu.trove.TLinkable
*/
public class TLinkedList extends AbstractSequentialList
implements Externalizable {
static final long serialVersionUID = 1L;
/** the head of the list */
protected T _head;
/** the tail of the list */
protected T _tail;
/** the number of elements in the list */
protected int _size = 0;
/**
* Creates a new TLinkedList
instance.
*
*/
public TLinkedList() {
super();
}
/**
* Returns an iterator positioned at index. Assuming
* that the list has a value at that index, calling next() will
* retrieve and advance the iterator. Assuming that there is a
* value before index in the list, calling previous()
* will retrieve it (the value at index - 1) and move the iterator
* to that position. So, iterating from front to back starts at
* 0; iterating from back to front starts at size().
*
* @param index an int
value
* @return a ListIterator
value
*/
public ListIterator listIterator(int index) {
return new IteratorImpl(index);
}
/**
* Returns the number of elements in the list.
*
* @return an int
value
*/
public int size() {
return _size;
}
/**
* Inserts linkable at index index in the list.
* All values > index are shifted over one position to accommodate
* the new addition.
*
* @param index an int
value
* @param linkable an object of type TLinkable
*/
public void add(int index, T linkable) {
if (index < 0 || index > size()) {
throw new IndexOutOfBoundsException("index:" + index);
}
insert(index,linkable);
}
/**
* Appends linkable to the end of the list.
*
* @param linkable an object of type TLinkable
* @return always true
*/
public boolean add(T linkable) {
insert(_size, linkable);
return true;
}
/**
* Inserts linkable at the head of the list.
*
* @param linkable an object of type TLinkable
*/
public void addFirst(T linkable) {
insert(0, linkable);
}
/**
* Adds linkable to the end of the list.
*
* @param linkable an object of type TLinkable
*/
public void addLast(T linkable) {
insert(size(), linkable);
}
/**
* Empties the list.
*
*/
public void clear() {
if (null != _head) {
for (TLinkable link = _head.getNext();
link != null;
link = link.getNext()) {
TLinkable prev = link.getPrevious();
prev.setNext(null);
link.setPrevious(null);
}
_head = _tail = null;
}
_size = 0;
}
/**
* Copies the list's contents into a native array. This will be a
* shallow copy: the Tlinkable instances in the Object[] array
* have links to one another: changing those will put this list
* into an unpredictable state. Holding a reference to one
* element in the list will prevent the others from being garbage
* collected unless you clear the next/previous links. Caveat
* programmer!
*
* @return an Object[]
value
*/
public Object[] toArray() {
Object[] o = new Object[_size];
int i = 0;
for (TLinkable link = _head; link != null; link = link.getNext()) {
o[i++] = link;
}
return o;
}
/**
* Copies the list to a native array, destroying the next/previous
* links as the copy is made. This list will be emptied after the
* copy (as if clear() had been invoked). The Object[] array
* returned will contain TLinkables that do not hold
* references to one another and so are less likely to be the
* cause of memory leaks.
*
* @return an Object[]
value
*/
public Object[] toUnlinkedArray() {
Object[] o = new Object[_size];
int i = 0;
for (T link = _head, tmp = null; link != null; i++) {
o[i] = link;
tmp = link;
link = (T) link.getNext();
tmp.setNext(null); // clear the links
tmp.setPrevious(null);
}
_size = 0; // clear the list
_head = _tail = null;
return o;
}
/**
* A linear search for o in the list.
*
* @param o an Object
value
* @return a boolean
value
*/
public boolean contains(Object o) {
for (TLinkable link = _head; link != null; link = link.getNext()) {
if (o.equals(link)) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public T get(int index) {
// Blow out for bogus values
if (index < 0 || index >= _size ) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + _size);
}
// Determine if it's better to get there from the front or the back
if (index > (_size >> 1)) {
int position = _size - 1;
T node = _tail;
while ( position > index ) {
node = ( T ) node.getPrevious();
position--;
}
return node;
}
else {
int position = 0;
T node = _head;
while( position < index ) {
node = ( T ) node.getNext();
position++;
}
return node;
}
}
/**
* Returns the head of the list
*
* @return an Object
value
*/
public T getFirst() {
return _head;
}
/**
* Returns the tail of the list.
*
* @return an Object
value
*/
public T getLast() {
return _tail;
}
/**
* Return the node following the given node. This method exists for two reasons:
*
* - It's really not recommended that the methods implemented by TLinkable be
* called directly since they're used internally by this class.
* - This solves problems arising from generics when working with the linked
* objects directly.
*
*
* NOTE: this should only be used with nodes contained in the list. The results are
* undefined with anything else.
*/
public T getNext( T current ) {
return ( T ) current.getNext();
}
/**
* Return the node preceding the given node. This method exists for two reasons:
*
* - It's really not recommended that the methods implemented by TLinkable be
* called directly since they're used internally by this class.
* - This solves problems arising from generics when working with the linked
* objects directly.
*
*
* NOTE: this should only be used with nodes contained in the list. The results are
* undefined with anything else.
*/
public T getPrevious( T current ) {
return ( T ) current.getPrevious();
}
/**
* Remove and return the first element in the list.
*
* @return an Object
value
*/
public T removeFirst() {
T o = _head;
T n = (T) o.getNext();
o.setNext(null);
if (null != n) {
n.setPrevious(null);
}
_head = n;
if (--_size == 0) {
_tail = null;
}
return o;
}
/**
* Remove and return the last element in the list.
*
* @return an Object
value
*/
public T removeLast() {
T o = _tail;
T prev = (T) o.getPrevious();
o.setPrevious(null);
if (null != prev) {
prev.setNext(null);
}
_tail = prev;
if (--_size == 0) {
_head = null;
}
return o;
}
/**
* Implementation of index-based list insertions.
*
* @param index an int
value
* @param linkable an object of type TLinkable
*/
protected void insert(int index, T linkable) {
T newLink = linkable;
if (_size == 0) {
_head = _tail = newLink; // first insertion
} else if (index == 0) {
newLink.setNext(_head); // insert at front
_head.setPrevious(newLink);
_head = newLink;
} else if (index == _size) { // insert at back
_tail.setNext(newLink);
newLink.setPrevious(_tail);
_tail = newLink;
} else {
T node = get(index);
T before = (T) node.getPrevious();
if (before != null) before.setNext(linkable);
linkable.setPrevious(before);
linkable.setNext(node);
node.setPrevious(linkable);
}
_size++;
}
/**
* Removes the specified element from the list. Note that
* it is the caller's responsibility to ensure that the
* element does, in fact, belong to this list and not another
* instance of TLinkedList.
*
* @param o a TLinkable element already inserted in this list.
* @return true if the element was a TLinkable and removed
*/
public boolean remove(Object o) {
if (o instanceof TLinkable) {
T p, n;
TLinkable link = (TLinkable)o;
p = (T) link.getPrevious();
n = (T) link.getNext();
if (n == null && p == null) { // emptying the list
_head = _tail = null;
} else if (n == null) { // this is the tail
// make previous the new tail
link.setPrevious(null);
p.setNext(null);
_tail = p;
} else if (p == null) { // this is the head
// make next the new head
link.setNext(null);
n.setPrevious(null);
_head = n;
} else { // somewhere in the middle
p.setNext(n);
n.setPrevious(p);
link.setNext(null);
link.setPrevious(null);
}
_size--; // reduce size of list
return true;
} else {
return false;
}
}
/**
* Inserts newElement into the list immediately before current.
* All elements to the right of and including current are shifted
* over.
*
* @param current a TLinkable
value currently in the list.
* @param newElement a TLinkable
value to be added to
* the list.
*/
public void addBefore(T current, T newElement) {
if (current == _head) {
addFirst(newElement);
} else if (current == null) {
addLast(newElement);
} else {
TLinkable p = current.getPrevious();
newElement.setNext(current);
p.setNext(newElement);
newElement.setPrevious(p);
current.setPrevious(newElement);
_size++;
}
}
/**
* Inserts newElement into the list immediately after current.
* All elements to the left of and including current are shifted
* over.
*
* @param current a TLinkable
value currently in the list.
* @param newElement a TLinkable
value to be added to
* the list.
*/
public void addAfter(T current, T newElement) {
if (current == _tail) {
addLast(newElement);
} else if (current == null) {
addFirst(newElement);
} else {
TLinkable n = current.getNext();
newElement.setPrevious(current);
newElement.setNext(n);
current.setNext(newElement);
n.setPrevious(current);
_size++;
}
}
/**
* Executes procedure for each entry in the list.
*
* @param procedure a TObjectProcedure
value
* @return false if the loop over the values terminated because
* the procedure returned false for some value.
*/
public boolean forEachValue(TObjectProcedure procedure) {
T node = _head;
while ( node != null ) {
boolean keep_going = procedure.execute( node );
if ( !keep_going ) return false;
node = ( T ) node.getNext();
}
return true;
}
public void writeExternal( ObjectOutput out ) throws IOException {
// VERSION
out.writeByte(0);
// NUMBER OF ENTRIES
out.writeInt(_size);
// HEAD
out.writeObject(_head);
// TAIL
out.writeObject(_head);
}
public void readExternal( ObjectInput in )
throws IOException, ClassNotFoundException {
// VERSION
in.readByte();
// NUMBER OF ENTRIED
_size = in.readInt();
// HEAD
_head = (T) in.readObject();
// TAIL
_tail = (T) in.readObject();
}
/**
* A ListIterator that supports additions and deletions.
*
*/
protected final class IteratorImpl implements ListIterator {
private int _nextIndex = 0;
private T _next;
private T _lastReturned;
/**
* Creates a new Iterator
instance positioned at
* index.
*
* @param position an int
value
*/
IteratorImpl(int position) {
if (position < 0 || position > _size) {
throw new IndexOutOfBoundsException();
}
_nextIndex = position;
if (position == 0) {
_next = _head;
} else if (position == _size) {
_next = null;
} else if (position < (_size >> 1)) {
int pos = 0;
for (_next = _head; pos < position; pos++) {
_next = (T) _next.getNext();
}
} else {
int pos = _size - 1;
for (_next = _tail; pos > position; pos--) {
_next = (T) _next.getPrevious();
}
}
}
/**
* Insert linkable at the current position of the iterator.
* Calling next() after add() will return the added object.
*
* @param linkable an object of type TLinkable
*/
public final void add(T linkable) {
_lastReturned = null;
_nextIndex++;
if (_size == 0) {
TLinkedList.this.add(linkable);
} else {
TLinkedList.this.addBefore(_next, linkable);
}
}
/**
* True if a call to next() will return an object.
*
* @return a boolean
value
*/
public final boolean hasNext() {
return _nextIndex != _size;
}
/**
* True if a call to previous() will return a value.
*
* @return a boolean
value
*/
public final boolean hasPrevious() {
return _nextIndex != 0;
}
/**
* Returns the value at the Iterator's index and advances the
* iterator.
*
* @return an Object
value
* @exception NoSuchElementException if there is no next element
*/
public final T next() {
if (_nextIndex == _size) {
throw new NoSuchElementException();
}
_lastReturned = _next;
_next = (T) _next.getNext();
_nextIndex++;
return _lastReturned;
}
/**
* returns the index of the next node in the list (the
* one that would be returned by a call to next()).
*
* @return an int
value
*/
public final int nextIndex() {
return _nextIndex;
}
/**
* Returns the value before the Iterator's index and moves the
* iterator back one index.
*
* @return an Object
value
* @exception NoSuchElementException if there is no previous element.
*/
public final T previous() {
if (_nextIndex == 0) {
throw new NoSuchElementException();
}
if (_nextIndex == _size) {
_lastReturned = _next = _tail;
} else {
_lastReturned = _next = (T) _next.getPrevious();
}
_nextIndex--;
return _lastReturned;
}
/**
* Returns the previous element's index.
*
* @return an int
value
*/
public final int previousIndex() {
return _nextIndex - 1;
}
/**
* Removes the current element in the list and shrinks its
* size accordingly.
*
* @exception IllegalStateException neither next nor previous
* have been invoked, or remove or add have been invoked after
* the last invocation of next or previous.
*/
public final void remove() {
if (_lastReturned == null) {
throw new IllegalStateException("must invoke next or previous before invoking remove");
}
if (_lastReturned != _next) {
_nextIndex--;
}
_next = (T) _lastReturned.getNext();
TLinkedList.this.remove(_lastReturned);
_lastReturned = null;
}
/**
* Replaces the current element in the list with
* linkable
*
* @param linkable an object of type TLinkable
*/
public final void set(T linkable) {
if (_lastReturned == null) {
throw new IllegalStateException();
}
T l = linkable;
// need to check both, since this could be the only
// element in the list.
if (_lastReturned == _head) {
_head = l;
}
if (_lastReturned == _tail) {
_tail = l;
}
swap(_lastReturned, l);
_lastReturned = l;
}
/**
* Replace from with to in the list.
*
* @param from a TLinkable
value
* @param to a TLinkable
value
*/
private void swap(T from, T to) {
T p = (T) from.getPrevious();
T n = (T) from.getNext();
if (null != p) {
to.setPrevious(p);
p.setNext(to);
}
if (null != n) {
to.setNext(n);
n.setPrevious(to);
}
from.setNext(null);
from.setPrevious(null);
}
}
} // TLinkedList