16.2: OBJECT-ORIENTED DESIGN: The List Abstract Data Type (ADT)
- Page ID
- 15162
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)The PhoneList
example from the previous section illustrates the basic concepts of the linked list. Keep in mind that there are other implementations that could have been described. For example, some linked lists use a reference to both the first and last elements of the list. Some lists use nodes that have two pointers, one to the next node and one to the previous node. This enables traversals in two directions—front to back and back to front—as well as making it easier to remove nodes from the list. The example we showed was intended mainly to illustrate the basic techniques involved in list processing.
A generic list structure
Also, the PhoneList
example is limited to a particular type of data—namely, a PhoneListNode
. Let’s develop a more general linked list class and a more general node class that can be used to store and process lists of any kind of data.
An Abstract Data Type (ADT) involves two components: the data that are being stored and manipulated and the methods and operations that can be performed on those data. For example, an int
is an ADT. The data are the integers ranging from some MININT
to some MAXINT
. The operations are the various integer operations: addition, subtraction, multiplication, and division. These operations prescribe the ways that ints
can be used. There are no other ways to manipulate integers.
Information hiding
Moreover, in designing an ADT, it’s important to hide the implementation of the operations from the users of the operations. Thus, our pro- Information hiding grams have used all of these integer operations on ints
, but we have no real idea how they are implemented—that is, what exact algorithm they use.
Objects can be designed as ADTs, because we can easily distinguish an object’s use from its implementation. Thus, the private
parts of an object—its instance variables and private methods—are hidden from the user while the object’s interface—its public
methods—are available. As with the integer operators, the object’s public methods prescribe just how the object can be used.
Design specifications
So let’s design a list ADT. We want it to be able to store any kind of data, and we want to prescribe the operations that can be performed on those data—the insert, delete, and so on. Also, we want to design the ADT so that it can be extended to create more specialized kinds of lists.
The Node
Class
JAVA EFFECTIVE DESIGN
Generalizing a Type.
An effective strategy for designing a list abstract data type is to start with a specific list and generalize it. The result should be a more abstract version of the original list.

Figure 16.13: The Node
class is a generalization of the PhoneListNode
class.
Our approach will be to generalize the classes we created in the PhoneList
example. Thus, the PhoneListNode
will become a generic Node
that can store any kind of data (Fig. 16–13). Some of the changes are merely name changes. Thus, wherever we had PhoneListNode
, we now have just Node
. The link access methods have not changed significantly. What has changed is that instead of instance variables for the name, phone number, and so on, we now have just a single data reference to an Object
. This is as general as you can get, because, as we pointed out earlier, data
can refer to any object, even to primitive data.
The implementation of the Node
class is shown in Figure 16.14. Note that the data access methods, getData()
and setData()
, use references to Object
for their parameter and return type. Note also how we’ve defined the toString()
method. It just invokes data.toString()
. Because toString()
is defined in Object
, every type of data will have this method. And because toString()
is frequently overridden in defining new objects, it is useful here.

Figure 16.14: The Node
class is a more abstract version of the PhoneListNode
class.
The List
Class

FIGURE 16.15 The List
class contains a pointer to the head of the list and public methods to insert and remove objects from both the front and rear of the list.
Let’s now generalize the PhoneList
class (Fig. 16–15). The List
class will still contain a reference to the head
of the list, which will now be a list of Nodes
. It will still define its constructor, its fig-listuml isEmpty()
method, and its print()
method in the same way as in the PhoneList
.
However, in designing a generic List
class, we want to design some new methods, particularly because we want to use this class as the basis for more specialized lists. The PhoneList.insert()
method was used to insert nodes at the end of a list. In addition to this method, let’s design a method that inserts at the head of the list. Also, PhoneList
had a method to remove nodes by name. However, now that we have generalized our data, we don’t know if the list’s Objects
have a name field, so we’ll scrap this method in favor of two new methods that remove a node from the beginning or end of the list, respectively.

Figure 16.16: The List
ADT.
We already know the basic strategies for implementing these new methods, which are shown in the definition in Figure 16.16. We have renamed the insertAtRear()
method, which otherwise is very similar to the PhoneList.insert()
method. The key change is that now its parameter must be an Object
, because we want to be able to insert any kind of object into our list. At the same time, our list consists of Nodes
, so we have to use the Object
to create a Node in our insert methods:
head = new Node (obj);
Recall that the Node
constructor takes an Object
argument and simply assigns it to the data
reference. So when we insert an Object
into the list, we make a new Node
and set its data
variable to point to that Object
. Note that we check whether the list is empty before traversing to the last node.
The new insertAtFront()
method (Fig. 16.16) is simple to implement, since no traversal of the list is necessary. You just need to create a new Node
with the Object
as its data element and then link the new node into the head of the list:
Node newnode = new Node(obj);
newnode.setNext(head);
head = newnode ;
See Figure 16.8a for a graphical representation of this type of insertion.
The new removeFirst()
method is also quite simple to implement. In this case, you want to return a reference to the Object
that’s stored in the first node, but you need to adjust head
so that it points to whatever the previous head.next
was pointing to before the removal. This requires the use of a temporary variable, as shown in the method.
The new removeLast()
method is a bit more complicated. It handles three cases: (1) The empty list case, (2) the single node list, and (3) all other lists. If the list is empty, it returns null
. Obviously, it shouldn’t even be called in this case. In designing subclasses of List
we will first invoke isEmpty()
before attempting to remove a node.
If the list contains a single node, we treat it as a special case and set head
to null
, thus resulting in an empty list. In the typical case, case 3, we traverse the list to find the last node, again using the strategy of maintaining both a previous
and a current
pointer. When we find the last node, we must adjust previous.next
so that it no longer points to it.
Testing the List ADT
Heterogeneous lists
Testing the list ADT follows the same strategy used in the PhoneList
example. However, one of the things we want to test is that we can indeed create lists of heterogeneous types—lists that include Integers
mixed with Floats
, mixed with other types of objects. The main()
method in Figure 16.17 illustrates this feature.

Figure 16.17: A series of tests for the List
ADT.
JAVA EFFECTIVE DESIGN
The List
ADT.
One advantage of defining a List
ADT is that it lets you avoid having to write the relatively difficult list-processing algorithms each time you need a list structure.
The list we create here involves various types of data. The PhoneRecord
class is a scaled-down version of the PhoneListNode
we used in the previous example (Fig. 16.18). Its definition is shown in Figure 16.19. Note how we use an Object
reference to remove objects from the list in main()
. We use the Object.toString()
method to display the object that was removed.

PhoneRecord
class stores data for a phone directory
Figure 16.19: A PhoneRecord
class.
SELF-STUDY EXERCISES
Exercise 16.7
Trace through the main()
method line by line and predict its output.
Exercise 16.8
Design a test of the List
program that shows that it is possible to insert new elements into a list after some or all of its previous nodes have been removed.