/* * Javolution - Java(TM) Solution for Real-Time and Embedded Systems * Copyright (C) 2005 - Javolution (http://javolution.org/) * All rights reserved. * * Permission to use, copy, modify, and distribute this software is * freely granted, provided that this notice is preserved. */ package javolution.util; import java.io.IOException; import java.util.NoSuchElementException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.IllegalStateException; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import javax.realtime.MemoryArea; import javolution.context.ObjectFactory; import javolution.context.PersistentContext; import javolution.lang.Reusable; /** * <p> This class represents a linked list with real-time behavior; * smooth capacity increase and no memory allocation as long as the * list size does not exceed its initial capacity.</p> * * <p> All of the operations perform as could be expected for a doubly-linked * list ({@link #addLast insertion}/{@link #removeLast() deletion} * at the end of the list are nonetheless the fastest). * Operations that index into the list will traverse the list from * the begining or the end whichever is closer to the specified index. * Random access operations can be significantly accelerated by * {@link #subList splitting} the list into smaller ones.</p> * * <p> {@link FastList} (as for any {@link FastCollection} sub-class) supports * fast iterations without using iterators.[code] * FastList<String> list = new FastList<String>(); * for (FastList.Node<String> n = list.head(), end = list.tail(); (n = n.getNext()) != end;) { * String value = n.getValue(); // No typecast necessary. * }[/code]</p> * * <p> {@link FastList} are {@link Reusable reusable}, they maintain * internal pools of {@link Node nodes} objects. When a node is removed * from its list, it is automatically restored to its pool.</p> * * <p> Custom list implementations may override the {@link #newNode} method * in order to return their own {@link Node} implementation (with * additional fields for example).</p> * * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> * @version 4.2, December 18, 2006 */ public class FastList <E> extends FastCollection <E> implements List <E> , Reusable { /** * Holds the main list factory. */ private static final ObjectFactory FACTORY = new ObjectFactory() { public Object create() { return new FastList(); } }; /** * Holds the node marking the beginning of the list (not included). */ private transient Node <E> _head = newNode(); /** * Holds the node marking the end of the list (not included). */ private transient Node <E> _tail = newNode(); /** * Holds the value comparator. */ private transient FastComparator <? super E> _valueComparator = FastComparator.DEFAULT; /** * Holds the current size. */ private transient int _size; /** * Creates a list of small initial capacity. */ public FastList() { this(4); } /** * Creates a persistent list associated to the specified unique identifier * (convenience method). * * @param id the unique identifier for this map. * @throws IllegalArgumentException if the identifier is not unique. * @see javolution.context.PersistentContext.Reference */ public FastList(String id) { this(); new PersistentContext.Reference(id, this) { protected void notifyChange() { FastList.this.clear(); FastList.this.addAll((FastList) this.get()); } }; } /** * Creates a list of specified initial capacity; unless the list size * reaches the specified capacity, operations on this list will not allocate * memory (no lazy object creation). * * @param capacity the initial capacity. */ public FastList(int capacity) { _head._next = _tail; _tail._previous = _head; Node <E> previous = _tail; for (int i = 0; i++ < capacity;) { Node <E> newNode = newNode(); newNode._previous = previous; previous._next = newNode; previous = newNode; } } /** * Creates a list containing the specified values, in the order they * are returned by the collection's iterator. * * @param values the values to be placed into this list. */ public FastList(Collection <? extends E> values) { this(values.size()); addAll(values); } /** * Returns a new, preallocated or {@link #recycle recycled} list instance * (on the stack when executing in a {@link javolution.context.StackContext * StackContext}). * * @return a new, preallocated or recycled list instance. */ public static <E> FastList <E> newInstance() { return (FastList <E> ) FACTORY.object(); } /** * Recycles a list {@link #newInstance() instance} immediately * (on the stack when executing in a {@link javolution.context.StackContext * StackContext}). */ public static void recycle(FastList instance) { FACTORY.recycle(instance); } /** * Appends the specified value to the end of this list * (equivalent to {@link #addLast}). * * @param value the value to be appended to this list. * @return <code>true</code> (as per the general contract of the * <code>Collection.add</code> method). */ public final boolean add( E value) { addLast(value); return true; } /** * Returns the value at the specified position in this list. * * @param index the index of value to return. * @return the value at the specified position in this list. * @throws IndexOutOfBoundsException if <code>(index < 0) || * (index >= size())</code> */ public final E get(int index) { if ((index < 0) || (index >= _size)) throw new IndexOutOfBoundsException("index: " + index); return nodeAt(index)._value; } /** * Replaces the value at the specified position in this list with the * specified value. * * @param index the index of value to replace. * @param value the value to be stored at the specified position. * @return the value previously at the specified position. * @throws IndexOutOfBoundsException if <code>(index < 0) || * (index >= size())</code> */ public final E set(int index, E value) { if ((index < 0) || (index >= _size)) throw new IndexOutOfBoundsException("index: " + index); final Node <E> node = nodeAt(index); E previousValue = node._value; node._value = value; return previousValue; } /** * Inserts the specified value at the specified position in this list. * Shifts the value currently at that position * (if any) and any subsequent values to the right (adds one to their * indices). * * @param index the index at which the specified value is to be inserted. * @param value the value to be inserted. * @throws IndexOutOfBoundsException if <code>(index < 0) || * (index > size())</code> */ public final void add(int index, E value) { if ((index < 0) || (index > _size)) throw new IndexOutOfBoundsException("index: " + index); addBefore(nodeAt(index), value); } /** * Inserts all of the values in the specified collection into this * list at the specified position. Shifts the value currently at that * position (if any) and any subsequent values to the right * (increases their indices). * * @param index the index at which to insert first value from the specified * collection. * @param values the values to be inserted into this list. * @return <code>true</code> if this list changed as a result of the call; * <code>false</code> otherwise. * @throws IndexOutOfBoundsException if <code>(index < 0) || * (index > size())</code> */ public final boolean addAll(int index, Collection <? extends E> values) { if ((index < 0) || (index > _size)) throw new IndexOutOfBoundsException("index: " + index); final Node indexNode = nodeAt(index); Iterator <? extends E> i = values.iterator(); while (i.hasNext()) { addBefore(indexNode, i.next()); } return values.size() != 0; } /** * Removes the value at the specified position in this list. * Shifts any subsequent values to the left (subtracts one * from their indices). Returns the value that was removed from the * list. * * @param index the index of the value to removed. * @return the value previously at the specified position. * @throws IndexOutOfBoundsException if <code>(index < 0) || * (index >= size())</code> */ public final E remove(int index) { if ((index < 0) || (index >= _size)) throw new IndexOutOfBoundsException("index: " + index); final Node <E> node = nodeAt(index); E previousValue = node._value; delete(node); return previousValue; } /** * Returns the index in this list of the first occurrence of the specified * value, or -1 if this list does not contain this value. * * @param value the value to search for. * @return the index in this list of the first occurrence of the specified * value, or -1 if this list does not contain this value. */ public final int indexOf(Object value) { final FastComparator comp = this.getValueComparator(); int index = 0; for (Node n = _head, end = _tail; (n = n._next) != end; index++) { if (comp == FastComparator.DEFAULT ? defaultEquals(value, n._value) : comp.areEqual(value, n._value)) return index; } return -1; } /** * Returns the index in this list of the last occurrence of the specified * value, or -1 if this list does not contain this value. * * @param value the value to search for. * @return the index in this list of the last occurrence of the specified * value, or -1 if this list does not contain this value. */ public final int lastIndexOf(Object value) { final FastComparator comp = this.getValueComparator(); int index = size() - 1; for (Node n = _tail, end = _head; (n = n._previous) != end; index--) { if (comp == FastComparator.DEFAULT ? defaultEquals(value, n._value) : comp.areEqual(value, n._value)) return index; } return -1; } /** * Returns a simple iterator over the elements in this list * (allocated on the stack when executed in a * {@link javolution.context.StackContext StackContext}). * * @return an iterator over this list values. */ public Iterator <E> iterator() { return listIterator(); } /** * Returns a list iterator over the elements in this list * (allocated on the stack when executed in a * {@link javolution.context.StackContext StackContext}). * * @return an iterator over this list values. */ public ListIterator <E> listIterator() { return FastListIterator.valueOf(this, _head._next, 0, _size); } /** * Returns a list iterator from the specified position * (allocated on the stack when executed in a * {@link javolution.context.StackContext StackContext}). * * The specified index indicates the first value that would be returned by * an initial call to the <code>next</code> method. An initial call to * the <code>previous</code> method would return the value with the * specified index minus one. * * @param index index of first value to be returned from the * list iterator (by a call to the <code>next</code> method). * @return a list iterator over the values in this list * starting at the specified position in this list. * @throws IndexOutOfBoundsException if the index is out of range * [code](index < 0 || index > size())[/code]. */ public ListIterator <E> listIterator(int index) { if ((index < 0) || (index > _size)) throw new IndexOutOfBoundsException("index: " + index); return FastListIterator.valueOf(this, nodeAt(index), index, _size); } /** * Returns a view of the portion of this list between the specified * indexes (allocated from the "stack" when executing in a * {@link javolution.context.StackContext StackContext}). * If the specified indexes are equal, the returned list is empty. * The returned list is backed by this list, so non-structural changes in * the returned list are reflected in this list, and vice-versa. * * This method eliminates the need for explicit range operations (of * the sort that commonly exist for arrays). Any operation that expects * a list can be used as a range operation by passing a subList view * instead of a whole list. For example, the following idiom * removes a range of values from a list: * <code>list.subList(from, to).clear();</code> * Similar idioms may be constructed for <code>indexOf</code> and * <code>lastIndexOf</code>, and all of the algorithms in the * <code>Collections</code> class can be applied to a subList. * * The semantics of the list returned by this method become undefined if * the backing list (i.e., this list) is <i>structurally modified</i> in * any way other than via the returned list (structural modifications are * those that change the size of this list, or otherwise perturb it in such * a fashion that iterations in progress may yield incorrect results). * * @param fromIndex low endpoint (inclusive) of the subList. * @param toIndex high endpoint (exclusive) of the subList. * @return a view of the specified range within this list. * * @throws IndexOutOfBoundsException if [code](fromIndex < 0 || * toIndex > size || fromIndex < toIndex)[/code] */ public final List <E> subList(int fromIndex, int toIndex) { if ((fromIndex < 0) || (toIndex > _size) || (fromIndex > toIndex)) throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + ", toIndex: " + toIndex + " for list of size: " + _size); return SubList.valueOf(this, nodeAt(fromIndex)._previous, nodeAt(toIndex), toIndex - fromIndex); } /** * Returns the first value of this list. * * @return this list's first value. * @throws NoSuchElementException if this list is empty. */ public final E getFirst() { final Node <E> node = _head._next; if (node == _tail) throw new NoSuchElementException(); return node._value; } /** * Returns the last value of this list. * * @return this list's last value. * @throws NoSuchElementException if this list is empty. */ public final E getLast() { final Node <E> node = _tail._previous; if (node == _head) throw new NoSuchElementException(); return node._value; } /** * Inserts the specified value at the beginning of this list. * * @param value the value to be inserted. */ public final void addFirst( E value) { addBefore(_head._next, value); } /** * Appends the specified value to the end of this list <i>(fast)</i>. * * @param value the value to be inserted. */ public void addLast( E value) { // Optimized. if (_tail._next == null) { increaseCapacity(); } _tail._value = value; _tail = _tail._next; _size++; } /** * Removes and returns the first value of this list. * * @return this list's first value before this call. * @throws NoSuchElementException if this list is empty. */ public final E removeFirst() { final Node <E> first = _head._next; if (first == _tail) throw new NoSuchElementException(); E previousValue = first._value; delete(first); return previousValue; } /** * Removes and returns the last value of this list <i>(fast)</i>. * * @return this list's last value before this call. * @throws NoSuchElementException if this list is empty. */ public final E removeLast() { if (_size == 0) throw new NoSuchElementException(); _size--; final Node <E> last = _tail._previous; final E previousValue = last._value; _tail = last; last._value = null; return previousValue; } /////////////////////// // Nodes operations. // /////////////////////// /** * Inserts the specified value before the specified Node. * * @param next the Node before which this value is inserted. * @param value the value to be inserted. */ public final void addBefore(Node <E> next, E value) { if (_tail._next == null) { increaseCapacity();// Increases capacity. } final Node newNode = _tail._next; // Detaches newNode. final Node tailNext = _tail._next = newNode._next; if (tailNext != null) { tailNext._previous = _tail; } // Inserts before next. final Node previous = next._previous; previous._next = newNode; next._previous = newNode; newNode._next = next; newNode._previous = previous; newNode._value = value; _size++; } /** * Returns the node at the specified index. This method returns * the {@link #headNode} node when [code]index < 0 [/code] or * the {@link #tailNode} node when [code]index >= size()[/code]. * * @param index the index of the Node to return. */ private final Node <E> nodeAt(int index) { // We cannot do backward search because of thread-safety. Node <E> node = _head; for (int i = index; i-- >= 0;) { node = node._next; } return node; } // Implements FastCollection abstract method. public final Node<E>head() { return _head; } // Implements FastCollection abstract method. public final Node<E>tail() { return _tail; } // Implements FastCollection abstract method. public final E valueOf(Record record) { return ((Node <E> ) record)._value; } // Implements FastCollection abstract method. public final void delete(Record record) { Node <E> node = (Node <E> ) record; _size--; node._value = null; // Detaches. node._previous._next = node._next; node._next._previous = node._previous; // Inserts after _tail. final Node <E> next = _tail._next; node._previous = _tail; node._next = next; _tail._next = node; if (next != null) { next._previous = node; } } // Overrides (optimization). public final boolean contains(Object value) { return indexOf(value) >= 0; } /////////////////////// // Contract Methods. // /////////////////////// // Implements abstract method. public final int size() { return _size; } // Overrides (optimization). public final void clear() { _size = 0; for (Node <E> n = _head, end = _tail; (n = n._next) != end;) { n._value = null; } _tail = _head._next; } /** * Sets the comparator to use for value equality. * * @param comparator the value comparator. * @return <code>this</code> */ public FastList <E> setValueComparator( FastComparator <? super E> comparator) { _valueComparator = comparator; return this; } // Overrides. public FastComparator <? super E> getValueComparator() { return _valueComparator; } // Overrides to return a list (JDK1.5+). public List<E> unmodifiable() { return ( List<E> ) super.unmodifiable(); } /** * Returns a new node for this list; this method can be overriden by * custom list implementation. * * @return a new node. */ protected Node <E> newNode() { return new Node(); } // Requires special handling during de-serialization process. private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { // Initial setup. _head = new Node(); _tail = new Node(); _head._next = _tail; _tail._previous = _head; setValueComparator((FastComparator) stream.readObject()); final int size = stream.readInt(); for (int i = size; i-- != 0;) { addLast(( E ) stream.readObject()); } } // Requires special handling during serialization process. private void writeObject(ObjectOutputStream stream) throws IOException { stream.writeObject(getValueComparator()); stream.writeInt(_size); Node node = _head; for (int i = _size; i-- != 0;) { node = node._next; stream.writeObject(node._value); } } // Increases capacity (_tail._next == null) private void increaseCapacity() { MemoryArea.getMemoryArea(this).executeInArea(new Runnable() { public void run() { Node <E> newNode0 = newNode(); _tail._next = newNode0; newNode0._previous = _tail; Node <E> newNode1 = newNode(); newNode0._next = newNode1; newNode1._previous = newNode0; Node <E> newNode2 = newNode(); newNode1._next = newNode2; newNode2._previous = newNode1; Node <E> newNode3 = newNode(); newNode2._next = newNode3; newNode3._previous = newNode2; } }); } // Implements Reusable interface. public void reset() { clear(); this.setValueComparator(FastComparator.DEFAULT); } /** * This class represents a {@link FastList} node; it allows for direct * iteration over the list {@link #getValue values}. * Custom {@link FastList} may use a derived implementation. * For example:[code] * static class MyList<E,X> extends FastList<E> { * protected MyNode newNode() { * return new MyNode(); * } * class MyNode extends Node<E> { * X xxx; // Additional node field (e.g. cross references). * } * }[/code] */ public static class Node <E> implements Record, Serializable { /** * Holds the next node. */ private Node <E> _next; /** * Holds the previous node. */ private Node <E> _previous; /** * Holds the node value. */ private E _value; /** * Default constructor. */ protected Node() { } /** * Returns the value for this node. * * @return the node value. */ public final E getValue() { return _value; } // Implements Record interface. public final Node<E>getNext() { return _next; } // Implements Record interface. public final Node<E>getPrevious() { return _previous; } } /** * This inner class implements a sub-list. */ private static final class SubList extends FastCollection implements List, Serializable { private static final ObjectFactory FACTORY = new ObjectFactory() { protected Object create() { return new SubList(); } protected void cleanup(Object obj) { SubList sl = (SubList) obj; sl._list = null; sl._head = null; sl._tail = null; } }; private FastList _list; private Node _head; private Node _tail; private int _size; public static SubList valueOf(FastList list, Node head, Node tail, int size) { SubList subList = (SubList) FACTORY.object(); subList._list = list; subList._head = head; subList._tail = tail; subList._size = size; return subList; } public int size() { return _size; } public Record head() { return _head; } public Record tail() { return _tail; } public Object valueOf(Record record) { return _list.valueOf(record); } public void delete(Record record) { _list.delete(record); } public boolean addAll(int index, Collection values) { if ((index < 0) || (index > _size)) throw new IndexOutOfBoundsException("index: " + index); final Node indexNode = nodeAt(index); Iterator i = values.iterator(); while (i.hasNext()) { _list.addBefore(indexNode, i.next()); } return values.size() != 0; } public Object get(int index) { if ((index < 0) || (index >= _size)) throw new IndexOutOfBoundsException("index: " + index); return nodeAt(index)._value; } public Object set(int index, Object value) { if ((index < 0) || (index >= _size)) throw new IndexOutOfBoundsException("index: " + index); final Node node = nodeAt(index); Object previousValue = node._value; node._value = value; return previousValue; } public void add(int index, Object element) { if ((index < 0) || (index > _size)) throw new IndexOutOfBoundsException("index: " + index); _list.addBefore(nodeAt(index), element); } public Object remove(int index) { if ((index < 0) || (index >= _size)) throw new IndexOutOfBoundsException("index: " + index); final Node node = nodeAt(index); Object previousValue = node._value; _list.delete(node); return previousValue; } public int indexOf(Object value) { final FastComparator comp = _list.getValueComparator(); int index = 0; for (Node n = _head, end = _tail; (n = n._next) != end; index++) { if (comp.areEqual(value, n._value)) return index; } return -1; } public int lastIndexOf(Object value) { final FastComparator comp = this.getValueComparator(); int index = size() - 1; for (Node n = _tail, end = _head; (n = n._previous) != end; index--) { if (comp.areEqual(value, n._value)) { return index; } } return -1; } public ListIterator listIterator() { return listIterator(0); } public ListIterator listIterator(int index) { if ((index >= 0) && (index <= _size)) { return FastListIterator.valueOf(_list, nodeAt(index), index, _size); } else { throw new IndexOutOfBoundsException("index: " + index + " for list of size: " + _size); } } public List subList(int fromIndex, int toIndex) { if ((fromIndex < 0) || (toIndex > _size) || (fromIndex > toIndex)) throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + ", toIndex: " + toIndex + " for list of size: " + _size); SubList subList = SubList.valueOf(_list, nodeAt(fromIndex)._previous, nodeAt(toIndex), toIndex - fromIndex); return subList; } private final Node nodeAt(int index) { if (index <= (_size >> 1)) { // Forward search. Node node = _head; for (int i = index; i-- >= 0;) { node = node._next; } return node; } else { // Backward search. Node node = _tail; for (int i = _size - index; i-- > 0;) { node = node._previous; } return node; } } } /** * This inner class implements a fast list iterator. */ private static final class FastListIterator implements ListIterator { private static final ObjectFactory FACTORY = new ObjectFactory() { protected Object create() { return new FastListIterator(); } protected void cleanup(Object obj) { FastListIterator i = (FastListIterator) obj; i._list = null; i._currentNode = null; i._nextNode = null; } }; private FastList _list; private Node _nextNode; private Node _currentNode; private int _length; private int _nextIndex; public static FastListIterator valueOf(FastList list, Node nextNode, int nextIndex, int size) { FastListIterator itr = (FastListIterator) FACTORY.object(); itr._list = list; itr._nextNode = nextNode; itr._nextIndex = nextIndex; itr._length = size; return itr; } public boolean hasNext() { return (_nextIndex != _length); } public Object next() { if (_nextIndex == _length) throw new NoSuchElementException(); _nextIndex++; _currentNode = _nextNode; _nextNode = _nextNode._next; return _currentNode._value; } public int nextIndex() { return _nextIndex; } public boolean hasPrevious() { return _nextIndex != 0; } public Object previous() { if (_nextIndex == 0) throw new NoSuchElementException(); _nextIndex--; _currentNode = _nextNode = _nextNode._previous; return _currentNode._value; } public int previousIndex() { return _nextIndex - 1; } public void add(Object o) { _list.addBefore(_nextNode, o); _currentNode = null; _length++; _nextIndex++; } public void set(Object o) { if (_currentNode == null) throw new IllegalStateException(); _currentNode._value = o; } public void remove() { if (_currentNode == null) throw new IllegalStateException(); if (_nextNode == _currentNode) { // previous() has been called. _nextNode = _nextNode._next; } else { _nextIndex--; } _list.delete(_currentNode); _currentNode = null; _length--; } } // For inlining of default comparator. private static boolean defaultEquals(Object o1, Object o2) { return (o1 == null) ? (o2 == null) : (o1 == o2) || o1.equals(o2); } private static final long serialVersionUID = 1L; }