git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@950 379699f6-c40d-0410-875b-85095c16579estable
parent
97ff053675
commit
610bc2106e
@ -0,0 +1,581 @@ |
||||
// This class is taken from the Classpath project.
|
||||
// Please note the different copyright holder!
|
||||
// The changes I did is this comment, the package line, some
|
||||
// imports from java.util and some minor jdk12 -> jdk11 fixes.
|
||||
// -- Jochen Hoenicke <jochen@gnu.org>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// LinkedList.java -- Linked list implementation of the List interface
|
||||
//
|
||||
// Copyright (c) 1998 by Stuart Ballard (stuart.ballard@mcmail.com)
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Library General Public License as published
|
||||
// by the Free Software Foundation, version 2. (see COPYING.LIB)
|
||||
//
|
||||
// This program 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 Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library 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 jode.util; |
||||
import java.util.NoSuchElementException; |
||||
import java.io.Serializable; |
||||
import java.io.ObjectOutputStream; |
||||
import java.io.ObjectInputStream; |
||||
import java.io.IOException; |
||||
|
||||
// TO DO:
|
||||
// ~ Doc comment for the class.
|
||||
// ~ Doc comments for the non-list methods.
|
||||
// ~ Some commenting on the Backing API and other general implementation notes.
|
||||
|
||||
/** |
||||
* Linked list implementation of the List interface. |
||||
*/ |
||||
public class LinkedList extends AbstractSequentialList |
||||
implements Serializable, Cloneable |
||||
{ |
||||
static final long serialVersionUID = 876323262645176354L; |
||||
|
||||
/** |
||||
* An Entry containing the head (in the next field) and the tail (in the |
||||
* previous field) of the list. The data field is null. If the list is empty, |
||||
* both the head and the tail point to ends itself. |
||||
*/ |
||||
transient Entry ends = new Entry(); |
||||
|
||||
/** |
||||
* The current length of the list. |
||||
*/ |
||||
transient int size = 0; |
||||
|
||||
/** |
||||
* Class to represent an entry in the list. Holds a single element. |
||||
*/ |
||||
private static class Entry { |
||||
|
||||
/** |
||||
* The list element. |
||||
*/ |
||||
Object data = null; |
||||
|
||||
/** |
||||
* The next entry in the list. If this is the last entry in the list, the |
||||
* ends field of the list is held here. |
||||
*/ |
||||
Entry next; |
||||
|
||||
/** |
||||
* The previous entry in the list. If this is the first entry in the list, |
||||
* the ends field of the list is held here. |
||||
*/ |
||||
Entry previous; |
||||
|
||||
/** |
||||
* Create an entry with given data and linkage. |
||||
*/ |
||||
Entry(Object d, Entry n, Entry p) { |
||||
data = d; |
||||
next = n; |
||||
previous = p; |
||||
} |
||||
|
||||
/** |
||||
* Create an entry with no data and linking to itself, for use as the ends |
||||
* field of the list. |
||||
*/ |
||||
Entry() { |
||||
next = previous = this; |
||||
} |
||||
|
||||
/** |
||||
* Remove this entry. |
||||
*/ |
||||
Object remove() { |
||||
previous.next = next; |
||||
next.previous = previous; |
||||
return data; |
||||
} |
||||
} |
||||
|
||||
private static interface Backing { |
||||
void checkMod(int known); |
||||
void upMod(); |
||||
void incSize(int by); |
||||
void decSize(int by); |
||||
} |
||||
|
||||
private final Backing back = new Backing() { |
||||
public void checkMod(int known) { |
||||
if (known != modCount) { |
||||
throw new ConcurrentModificationException(); |
||||
} |
||||
} |
||||
public void upMod() { |
||||
modCount++; |
||||
} |
||||
public void incSize(int by) { |
||||
size += by; |
||||
} |
||||
public void decSize(int by) { |
||||
size -= by; |
||||
} |
||||
}; |
||||
|
||||
/** A ListIterator over the list. This class keeps track of its |
||||
* position in the list, the size of the list, and the two list |
||||
* entries it is between. This enables it to be used identically |
||||
* for both the list itself and a sublist of the list. |
||||
*/ |
||||
private static class Iter implements ListIterator { |
||||
|
||||
/** |
||||
* The index of the element that will be returned by next(). |
||||
*/ |
||||
int pos; |
||||
|
||||
/** |
||||
* The size of the backing list. |
||||
*/ |
||||
int size; |
||||
|
||||
/** |
||||
* The entry containing the element that will be returned by next(). |
||||
*/ |
||||
Entry next; |
||||
|
||||
/** |
||||
* The entry containing the element that will be returned by previous(). |
||||
*/ |
||||
Entry previous; |
||||
|
||||
/** |
||||
* The entry that will be affected by remove() or set(). |
||||
*/ |
||||
Entry recent; |
||||
|
||||
/** |
||||
* The known value of the modCount of the backing list. |
||||
*/ |
||||
int knownMod; |
||||
|
||||
private final Backing b; |
||||
|
||||
/** |
||||
* Create a new Iter starting at a given Entry within the list, at a given |
||||
* position, in a list of given size. |
||||
* |
||||
* @param index the index to begin iteration. |
||||
* @exception IndexOutOfBoundsException if index < 0 || index > size. |
||||
*/ |
||||
Iter(Backing backing, Entry n, int index, int s, int mc) { |
||||
b = backing; |
||||
pos = index; |
||||
size = s; |
||||
next = n; |
||||
previous = n.previous; |
||||
knownMod = mc; |
||||
} |
||||
|
||||
public int nextIndex() { |
||||
b.checkMod(knownMod); |
||||
return pos; |
||||
} |
||||
|
||||
public int previousIndex() { |
||||
b.checkMod(knownMod); |
||||
return pos - 1; |
||||
} |
||||
|
||||
public boolean hasNext() { |
||||
b.checkMod(knownMod); |
||||
return pos < size; |
||||
} |
||||
|
||||
public boolean hasPrevious() { |
||||
b.checkMod(knownMod); |
||||
return pos > 0; |
||||
} |
||||
|
||||
public Object next() { |
||||
b.checkMod(knownMod); |
||||
if (pos >= size) { |
||||
throw new NoSuchElementException(); |
||||
} else { |
||||
pos++; |
||||
recent = previous = next; |
||||
next = recent.next; |
||||
return recent.data; |
||||
} |
||||
} |
||||
|
||||
public Object previous() { |
||||
b.checkMod(knownMod); |
||||
if (pos <= 0) { |
||||
throw new NoSuchElementException(); |
||||
} else { |
||||
pos--; |
||||
recent = next = previous; |
||||
previous = recent.previous; |
||||
return recent.data; |
||||
} |
||||
} |
||||
|
||||
public void remove() { |
||||
b.checkMod(knownMod); |
||||
if (recent == null) { |
||||
throw new IllegalStateException(); |
||||
} |
||||
|
||||
// Adjust the position to before the removed element
|
||||
if (recent == previous) pos--; |
||||
|
||||
// Could use recent.remove() but this way is quicker, and also correctly
|
||||
// fixes next and previous.
|
||||
next = recent.previous.next = recent.next; |
||||
previous = recent.next.previous = recent.previous; |
||||
size--; |
||||
b.decSize(1); |
||||
knownMod++; |
||||
b.upMod(); |
||||
recent = null; |
||||
} |
||||
|
||||
public void add(Object o) { |
||||
b.checkMod(knownMod); |
||||
previous.next = next.previous = new Entry(o, next, previous); |
||||
|
||||
// New for 1.2RC1 - the semantics changed so that the iterator is
|
||||
// positioned *after* the new element.
|
||||
previous = next; |
||||
next = previous.next; |
||||
pos++; |
||||
|
||||
size++; |
||||
b.incSize(1); |
||||
knownMod++; |
||||
b.upMod(); |
||||
recent = null; |
||||
} |
||||
|
||||
public void set(Object o) { |
||||
b.checkMod(knownMod); |
||||
if (recent == null) { |
||||
throw new IllegalStateException(); |
||||
} |
||||
recent.data = o; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Obtain the Entry at a given position in a list. This method of course |
||||
* takes linear time, but it is intelligent enough to take the shorter of the |
||||
* paths to get to the Entry required. This implies that the first or last |
||||
* entry in the list is obtained in constant time, which is a very desirable |
||||
* property. |
||||
* For speed and flexibility in which ranges are valid, range checking is not |
||||
* done in this method, and if n is outside the range -1 <= n <= size, the |
||||
* result will be wrong (but no exception will be thrown). |
||||
* Note that you *can* obtain entries at position -1 and size, which are |
||||
* equal to prehead and posttail respectively. |
||||
* This method is static so that it can also be used in subList. |
||||
* |
||||
* @param n the number of the entry to get. |
||||
* @param size the size of the list to get the entry in. |
||||
* @param head the entry before the first element of the list (usually ends). |
||||
* @param tail the entry after the last element of the list (usually ends). |
||||
*/ |
||||
static Entry getEntry(int n, int size, Entry head, Entry tail) { |
||||
|
||||
// n less than size/2, iterate from start
|
||||
if (n < size >> 1) { |
||||
while (n-- >= 0) { |
||||
head = head.next; |
||||
} |
||||
return head; |
||||
|
||||
// n greater than size/2, iterate from end
|
||||
} else { |
||||
while (++n <= size) { |
||||
tail = tail.previous; |
||||
} |
||||
return tail; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Create an empty linked list. |
||||
*/ |
||||
public LinkedList() { |
||||
super(); |
||||
} |
||||
|
||||
/** |
||||
* Create a linked list containing the elements, in order, of a given |
||||
* collection. |
||||
* |
||||
* @param c the collection to populate this list from. |
||||
*/ |
||||
public LinkedList(Collection c) { |
||||
super(); |
||||
// Note: addAll could be made slightly faster, but not enough so to justify
|
||||
// re-implementing it from scratch. It is just a matter of a relatively
|
||||
// small constant factor.
|
||||
addAll(c); |
||||
} |
||||
|
||||
public Object getFirst() { |
||||
if (size == 0) { |
||||
throw new NoSuchElementException(); |
||||
} |
||||
return ends.next.data; |
||||
} |
||||
|
||||
public Object getLast() { |
||||
if (size == 0) { |
||||
throw new NoSuchElementException(); |
||||
} |
||||
return ends.previous.data; |
||||
} |
||||
|
||||
public Object removeFirst() { |
||||
if (size == 0) { |
||||
throw new NoSuchElementException(); |
||||
} |
||||
size--; |
||||
modCount++; |
||||
return ends.next.remove(); |
||||
} |
||||
|
||||
public Object removeLast() { |
||||
if (size == 0) { |
||||
throw new NoSuchElementException(); |
||||
} |
||||
size--; |
||||
modCount++; |
||||
return ends.previous.remove(); |
||||
} |
||||
|
||||
public void addFirst(Object o) { |
||||
ends.next.previous = ends.next = new Entry(o, ends.next, ends); |
||||
size++; |
||||
modCount++; |
||||
} |
||||
|
||||
public void addLast(Object o) { |
||||
ends.previous.next = ends.previous = new Entry(o, ends, ends.previous); |
||||
size++; |
||||
modCount++; |
||||
} |
||||
|
||||
/** |
||||
* Obtain the number of elements currently in this list. |
||||
* |
||||
* @returns the number of elements currently in this list. |
||||
*/ |
||||
public int size() { |
||||
return size; |
||||
} |
||||
|
||||
/** |
||||
* Remove a range of elements from this list. |
||||
* |
||||
* @param fromIndex the index, inclusive, to remove from. |
||||
* @param toIndex the index, exclusive, to remove to. |
||||
* @exception IndexOutOfBoundsException if fromIndex > toIndex || fromIndex < |
||||
* 0 || toIndex > size(). |
||||
*/ |
||||
// Note: normally removeRange is provided to allow efficient ways to
|
||||
// implement clear() on subLists. However, in this case clear on subLists
|
||||
// works anyway, so this implementation is included just for completeness
|
||||
// and because subclasses might try to use it.
|
||||
protected void removeRange(int fromIndex, int toIndex) { |
||||
subList(fromIndex, toIndex).clear(); |
||||
} |
||||
|
||||
/** |
||||
* Clear the list. |
||||
*/ |
||||
public void clear() { |
||||
ends.next = ends.previous = ends; |
||||
modCount++; |
||||
size = 0; |
||||
} |
||||
|
||||
/** |
||||
* Obtain a ListIterator over this list, starting at a given index. The |
||||
* ListIterator returned by this method supports the add, remove and set |
||||
* methods. |
||||
* |
||||
* @param index the index of the element to be returned by the first call to |
||||
* next(), or size() to be initially positioned at the end of the list. |
||||
* @exception IndexOutOfBoundsException if index < 0 || index > size(). |
||||
*/ |
||||
public ListIterator listIterator(int index) { |
||||
|
||||
// Check bounds
|
||||
if (index < 0 || index > size) { |
||||
throw new IndexOutOfBoundsException(); |
||||
} |
||||
|
||||
return new Iter(back, getEntry(index, size, ends, ends), |
||||
index, size, modCount); |
||||
} |
||||
|
||||
/** |
||||
* Obtain a List view of a subsection of this list, from fromIndex |
||||
* (inclusive) to toIndex (exclusive). The returned list is modifiable in |
||||
* every respect. Changes to the returned list are reflected in this list. If |
||||
* this list is structurally modified is any way other than through the |
||||
* returned list, any subsequent operations on the returned list will result |
||||
* in a ConcurrentModificationException (that is, the returned list is |
||||
* fail-fast). |
||||
* |
||||
* @param fromIndex the index that the returned list should start from |
||||
* (inclusive). |
||||
* @param toIndex the index that the returned list should go to (exclusive). |
||||
* @returns a List backed by a subsection of this list. |
||||
* @exception IndexOutOfBoundsException if fromIndex < 0 || toIndex > size() |
||||
* || fromIndex > toIndex. |
||||
*/ |
||||
public List subList(int fromIndex, int toIndex) { |
||||
|
||||
// Check bounds
|
||||
if (fromIndex > toIndex || fromIndex < 0 || toIndex > size) { |
||||
throw new IndexOutOfBoundsException(); |
||||
} |
||||
|
||||
return new SubLinkedList(back, getEntry(fromIndex - 1, size, ends, ends), |
||||
getEntry(toIndex, size, ends, ends), |
||||
toIndex - fromIndex); |
||||
} |
||||
|
||||
private static class SubLinkedList extends AbstractSequentialList { |
||||
|
||||
Entry head; // entry before the beginning
|
||||
Entry tail; // entry after the end
|
||||
int size; |
||||
private final Backing b; |
||||
|
||||
private final Backing back = new Backing() { |
||||
public void checkMod(int known) { |
||||
if (known != modCount) { |
||||
throw new ConcurrentModificationException(); |
||||
} |
||||
} |
||||
public void upMod() { |
||||
modCount++; |
||||
} |
||||
public void incSize(int by) { |
||||
size += by; |
||||
} |
||||
public void decSize(int by) { |
||||
size -= by; |
||||
} |
||||
}; |
||||
|
||||
SubLinkedList(Backing backing, Entry h, Entry t, int s) { |
||||
b = backing; |
||||
head = h; |
||||
tail = t; |
||||
size = s; |
||||
} |
||||
|
||||
public int size() { |
||||
b.checkMod(this.modCount); |
||||
return size; |
||||
} |
||||
|
||||
public ListIterator listIterator(int index) { |
||||
b.checkMod(this.modCount); |
||||
|
||||
// Check bounds
|
||||
if (index < 0 || index > size) { |
||||
throw new IndexOutOfBoundsException(); |
||||
} |
||||
|
||||
return new Iter(back, getEntry(index, size, head, tail), |
||||
index, size, modCount); |
||||
} |
||||
|
||||
public void clear() { |
||||
b.checkMod(this.modCount); |
||||
head.next = tail; |
||||
tail.previous = head; |
||||
size = 0; |
||||
b.decSize(size); |
||||
modCount++; |
||||
b.upMod(); |
||||
} |
||||
|
||||
// No removeRange because this class cannot be publically subclassed.
|
||||
|
||||
public List subList(int fromIndex, int toIndex) { |
||||
b.checkMod(this.modCount); |
||||
|
||||
// Check bounds
|
||||
if (fromIndex > toIndex || fromIndex < 0 || toIndex > size) { |
||||
throw new IndexOutOfBoundsException(); |
||||
} |
||||
|
||||
return new SubLinkedList(back, getEntry(fromIndex - 1, size, head, tail), |
||||
getEntry(toIndex, size, head, tail), |
||||
toIndex - fromIndex); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Create a shallow copy of this LinkedList. |
||||
* @return an object of the same class as this object, containing the |
||||
* same elements in the same order. |
||||
*/ |
||||
public Object clone() |
||||
{ |
||||
LinkedList copy; |
||||
try |
||||
{ |
||||
copy = (LinkedList) super.clone(); |
||||
} |
||||
catch (CloneNotSupportedException ex) |
||||
{ |
||||
throw new InternalError(ex.getMessage()); |
||||
} |
||||
copy.size = 0; |
||||
copy.ends = new Entry(); |
||||
copy.addAll(this); |
||||
return copy; |
||||
} |
||||
|
||||
/** |
||||
* Serialize an object to a stream. |
||||
* @serialdata the size of the list (int), followed by all the elements |
||||
* (Object) in proper order. |
||||
*/ |
||||
private void writeObject(ObjectOutputStream s) |
||||
throws IOException |
||||
{ |
||||
s.writeInt(size); |
||||
for (Iterator i = iterator(); i.hasNext(); ) |
||||
s.writeObject(i.next()); |
||||
} |
||||
|
||||
/** |
||||
* Deserialize an object from a stream. |
||||
* @serialdata the size of the list (int), followed by all the elements |
||||
* (Object) in proper order. |
||||
*/ |
||||
private void readObject(ObjectInputStream s) |
||||
throws IOException, ClassNotFoundException |
||||
{ |
||||
int serialSize = s.readInt(); |
||||
ends = new Entry(); |
||||
for (int i=0; i< serialSize; i++) |
||||
addLast(s.readObject()); |
||||
} |
||||
} |
Loading…
Reference in new issue