gnu.trove.list.linked.TLinkedList Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of trove4j Show documentation
Show all versions of trove4j Show documentation
The Trove library provides high speed regular and primitive
collections for Java.
///////////////////////////////////////////////////////////////////////////////
// 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;
/**
* 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:
* - the same object cannot be put into more than one list at the same time.
*
- the same object cannot be put into the same list more than once at the same time.
*
- 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.
*
- 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.
*
*
* @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> 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 ( TLinkable 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 Object[]
value
* @return an Object[]
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 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
@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 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.
*
* @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:
*
* - 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.
*
* @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 Object
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 Object
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 int
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 link = (TLinkable) 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 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 {
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 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 {
T n = current.getNext();
newElement.setPrevious( current );
newElement.setNext( n );
current.setNext( newElement );
n.setPrevious( newElement );
_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.
*/
@SuppressWarnings({"unchecked"})
public boolean forEachValue( TObjectProcedure 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 {
private int _nextIndex = 0;
private T _next;
private T _lastReturned;
/**
* Creates a new Iterator
instance positioned at
* index.
*
* @param position an int
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 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
* @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 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
* @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 int
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
* linkable
*
* @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 TLinkable
value
* @param to a TLinkable
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