remove(cursor pos)
begin
if empty list then
ERROR("empty list.")
else if cursor points at list header then
ERROR("cannot remove the list header")
else
pos
previous
next = pos
next;
pos
next
previous = pos
previous;
endif
end
ListItem class is easily defined as followsListItem.java
final class ListItem {
Object obj;
ListItem previous, next;
public ListItem(Object obj) {
this(null, obj, null);
}
public ListItem(ListItem previous, Object obj, ListItem next) {
this.previous = previous;
this.obj = obj;
this.next = next;
}
}
A class definition with only two constructor methods. The keyword final ensures that this class has no subclasses nor that a user can derive a
class from this one.
ListItem are placed in a doubly-linked list.
For this we have to define a class LinkedList. It contains
a special ListItem, called the head, to give the
user a principal handle to the list items. The insertion methods
(insertBefore, insertAfter) and the
removal method (remove) in the LinkedList class need
a pointer to the position in the list at which action should take place.
For this purpose we introduce a ListIterator class. A
ListIterator contains the LinkedList to which it
belongs and a pointer to the ListItem that is currently selected.
The ListIterator class contains methods to move the cursor
position. The LinkedList class contains the methods
Head, which returns an iterator that points to the head of
the linked list;find, which searches in the linked list for a requested
object and, if found, returns an iterator pointing to the objectelements in the LinkedList class; it returns an Enumeration object.
Most of the Java code for the two classes below should speak for itself.
final class ListIterator {
LinkedList owner;
ListItem pos;
ListIterator(LinkedList owner, ListItem pos){
this.owner = owner;
this.pos = pos;
}
/*
* check whether object owns the iterator
*/
public boolean belongsTo(Object owner) {
return this.owner == owner;
}
/*
* move to head position
*/
public void head() {
pos = owner.head;
}
/*
* move to next position
*/
public void next() {
pos = pos.next;
}
/*
* move to previous position
*/
public void previous() {
pos = pos.previous;
}
}
import java.util.*;
public class LinkedList {
ListItem head;
/*
* creates an empty list
*/
public LinkedList() {
head = new ListItem(null);
head.next = head.previous = head;
}
/*
* remove all elements in the list
*/
public final synchronized void clear() {
head.next = head.previous = head;
}
/*
* returns true if this container is empty.
*/
public final boolean isEmpty() {
return head.next == head;
}
/*
* insert element after current position
*/
public final synchronized void insertAfter(Object obj, ListIterator cursor) {
ListItem newItem = new ListItem(cursor.pos, obj, cursor.pos.next);
newItem.next.previous = newItem;
cursor.pos.next = newItem;
}
/*
* insert element before current position
*/
public final synchronized void insertBefore(Object obj, ListIterator cursor) {
ListItem newItem = new ListItem(cursor.pos.previous, obj, cursor.pos);
newItem.previous.next = newItem;
cursor.pos.previous = newItem;
}
/*
* remove the element at current position
*/
public final synchronized void remove(ListIterator cursor) {
if (isEmpty()) {
throw new IndexOutOfBoundsException("empty list.");
}
if (cursor.pos == head) {
throw new NoSuchElementException("cannot remove the head");
}
cursor.pos.previous.next = cursor.pos.next;
cursor.pos.next.previous = cursor.pos.previous;
}
/*
* Return an iterator positioned at the head.
*/
public final ListIterator head() {
return new ListIterator(this, head);
}
/*
* find the first occurrence of the object in a list
*/
public final synchronized ListIterator find(Object obj) {
if (isEmpty()) {
throw new IndexOutOfBoundsException("empty list.");
}
ListItem pos = head;
while (pos.next != head) { // There are still elements to be inspected
pos = pos.next;
if (pos.obj == obj) {
return new ListIterator(this, pos);
}
}
throw new NoSuchElementException("no such object found");
}
/*
* Returns an enumeration of the elements. Use the Enumeration methods on
* the returned object to fetch the elements sequentially.
*/
public final synchronized Enumeration elements() {
return new ListEnumerator(this);
}
}
synchronized keyword in many of the above methods indicates
that the methods that have this modifier change the internal state
of a LinkedList object
which is not "thread-safe": for example, insertions and removals of
list elements should not be carried out in random order, but in the order
in which they are requested. The synchronized keyword takes
care of this: when you call a
synchronized instance method, Java first locks the instance so
that no other threads can modify the object concurrently. See
the section
on interaction between threads for more details on synchronization.
Enumeration interface to step through the
elements in an instance of a built-in container class such as
Vector. You can look up the source code of
the Enumeration interface
in the development kit; it looks as follows:
public interface Enumeration {
boolean hasMoreElements();
Object nextElement();
}
So, these are the two methods that you can use to iterate through the
set of values. Two examples of their use.
Enumeration e = getAppletContext().getApplets(); // get all applets
while (e.hasMoreElements()) { // step through all applets
Object applet = e.nextElement();
System.out.println(applet.getClass().getName());
}
Vector vector = new Vector(); // declaration of vector
vector.addElement(new ...); // with addElement you can add items
Enumeration e = vector.elements(); // get all vector elements
while (e.hasMoreElements()) { // step through all vector elements
Object obj = e.nextElement();
// work with the object .....
}
Of course we want to offer this enumeration facility for
doubly-linked lists as well. The elements method of the
LinkedList class already delivers an Enumeration,
actually an instance of the ListEnumerator class.
The skeleton of this class is as follows:
import java.util.*;
final class ListEnumerator implements Enumeration {
LinkedList list;
ListIterator cursor;
ListEnumerator(LinkedList l) {
}
public boolean hasMoreElements() {
}
public Object nextElement() {
}
}
The enumerator contains
LinkedList instance variable that
points to the doubly-linked list we want to enumerate;ListItem instance variable that serves as a cursor
to the doubly-linked list under consideration.
The constructor ListEnumerator(LinkedList l) generates the
enumeration from a given doubly-linked list: it positions the cursor at
the list header and move it one position further. In Java code:
ListEnumerator(LinkedList l) {
list = l;
cursor = list.head();
cursor.next();
}
What remains to be done is the implementation of the two promised methods:
hasMoreElements:
simply check whether the cursor presently points at the list header.
public boolean hasMoreElements() {
return cursor.pos != list.head;
}
nextElement:
return the data of the list item presently pointed at and move the cursor
one step further. All this should happen unless the cursor is
already pointing at the list header, in which case an exception is thrown.
(See the section on exceptions for
details about this topic)
public Object nextElement() {
synchronized (list) {
if (cursor.pos != list.head) {
Object object = cursor.pos.obj;
cursor.next();
return object;
}
}
throw new NoSuchElementException("ListEnumerator");
}
Here you see another use of the synchronized keyword:
synchronized (expression) statement,expression is an object or array which is locked
during execution of the statement. This ensures
that no other threads can be executing the program section at the same time.
Collecting the Java code defining the ListEnumerator class
we get:
ListEnumerator.java
import java.util.*;
final class ListEnumerator implements Enumeration {
LinkedList list;
ListIterator cursor;
ListEnumerator(LinkedList l) {
list = l;
cursor = list.head();
cursor.next();
}
public boolean hasMoreElements() {
return cursor.pos != list.head;
}
public Object nextElement() {
synchronized (list) {
if (cursor.pos != list.head) {
Object object = cursor.pos.obj;
cursor.next();
return object;
}
}
throw new NoSuchElementException("ListEnumerator");
}
}
previous and next buttons allow you to move
the cursor. At the right-hand side we have a button to step through all
list elements and to find an object in the list.
The source code for this applet is available and can be used for testing other data structures such as queues, stacks, singly-linked (circular) lists, and so on.