Held Tuesday, March 14, 2000
Overview
Today we begin to consider one of the key ADTs: the list ADT.
We investigate a number of perspectives on what a list is and
start to think about which set of operations is preferable.
Notes
- Are there questions on
exam 3?
- Phase 3 is available.
- The expectation was that your part would include all the
appropriate interfaces and stub implementations
- Over the next few days, work to make sure that your stubs
work together
Contents
Summary
- Introduction to lists
- Iteration
- The design of lists
- A data-oriented perspective
- A functionality-oriented perspectivve
- Other design issues
- Due:
- Project, Phase 3:
Compilable interfaces and stubs for all your project classes.
- Java Plus Data Structures, Chapter 4: Implementing Lists
with Arrays
- Due:
- Java Plus Data Structures, Chapter 7: Linked Lists
- When we talk about data structures and ADTs, there are
a few basic questions we should ask ourselves.
- ADT Questions:
- What is the overall philosophy of the ADT?
- What are the core operations our ADT provides?
- What applications can take advantage of this ADT?
- Data Structure Questions:
- What is the overall structure of the implementation?
- How do I implement each operation?
- Given that implementation, what is the running time of each operation?
- I will do my best to ask you these six questions each time we visit
a data structure.
- Lists are among the simplest of ADTs.
- Like arrays, they are used for collecting data. That is, lists
can be used to store and retreive data.
- In effect, a list is an linearly-organized collection of information.
- Every nonempty list has a unique first element.
- Every nonempty list has a unique last element.
- Every element except the last has a unique successor.
- Each element except the first has a unique predecessor.
- Note that this is a data-oriented description of lists.
Is there also a function-oriented description of lists?
- Suprisingly, different computer scientists have very different
views of the operations lists might provide and the meanings of
those operations.
- For example, some expect that the elements in the list stay
in the same order after different operations. Others don't.
- That's nothing compared to the Lisp vs. everything else controversy.
- Let us begin our consideration of the functions for lists
by viewing lists from the Scheme perspective.
- What are the core list functions? These are the ones I've come up
with:
-
cons: Create a list, given a head and a tail.
-
car and head: Get the first element
of the list.
-
cdr and tail: Get all but the first
element of the list.
-
setcar! (boo! hiss!): Change the first element
of the list.
-
setcdr! (boo! hiss!): Change the rest of the list.
- Everything else can be built with these five.
- How might we phrase these in Java? Note that they correspond fairly
closely to the methods we would expect to see for a class with two
fields (
head and tail).
-
cons is the constructor.
-
car and cdr get the two fields.
-
setcar! and setcdr! set the two fields.
- There are also other ways to think about lists in terms of functions.
- For example, we might think about lists that more closely resemble
the collection interface.
- What are possible operations?
- We won't include all of these, but they're some to consider.
- Creation of new, empty, lists.
- Should we specify the type of elements?
- Should we specify the capacity?
- Insertion of elements in the list.
- Deletion of elements from the list.
- Determine size of the list.
- Test emptiness of the list.
- Is this just related to the size?
- Test containment of objects in the list.
- Modification of elements in the list.
- Is this just insertion and deletion?
- Iteration of the list
- Stepping through the elements, perhaps as in
map.
- As we design various kinds of lists, we may choose different
subsets of these operations, and fill in more details.
- There are a number of different ``kinds'' of lists, relating,
in part, to the way in which the user thinks of using them,
adding to them, and getting elements from them.
- We'll consider three different ones. After a high-level
overview, we'll return to each in more detail.
- The simplest lists simply provide the linear structure, but with
no user control over that structure. The user can add and delete
elements, but not determine where added elements go in the list
(or what happens during deletion).
- Iteration provides access to the elements.
- The list cannot rearrange itself unless
elements are added or removed.
- We add some complexity and usefulness by giving the user
some control over where elements go in the list. Such lists
might be called sequenced lists.
- The simplest control is simply to say ``new elements go
at the end/front, and elements never change position''.
- We might also add a ``current'' element and insert
before or after it.
- We might also impose a predetermined order (a ``sort order'')
on the list and require that it be iterated in that order.
- Insertion and deletion ensure that the sort order is maintained.
- Both lists and arrays provide mechanisms for collecting information.
- Unlike arrays, lists do not necessarily guarantee constant
time access to each element. (No matter what form of list you
consider.)
- However, lists do (usually) guarantee constant time insertion of elements
at a few places in the list.
- Some list implementations guarantee constant time insertion
at the front of the list.
- Some list implementations guarantee constant time insertion at
the end of the list.
- Some list implementations provide a ``current element''
(also called a cursor) and
guarantee constant time insertion after/before the current element.
- Before we implement lists, we need to consider a number of
other issues related to the design of lists.
- There are a number of ways in which we can implement lists, and
these implementations govern the cost of the various operations.
- Most programmers expect that insertion and testing for emptiness
are constant time operations. Certain kinds of deletion are also
constant time (e.g., delete the first element).
- There are a number of hidden issues to consider when designing
implementations.
- We might speak about sharing of sublists.
- For example, when we ask for the
tail
of a list and then modify that tail, do we modify the original list?
- Here's one example in Java form
// Assume l is the list (aardvark zebra bison chipmunk)
ConsCell lprime = l.tail();
lprime.setHead("macaque");
out.println(l.tail().head().toString());;
- Here's a similar example that might have different results
(at least in terms of our understanding of what modifications mean).
Note that it includes some ``obvious'' methods that are not
yet part of the
COnsCell class.
// Assume l is the list (aardvark zebra bison chipmunk)
ConsCell lprime = l.tail();
lprime.remove("bison");
out.println(l.contains("bison"));
- In designing our implementations, we will need to consider tradeoffs
between efficiency, memory usage, simplicity of implementation,
and ambiguity.