Download as pdf or txt
Download as pdf or txt
You are on page 1of 11

!

83

public void add (Object elem) {


/ / Add elem after the last element of this list.
if (length == elems.length) expand();
elems[length++] = elem;
}
public void addAll (List that) {
/ / Add all the elements of that after the last element of this list.
ArrayList other = (ArrayList) that;
for (int i = 0; i < other.length; i++)
add(other.elems[i]);
}
public Object remove (int i) {
/ / Remove and return the element with index i in this list. Throw an
// IndexOutOfBoundsException if i is out of range.
if (i < 0 I I i >= length)
throw new IndexOutOfBoundsException();
Object oldElem = elemsfi];
for (int j = i+1; j < length; j++)
elems[j-1] = elems[j];
length--;
elems[length] = null;
return oldElem;
}
//////////// Iterator ////////////
public Iterator iterator () {
/ / Return an iterator that will visit all elements of this list, in left-to-right
/ / order.
return new A r r a y L i s t . L R I t e r a t o r ( ) ;
}
/ / / / / / / / / / / / Auxiliary method / / / / / / / / / / / /
private void expand () {
/ / Make the elems array longer.
/ / Code omitted here.

Program 8.9 (continued}


184 lava Collections

it (including the implicit reference between the two). Each call to the iterator's next
method simply returns the list element indexed by place, and also increments place.
Observe that the ArrayList class's instance variables elems and length were
declared as private in Program 8.9. The methods in the ArrayList .LRIterator
class can still access these instance variables, since an inner class can access all parts of
the class that contains it.

private class LRIterator implements Iterator {


// An Array List. LRIterator object is a left-to-right iterator over a
/ / l ist represented by an ArrayList object.
/ / This iterator is represented by the array index of the next element to be
// visited, place.
private int place;
private LRIterator () {
place = 0;

public boolean hasNext () {


return (place < length) ;
}
public Object next () {
if (place >= length)
throw new NoSuchElementException ( )
return elems [place++] ;
}
/ / The remove method is omitted here.

Program 8.10 Iterator for the implementation of bounded lists using arrays (as an inner class of
ArrayList).

class tag elems length


list Q ^ArrayListj f \ 4 ]
class tag length 0
ObjectD | 6 I That ] is | the |question| I I

iter

Figure 8.11 An ArrayList object list, and an iterator object iter constructed from it by: iter
= list.iterator().
List Abstract Data !85

It is important to note that the iterator shares the list object. In particular, the iterator
does not copy the list elements. It does not need its own data structure. All the
information needed by the iterator is contained in the list's own data structure, apart
from the place-marker. This is typical of most iterators over most collections: a place-
marker and a reference to the underlying collection (which may be implicit) are quite
sufficient to implement them.

8.6 Implementation of unbounded lists using linked lists


An unbounded list can easily be represented by a singly-linked list (SLL). We will need
links to both the first and last nodes, since elements will sometimes be added at the end of
the list. Figure 8.12 shows this representation in the general case, and also illustrates how
a list of words would be represented.
The implementation of the list ADT is again straightforward. Table 8.13 summarizes
the SLL algorithms that would be used, together with their complexities.
The implementation itself is shown in Program 8.14. This is a Java class named
LinkedList that is declared as implementing the List interface. The instance vari-

(a) first «a,b, ...,z>.


last\»

(b) first To be not *4H to be


last\*
«To, be, or, not, to, be>:
(c) first That questiorf «That, is, the, question»
last\»

(d) first
last

Figure 8.12 Representation of unbounded lists by SLLs: (a) general case; (b—c) lists of words: (di
empty list.

Table 8.13 Implementation of lists using SLLs: summary of algorithms (where n, n1, and n2 are the
lengths of the lists involved).

Operation Algorithm Time complexity


get node counting 0(n)
equals element-wise equality test 0(n2)
set node counting O(n)
add (2 parameters) node counting followed by SLL insertion 0(n)
add (1 parameter) SLL insertion after last node 0(1)
addAll repeated SLL insertion after last node 0(n2)
remove node counting followed by SLL deletion 0(n)
186 lava Collections

ables f i r s t and last are links to the first and last nodes, respectively, and are initially
null. The instance variable length keeps track of the list's length. The nodes them-
selves are of class SLLNode (see Program 4.4).
The 1-parameter add operation has time complexity 0(1), because a direct link to the
last node is available. However, the get, set, 2-parameter add, and remove opera-
tions each has to locate the node with a given index. Unless that node happens to be the
first or last node, no direct link to that node is available, so the operation must count out
nodes from the start of the SLL. The index could be anything between 0 and n, where n is

public class LinkedList implements List {


/ / Each LinkedList object is an unbounded list whose elements are
/ / objects.
/ / This list is represented as follows: f i r s t and last are links to the first
/ / and last nodes of an SLL containing its elements; length is the number
/ / of elements.
private SLLNode first, last;
private int length;
1 1 1 1 1 1 1 1 1 1 1 1 Constructor 1 1 1 1 1 1 1 1 1 1 1 1
public LinkedList ( ) {
/ / Construct a list, initially empty.
first = last = null;
length = 0;
}
1 1 1 1 1 1 1 1 1 1 1 1 Accessors 1 1 1 1 1 1 1 1 1 1 1 1
public boolean is Empty ( ) {
/ / Return true if and only if this list is empty.
return ( f i r s t == null);
}
public int size () {
/ / Return this list's length.
return length;
}
public Object get (int i) {
/ / Return the element with index i in this list. Throw an
/ / IndexOutOf BoundsException if i is out of range.
if (i < 0 || i >= length)
throw new IndexOutOf BoundsException ();
SLLNode curr = node ( i ) ;
return curr.element ;

Program 8.14 Java implementation of unbounded lists using SLLs (continued on next page).
List Abstract Data

public boolean equals {List that) {


/ / Return true if and only if this list and that have the same length, and
/ / each element o f this list equals t h e corresponding element o f
if (length != other . length)
return false;
for (SLLNode currl = first,
curr2 = other. first;
currl ! = null ;
currl = currl. succ, curr2 = curr2.succ) {
if {! currl .element .equals (curr2 .element) )
return false,
}
return true;
}
////////////// Transformers ////////
public void clear ( ) {
/ / Make this list empty.
first = last = null;
length = 0 ;
}
public void set (int i, Object elem) {
/ / Replace by elem the element at index i in this list. Throw an
/ / IndexOutOf BoundsException if i is out of range.
if (i < 0 || i >= length)
throw new IndexOutOf BoundsException () ;
SLLNode curr = node ( i ) ;
curr. element = elem;

Program 8.14 (continued on next page)

the list's length, and will be about n/2 on average. Thus these four operations all have
time complexity O(n).
The linked-list implementation of lists could be used in any application (including the
text editor of Example 8.1). The fact that it supports unbounded lists is a major
advantage. On the other hand, the O(n) time complexity of several operations would
slow down any application that uses these operations frequently (such as the text editor).
In practice, this implementation is not suitable for applications that process very long
lists.
The iterator operation in Program 8.14 constructs a left-to-right iterator over
the LinkedList object. This iterator is an object of the class LinkedList.
LRIterator shown in Program 8.15 (which would be an inner class of the
LinkedList class). The iterator is represented by a place-marker, place. This
188 lava Collections

public void add (int i, Object elem) {


/ / Add elem as the element with index i in this list. Throw an
/ / IndexOutOf Bounds Except ion if i is out of range.
if (i < 0 || i > length)
throw new IndexOutOf BoundsExcept ion ()
SLLNode newest = new SLLNode (elem, null);
if (i == 0) {
newest. succ = first;
first = newest;
} else {
SLLNode pred = node(i-l);
newest. succ = pred. succ;
pred. succ = newest;
}
if (newest. succ == null)
last = newest;
length++;
}
public void add (Object elem) {
/ / Add elem after the last element of this list.
SLLNode newest = new SLLNode (elem, null);
if (first == null)
first = newest;
else
last. succ = newest;
last = newest;
length++;
}
public void addAll (List that) {
/ / Add all the elements of that after the last element of this list.
LinkedList other = (LinkedList) that;
for (SLLNode curr = other. first;
curr != null; curr = curr. succ)
add ( curr . element ) ;

Program 8.14 (continued on next page)

place-marker is initially a link to the first node of the SLL, but will later be a link to the
other nodes in turn. Figure 8.16 shows a LinkedList object together with a
LinkedList .LRIterator object that has just been constructed from it (including
the implicit reference between the two). Each call to the iterator's next method simply
returns the list element in the node to which place is a link, and also advances place
to the successor node (which may be null).
List Abstract Data Types 189

public Object remove (int i) {


/ / Remove and return the element with index i in this list. Throw an
/ / IndexOutOf Bounds Except ion if i is out of range.
if (i < 0 || i >= length)
throw n e w IndexOu t o f Bounds Exception

if (i == 0) {
oldElem = first.element;
if ( f i r s t == last)
last = null;
first = first.succ;
} else {
SLLNode pred = node(i-1);
SLLNode old = pred.succ;
oldElem = old.element;
pred.succ = old.succ;
if (old == last)
last = pred;
}
length--;
return oldElem;
}
111111111111 Iterator 11! 11111 i 11
public Iterator iterator () {
/ / Return an iterator that will visit all elements of this list, in left-to-right
/ / order.
return new LinkedList.LRIterator();
}
//////////// Auxiliary method ///////
private SLLNode node (int i) {
/ / Return a link to the node containing the element with index i in this list.
SLLNode curr = first;
for (int j = 0; j < i; j++)
curr = curr.succ;
return curr;

Program 8.14 (continued)


190 lava Collections

private class LRIterator implements Iterator {


/ / A L i n k e d L i s t . LRIterator object is a left-to-right iterator over a
/ / list represented by a LinkedList object.
/ / This iterator is represented by a link to the node containing the next
/ / element to be visited, place.
private SLLNode place;
private LRIterator () {
place = first;

public boolean hasNext () {


return (place != null) ;
}
public Object next () {
if (place == null)
throw new NoSuchElementException ( ) ;
Object nextElem = place . element ;
place = place. succ;
return nextElem;
}
/ / The remove method is omitted here.

Program 8.15 Iterator for the implementation of unbounded lists using SLLs (as an inner class of
LinkedList).

class tag first last length


• i i
list LinkedList I >• I V I 4 |

class tag element succ


SLLNode |question|

iter

Figure 8.16 A LinkedList object list, and an iterator object iter constructed from it by: iter
= list.iterator().
191

8.7 Lists in the Java class library


The Java 2 class library includes several classes that support lists of objects. The class
library similarly supports sets and maps (tables). The classes that support lists, sets, and
maps are known as the collection classes, and are surveyed in Appendix C.
The list ADT contract is embodied by the J a v a . u t i l . List interface, which is
shown in Appendix C.2. This interface is similar to the simpler List interface of
Program 8.2. The Java.util .List interface differs mainly in that:
• Its equals method accepts an arbitrary object as argument. This is because it over-
rides the equals method of the ancestor Object class. Of course, the equals
method in any List class must return false if its argument is not a List object,
• Its addAll method accepts an arbitrary collection as argument, not necessarily a list.
The motivation here is to make the collection classes inter-operable, as far as possible.
Thus /. addAl1 (c) adds all the elements of collection c to list l. We can view this as
first forming a list of all the elements of c, then concatenating the lists as usual.
• It provides several additional methods such as indexOf and lastlndexOf (which
search the list for a given element), contains (which tests whether the list contains a
given element), containsAll (which tests whether the list contains all the elements
in a given collection), removeAll and retainAll (which remove from the list all
the elements in, or not in, a given collection).
• It provides additional iterators (discussed below).
Several library classes implement the java. util . List interface, representing lists
in different ways.
The java, util .ArrayList class represents a list by an array, as described in
Section 8.5. If addition of an element causes an overflow, all elements are copied into a
new and longer array. As well as the operations required by the J a v a . u t i l .List
interface, this class provides an operation that allows application code to expand the array
in anticipation of future additions.
The J a v a . u t i l .Vector class is a Java 1 legacy that has been retrospectively
adapted to implement the Java.util .List interface. This class is very similar to
j ava . u t i l . ArrayList, but the latter was specifically designed to fit into the collec-
tion classes framework and should be preferred.
The java. util .LinkedList class represents a list by a doubly-linked list
(DLL). This class provides additional operations getFirst, getLast, addFirst,
addLast, removeFirst, and removeLast, which allow a LinkedList object to
mimic a stack or queue. It is not clear why these useful operations were left out of the
j ava. u t i l . List interface, as they can be implemented efficiently with any reason-
able list representation (see Exercise 8.12). The choice of a DLL representation is easily
justified (see Exercise 8.13).
We have already encountered the Java. u t i l . Iterator interface, in Section 8,4.
This interface provides not only the hasNext and next operations shown in Program
8.5, but also a remove operation (which removes from the underlying collection the
element most recently returned by next). For example, the following code pattern:
192 lava Collections

Iterator elems = list . iterator ()


while ( elems. hasNext ()) {
Object elem = elems. next ();
// code that inspects elem
elems . remove ( ) ;

iterates over the list list, inspecting and then removing each element in turn.
The java.util .Listlterator interface has additional operations that are
specific to iteration over lists. It extends the java.util . Iterator interface with
operations has Previous and previous (which are analogous to hasNext and
next, and allow right-to-left iteration over the underlying list), set (which replaces,
in the underlying list, the element most recently returned by next or previous), and
add (which adds a new element to the underlying list just before the place-marker). The
j a v a . u t i l .LinkedList class provides a listlterator operation, which
constructs a Listlterator object from a LinkedList object.

8.8 Case study: a videotape manager


It is awkward for users to keep track of the contents of their videotapes. Indeed, the latest
'smart' video recorders offer to do this automatically. A particular problem is to find a
gap on a videotape where we can record a video program without overwriting another
program.
In this section we will develop a simple system to keep track of the contents of a
videotape. We will think of a videotape as being divided into segments of varying length,
where each segment is either recorded (containing a video program) or blank. The
videotape's contents will be a list of segments.
To make things slightly more complicated, a video program can be recorded in either
standard-play mode or long-play mode. A video program recorded in long-play mode
occupies half as much space on the tape as one recorded in standard-play mode.
We will assume that our videotape manager must satisfy the following requirements:
• It must be possible to view the list of segments on the videotape.
• It must be possible to add a new video program to the videotape. The new video
program should occupy the first sufficiently-long blank segment on the videotape. If
no sufficiently-long segment can be found, a suitable message should be displayed.
Note that adding a video program may involve splitting an existing blank segment into
the new recorded segment and a shorter blank segment.
• It must be possible to erase a video program from the videotape. The segment contain-
ing the video program should become blank. This blank segment should be combined
with adjacent blank segments to prevent the videotape's contents from becoming
fragmented (see Section 8.8.3).
The complete system is available as a Java application on the companion Web
List Abstract Data Types 193

site, and includes a suitable user interface. In this section we will confine our
attention to the representation of a videotape and the algorithms needed to add
and erase programs.

8.8.1 Representing a videotape's contents


Our first task in the design of our video manager is to decide how to represent the various
entities.

Representing video programs


A recorded video program has attributes such as its title, its channel, its duration, and
whether it has been recorded in standard-play or long-play mode.
Program 8.17 shows a Java class named VideoProgram, whose objects will repre-

public class VideoProgram {


/ / Each VideoProgram object represents a recorded video program.
/ / This video program is represented by its title, its channel name, its
/ / duration (in seconds), and a flag indicating whether it is recorded in
/ / long-play mode.
private String title, channel;
private int duration;
private boolean isLongPlay;

//////////// Constructor///////////publicVideoProgram (String title, String c


int duration, boolean isLongPlay) {
/ / Create a new video program with the given details.
this. title = title;
this. channel = channel;
this. duration = duration;
this. isLongPlay = isLongPlay;
}

/ / / / / / / / / / / / Accessors/////////////////One simple accessor per attribute: getTitle, ge


// getDuration, isLongPlay.
/ / See code on the companion Web site.

Program 8.17 Representation of recorded video programs by a Java class VideoProgram,

You might also like