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 pospreviousnext = posnext; posnextprevious = posprevious; endif end
ListItem
class is easily defined as followsListItem.java
A class definition with only two constructor methods. The keywordfinal 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; } }
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:
So, these are the two methods that you can use to iterate through the set of values. Two examples of their use.public interface Enumeration { boolean hasMoreElements(); Object nextElement(); }
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:
The enumerator containsimport java.util.*; final class ListEnumerator implements Enumeration { LinkedList list; ListIterator cursor; ListEnumerator(LinkedList l) { } public boolean hasMoreElements() { } public Object nextElement() { } }
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:
What remains to be done is the implementation of the two promised methods:ListEnumerator(LinkedList l) { list = l; cursor = list.head(); cursor.next(); }
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.