///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001, Eric D. Friedman All Rights Reserved.
// Copyright (c) 2009, Rob Eden All Rights Reserved.
// Copyright (c) 2009, Jeff Randall 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.list.linked;
import gnu.trove.list.TLinkable;
import gnu.trove.procedure.TObjectProcedure;
import java.io.*;
import java.util.AbstractSequentialList;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.lang.reflect.Array;
/**
* <p>A LinkedList implementation which holds instances of type
* <tt>TLinkable</tt>.
* <p/>
* 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
* <tt>Node</tt> wrapper objects for every element in your list.
* <p/>
* The requirement to achieve this time/space gain is that the
* Objects stored in the List implement the <tt>TLinkable</tt>
* interface.
* <p/>
* The limitations are: <ul>
* <li>the same object cannot be put into more than one list at the same time.
* <li>the same object cannot be put into the same list more than once at the same time.
* <li>objects must only be removed from list they are in. 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.
* <li> 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.
* </ul>
*
* @author Eric D. Friedman
* @author Rob Eden
* @author Jeff Randall
* @version $Id: TLinkedList.java,v 1.1.2.3 2010/09/27 17:23:07 robeden Exp $
* @see gnu.trove.list.TLinkable
*/
public class TLinkedList<T extends TLinkable<T>> extends AbstractSequentialList<T>
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 <code>TLinkedList</code> instance. */
public TLinkedList() {
super();
}
/**
* Returns an iterator positioned at <tt>index</tt>. 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 <tt>index</tt> 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 <tt>size()</tt>.
*
* @param index an <code>int</code> value
* @return a <code>ListIterator</code> value
*/
public ListIterator<T> listIterator( int index ) {
return new IteratorImpl( index );
}
/**
* Returns the number of elements in the list.
*
* @return an <code>int</code> value
*/
public int size() {
return _size;
}
/**
* Inserts <tt>linkable</tt> at index <tt>index</tt> in the list.
* All values > index are shifted over one position to accommodate
* the new addition.
*
* @param index an <code>int</code> 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 <tt>linkable</tt> 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 <tt>linkable</tt> at the head of the list.
*
* @param linkable an object of type TLinkable
*/
public void addFirst( T linkable ) {
insert( 0, linkable );
}
/**
* Adds <tt>linkable</tt> 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<T> link = _head.getNext();
link != null;
link = link.getNext() ) {
TLinkable<T> 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. <b>Caveat
* programmer!</b>
*
* @return an <code>Object[]</code> 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 <b>not</b> hold
* references to one another and so are less likely to be the
* cause of memory leaks.
*
* @return an <code>Object[]</code> value
*/
public Object[] toUnlinkedArray() {
Object[] o = new Object[_size];
int i = 0;
for ( TLinkable<T> link = _head, tmp; link != null; i++ ) {
o[i] = link;
tmp = link;
link = link.getNext();
tmp.setNext( null ); // clear the links
tmp.setPrevious( null );
}
_size = 0; // clear the list
_head = _tail = null;
return o;
}
/**
* Returns a typed array of the objects in the set.
*
* @param a an <code>Object[]</code> value
* @return an <code>Object[]</code> value
*/
@SuppressWarnings({"unchecked"})
public T[] toUnlinkedArray( T[] a ) {
int size = size();
if ( a.length < size ) {
a = (T[]) Array.newInstance( a.getClass().getComponentType(), size );
}
int i = 0;
for ( T link = _head, tmp; link != null; i++ ) {
a[i] = link;
tmp = link;
link = link.getNext();
tmp.setNext( null ); // clear the links
tmp.setPrevious( null );
}
_size = 0; // clear the list
_head = _tail = null;
return a;
}
/**
* A linear search for <tt>o</tt> in the list.
*
* @param o an <code>Object</code> value
* @return a <code>boolean</code> value
*/
public boolean contains( Object o ) {
for ( TLinkable<T> link = _head; link != null; link = link.getNext() ) {
if ( o.equals( link ) ) {
return true;
}
}
return false;
}
/** {@inheritDoc} */
@Override
@SuppressWarnings({"unchecked"})
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 = node.getPrevious();
position--;
}
return node;
} else {
int position = 0;
T node = _head;
while ( position < index ) {
node = node.getNext();
position++;
}
return node;
}
}
/**
* Returns the head of the list
*
* @return an <code>Object</code> value
*/
public T getFirst() {
return _head;
}
/**
* Returns the tail of the list.
*
* @return an <code>Object</code> value
*/
public T getLast() {
return _tail;
}
/**
* Return the node following the given node. This method exists for two reasons:
* <ol>
* <li>It's really not recommended that the methods implemented by TLinkable be
* called directly since they're used internally by this class.</li>
* <li>This solves problems arising from generics when working with the linked
* objects directly.</li>
* </ol>
* <p/>
* NOTE: this should only be used with nodes contained in the list. The results are
* undefined with anything else.
*
* @param current The current node
* @return the node after the current node
*/
@SuppressWarnings({"unchecked"})
public T getNext( T current ) {
return current.getNext();
}
/**
* Return the node preceding the given node. This method exists for two reasons:
* <ol>
* <li>It's really not recommended that the methods implemented by TLinkable be
* called directly since they're used internally by this class.</li>
* <li>This solves problems arising from generics when working with the linked
* objects directly.</li>
* </ol>
* <p/>
* NOTE: this should only be used with nodes contained in the list. The results are
* undefined with anything else.
*
* @param current The current node
* @return the node after the current node
*/
@SuppressWarnings({"unchecked"})
public T getPrevious( T current ) {
return current.getPrevious();
}
/**
* Remove and return the first element in the list.
*
* @return an <code>Object</code> value
*/
@SuppressWarnings({"unchecked"})
public T removeFirst() {
T o = _head;
if ( o == null ) {
return null;
}
T n = 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 <code>Object</code> value
*/
@SuppressWarnings({"unchecked"})
public T removeLast() {
T o = _tail;
if ( o == null ) {
return null;
}
T prev = 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 <code>int</code> value
* @param linkable an object of type TLinkable
*/
@SuppressWarnings({"unchecked"})
protected void insert( int index, T linkable ) {
if ( _size == 0 ) {
_head = _tail = linkable; // first insertion
} else if ( index == 0 ) {
linkable.setNext( _head ); // insert at front
_head.setPrevious( linkable );
_head = linkable;
} else if ( index == _size ) { // insert at back
_tail.setNext( linkable );
linkable.setPrevious( _tail );
_tail = linkable;
} else {
T node = get( index );
T before = 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
*/
@SuppressWarnings({"unchecked"})
public boolean remove( Object o ) {
if ( o instanceof TLinkable ) {
T p, n;
TLinkable<T> link = (TLinkable<T>) o;
p = link.getPrevious();
n = link.getNext();
if ( n == null && p == null ) { // emptying the list
// It's possible this object is not something that's in the list. So,
// make sure it's the head if it doesn't point to anything. This solves
// problems caused by removing something multiple times.
if ( o != _head ) {
return false;
}
_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 <code>TLinkable</code> value currently in the list.
* @param newElement a <code>TLinkable</code> 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 {
T 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 <code>TLinkable</code> value currently in the list.
* @param newElement a <code>TLinkable</code> 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 {
T n = current.getNext();
newElement.setPrevious( current );
newElement.setNext( n );
current.setNext( newElement );
n.setPrevious( newElement );
_size++;
}
}
/**
* Executes <tt>procedure</tt> for each entry in the list.
*
* @param procedure a <code>TObjectProcedure</code> value
* @return false if the loop over the values terminated because
* the procedure returned false for some value.
*/
@SuppressWarnings({"unchecked"})
public boolean forEachValue( TObjectProcedure<T> procedure ) {
T node = _head;
while ( node != null ) {
boolean keep_going = procedure.execute( node );
if ( !keep_going ) {
return false;
}
node = 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( _tail );
}
@SuppressWarnings({"unchecked"})
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<T> {
private int _nextIndex = 0;
private T _next;
private T _lastReturned;
/**
* Creates a new <code>Iterator</code> instance positioned at
* <tt>index</tt>.
*
* @param position an <code>int</code> value
*/
@SuppressWarnings({"unchecked"})
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 = _next.getNext();
}
} else {
int pos = _size - 1;
for ( _next = _tail; pos > position; pos-- ) {
_next = _next.getPrevious();
}
}
}
/**
* Insert <tt>linkable</tt> 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 <code>boolean</code> value
*/
public final boolean hasNext() {
return _nextIndex != _size;
}
/**
* True if a call to previous() will return a value.
*
* @return a <code>boolean</code> value
*/
public final boolean hasPrevious() {
return _nextIndex != 0;
}
/**
* Returns the value at the Iterator's index and advances the
* iterator.
*
* @return an <code>Object</code> value
* @throws NoSuchElementException if there is no next element
*/
@SuppressWarnings({"unchecked"})
public final T next() {
if ( _nextIndex == _size ) {
throw new NoSuchElementException();
}
_lastReturned = _next;
_next = _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 <code>int</code> value
*/
public final int nextIndex() {
return _nextIndex;
}
/**
* Returns the value before the Iterator's index and moves the
* iterator back one index.
*
* @return an <code>Object</code> value
* @throws NoSuchElementException if there is no previous element.
*/
@SuppressWarnings({"unchecked"})
public final T previous() {
if ( _nextIndex == 0 ) {
throw new NoSuchElementException();
}
if ( _nextIndex == _size ) {
_lastReturned = _next = _tail;
} else {
_lastReturned = _next = _next.getPrevious();
}
_nextIndex--;
return _lastReturned;
}
/**
* Returns the previous element's index.
*
* @return an <code>int</code> value
*/
public final int previousIndex() {
return _nextIndex - 1;
}
/**
* Removes the current element in the list and shrinks its
* size accordingly.
*
* @throws IllegalStateException neither next nor previous
* have been invoked, or remove or add have been invoked after
* the last invocation of next or previous.
*/
@SuppressWarnings({"unchecked"})
public final void remove() {
if ( _lastReturned == null ) {
throw new IllegalStateException( "must invoke next or previous before invoking remove" );
}
if ( _lastReturned != _next ) {
_nextIndex--;
}
_next = _lastReturned.getNext();
TLinkedList.this.remove( _lastReturned );
_lastReturned = null;
}
/**
* Replaces the current element in the list with
* <tt>linkable</tt>
*
* @param linkable an object of type TLinkable
*/
public final void set( T linkable ) {
if ( _lastReturned == null ) {
throw new IllegalStateException();
}
swap( _lastReturned, linkable );
_lastReturned = linkable;
}
/**
* Replace from with to in the list.
*
* @param from a <code>TLinkable</code> value
* @param to a <code>TLinkable</code> value
*/
private void swap( T from, T to ) {
T from_p = from.getPrevious();
T from_n = from.getNext();
T to_p = to.getPrevious();
T to_n = to.getNext();
// NOTE: 'to' cannot be null at this point
if ( from_n == to ) {
if ( from_p != null ) from_p.setNext( to );
to.setPrevious( from_p );
to.setNext( from );
from.setPrevious( to );
from.setNext( to_n );
if ( to_n != null ) to_n.setPrevious( from );
}
// NOTE: 'from' cannot be null at this point
else if ( to_n == from ) {
if ( to_p != null ) to_p.setNext( to );
to.setPrevious( from );
to.setNext( from_n );
from.setPrevious( to_p );
from.setNext( to );
if ( from_n != null ) from_n.setPrevious( to );
}
else {
from.setNext( to_n );
from.setPrevious( to_p );
if ( to_p != null ) to_p.setNext( from );
if ( to_n != null ) to_n.setPrevious( from );
to.setNext( from_n );
to.setPrevious( from_p );
if ( from_p != null ) from_p.setNext( to );
if ( from_n != null ) from_n.setPrevious( to );
}
if ( _head == from ) _head = to;
else if ( _head == to ) _head = from;
if ( _tail == from ) _tail = to;
else if ( _tail == to ) _tail = from;
if ( _lastReturned == from ) _lastReturned = to;
else if ( _lastReturned == to ) _lastReturned = from;
if ( _next == from ) _next = to;
else if ( _next == to ) _next = from;
}
}
} // TLinkedList