import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.util.NoSuchElementException;
/*
* Copyright 2005 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This is a simple linked linked implementation. Each node must implement
* </code>LinkedListNode<code> so that it references the node before and after
* it. This way a node can be removed without having to scan the list to find
* it. This class does not provide an Iterator implementation as its designed
* for efficiency and not genericity. There are a number of ways to iterate the
* list.
*
* Simple iterator:
*
* <pre>
* for (LinkedListNode node = list.getFirst(); node != null; node = node.getNext()) {
* }
* </pre>
*
* Iterator that pops the first entry:
*
* <pre>
* for (LinkedListNode node = list.removeFirst(); node != null; node = list.removeFirst()) {
* }
* </pre>
*
*
* @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a>
* @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
*
*/
public class LinkedList implements Externalizable {
private static final long serialVersionUID = 400L;
private LinkedListNode firstNode;
private LinkedListNode lastNode;
private int size;
private LinkedListIterator iterator;
/**
* Construct an empty <code>LinkedList</code>
*/
public LinkedList() {
this.iterator = new LinkedListIterator();
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
firstNode = (LinkedListNode) in.readObject();
lastNode = (LinkedListNode) in.readObject();
size = in.readInt();
iterator = (LinkedListIterator) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(firstNode);
out.writeObject(lastNode);
out.writeInt(size);
out.writeObject(iterator);
}
/**
* Add a <code>LinkedListNode</code> to the list. If the
* <code>LinkedList</code> is empty then the first and last nodes are set to
* the added node.
*
* @param node
* The <code>LinkedListNode</code> to be added
*/
public void add(final LinkedListNode node) {
if (this.firstNode == null) {
this.firstNode = node;
this.lastNode = node;
} else {
this.lastNode.setNext(node);
node.setPrevious(this.lastNode);
this.lastNode = node;
}
this.size++;
}
/**
* Removes a <code>LinkedListNode</code> from the list. This works by attach
* the previous reference to the child reference. When the node to be removed
* is the first node it calls <code>removeFirst()</code>. When the node to
* be removed is the last node it calls <code>removeLast()</code>.
*
* @param node
* The <code>LinkedListNode</code> to be removed.
*/
public void remove(final LinkedListNode node) {
if (this.firstNode == node) {
removeFirst();
} else if (this.lastNode == node) {
removeLast();
} else {
node.getPrevious().setNext(node.getNext());
(node.getNext()).setPrevious(node.getPrevious());
this.size--;
node.setPrevious(null);
node.setNext(null);
}
}
/**
* Return the first node in the list
*
* @return The first <code>LinkedListNode</code>.
*/
public final LinkedListNode getFirst() {
return this.firstNode;
}
/**
* Return the last node in the list
*
* @return The last <code>LinkedListNode</code>.
*/
public final LinkedListNode getLast() {
return this.lastNode;
}
/**
* Remove the first node from the list. The next node then becomes the first
* node. If this is the last node then both first and last node references are
* set to null.
*
* @return The first <code>LinkedListNode</code>.
*/
public LinkedListNode removeFirst() {
if (this.firstNode == null) {
return null;
}
final LinkedListNode node = this.firstNode;
this.firstNode = node.getNext();
node.setNext(null);
if (this.firstNode != null) {
this.firstNode.setPrevious(null);
} else {
this.lastNode = null;
}
this.size--;
return node;
}
public void insertAfter(final LinkedListNode existingNode, final LinkedListNode newNode) {
if (newNode.getPrevious() != null || newNode.getNext() != null) {
// do nothing if this node is already inserted somewhere
return;
}
if (existingNode == null) {
if (this.isEmpty()) {
this.firstNode = newNode;
this.lastNode = newNode;
} else {
// if existing node is null, then insert it as a first node
final LinkedListNode node = this.firstNode;
node.setPrevious(newNode);
newNode.setNext(node);
this.firstNode = newNode;
}
} else if (existingNode == this.lastNode) {
existingNode.setNext(newNode);
newNode.setPrevious(existingNode);
this.lastNode = newNode;
} else {
(existingNode.getNext()).setPrevious(newNode);
newNode.setNext(existingNode.getNext());
existingNode.setNext(newNode);
newNode.setPrevious(existingNode);
}
this.size++;
}
/**
* Remove the last node from the list. The previous node then becomes the last
* node. If this is the last node then both first and last node references are
* set to null.
*
* @return The first <code>LinkedListNode</code>.
*/
public LinkedListNode removeLast() {
if (this.lastNode == null) {
return null;
}
final LinkedListNode node = this.lastNode;
this.lastNode = node.getPrevious();
node.setPrevious(null);
if (this.lastNode != null) {
this.lastNode.setNext(null);
} else {
this.firstNode = this.lastNode;
}
this.size--;
return node;
}
/**
* @return boolean value indicating the empty status of the list
*/
public final boolean isEmpty() {
return (this.firstNode == null);
}
/**
* Iterates the list removing all the nodes until there are no more nodes to
* remove.
*/
public void clear() {
while (removeFirst() != null) {
}
}
/**
* @return return size of the list as an int
*/
public final int size() {
return this.size;
}
public int hashCode() {
final int PRIME = 31;
int result = 1;
for (LinkedListNode node = this.firstNode; node != null; node = node.getNext()) {
result = PRIME * result + node.hashCode();
}
return result;
}
public boolean equals(final Object object) {
if (object == this) {
return true;
}
if (object == null || !(object instanceof LinkedList)) {
return false;
}
final LinkedList other = (LinkedList) object;
if (this.size() != other.size()) {
return false;
}
for (LinkedListNode thisNode = this.firstNode, otherNode = other.firstNode; thisNode != null
&& otherNode != null; thisNode = thisNode.getNext(), otherNode = otherNode.getNext()) {
if (!thisNode.equals(otherNode)) {
return false;
}
}
return true;
}
public Iterator iterator() {
this.iterator.reset(this);
return this.iterator;
}
public java.util.Iterator javaUtilIterator() {
return new JavaUtilIterator(this);
}
/**
* Returns a list iterator
*
* @return
*/
public static class LinkedListIterator implements Iterator, Externalizable {
private LinkedList list;
private LinkedListNode current;
public void reset(final LinkedList list) {
this.list = list;
this.current = this.list.firstNode;
}
public Object next() {
if (this.current == null) {
return null;
}
final LinkedListNode node = this.current;
this.current = this.current.getNext();
return node;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
list = (LinkedList) in.readObject();
current = (LinkedListNode) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(list);
out.writeObject(current);
}
}
public static class JavaUtilIterator implements java.util.Iterator, Externalizable {
private LinkedList list;
private LinkedListNode currentNode;
private LinkedListNode nextNode;
private boolean immutable;
public JavaUtilIterator() {
}
public JavaUtilIterator(final LinkedList list) {
this(list, true);
}
public JavaUtilIterator(final LinkedList list, final boolean immutable) {
this.list = list;
this.nextNode = this.list.getFirst();
this.immutable = immutable;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
list = (LinkedList) in.readObject();
currentNode = (LinkedListNode) in.readObject();
nextNode = (LinkedListNode) in.readObject();
immutable = in.readBoolean();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(list);
out.writeObject(currentNode);
out.writeObject(nextNode);
out.writeBoolean(immutable);
}
public boolean hasNext() {
return (this.nextNode != null);
}
public Object next() {
this.currentNode = this.nextNode;
if (this.currentNode != null) {
this.nextNode = this.currentNode.getNext();
} else {
throw new NoSuchElementException("No more elements to return");
}
return this.currentNode;
}
public void remove() {
if (this.immutable) {
throw new UnsupportedOperationException(
"This Iterator is immutable, you cannot call remove()");
}
if (this.currentNode != null) {
this.list.remove(this.currentNode);
this.currentNode = null;
} else {
throw new IllegalStateException("No item to remove. Call next() before calling remove().");
}
}
}
}
/**
* Items placed in a <code>LinkedList<code> must implement this interface .
*
* @see LinkedList
*
* @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a>
* @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
*/
interface LinkedListNode extends Externalizable {
/**
* Returns the next node
*
* @return The next LinkedListNode
*/
public LinkedListNode getNext();
/**
* Sets the next node
*
* @param next
* The next LinkedListNode
*/
public void setNext(LinkedListNode next);
/**
* Returns the previous node
*
* @return The previous LinkedListNode
*/
public LinkedListNode getPrevious();
/**
* Sets the previous node
*
* @param previous
* The previous LinkedListNode
*/
public void setPrevious(LinkedListNode previous);
}
interface Iterator extends Serializable {
public Object next();
}