Class 26: Linked Lists The Java Collection Classes Introduction to Computation and Problem

advertisement
Introduction to Computation and Problem
Solving
Class 26:
Linked Lists
Prof. Steven R. Lerman
and
Dr. V. Judson Harward
The Java Collection Classes
• The java.util package contains
implementations of many data structures that we
are also going to discuss and implement in a
simpler way in class.
• You are welcome to use the standard Java
implementations in problem sets. They are more
elaborate and abstract than the implementations
we will develop.
• Learning how to implement a few data structures
makes you more sophisticated in how you use all
data structures.
• We will cover the Java implementations in a later
lecture.
2
1
Goals
• We will examine and build a new kind of
data structure (a Linked List) using linking
rather than arrays.
• The most important theme, however, is
that implementation affects how useful a
data structure is as much as its interface
(set of methods).
3
Lists as an Abstract Data Type
A list is a collection of elements that has a
particular order.
– It can have arbitrary length.
– You should be able to insert or delete an
element anywhere.
– You should be able to go through the list in
order an element at a time.
4
2
A List Interface
public interface List<E> {
public boolean isEmpty();
public void addFirst( E e );
public void addLast( E e );
public boolean contains( E e );
public boolean remove( E e );
public E removeFirst()
throws NoSuchElementException;
public void clear();
public int size();
}
5
Lists and Ordinal Position
• There are certain obvious things we would
like to do with lists that we can't do using
only this interface. Two examples:
– How would you access the elements of the list
if you did not know them in advance?
– How would you insert an item into the list at
any other position than the beginning and the
end?
• One approach is to number or index the
positions in the list.
6
3
Indexed Lists
• We could then add three methods
– public E get( int n )
throws IndexOutOfBoundsException;
– public E remove( int n )
throws IndexOutOfBoundsException;
– public void add( E e, int n )
throws IndexOutOfBoundsException;
• The following code block will traverse an indexed
list, myList:
for ( int i = 0; i < myList.size(); i++ )
{
E e = myList.get( i );
. . .
}
7
Indexed Lists, 2
• Lists implemented using arrays (e.g., Java’s ArrayList)
often provide such methods because they are easy to
implement.
• The idea of using an index to access list members can lead
to problems, however.
– Since the index depends on ordinal position, it can
change every time an item is added to or deleted from
the list.
– If the list is not implemented on top of an indexed data
structure like an array, accessing an indexed element is
slow.
• When we use large lists like a telephone directory in
everyday life, we don't consider the index of an entry, only
its relative position.
8
4
Iterators
• An iterator is a helper class designed to
be used with a List or other collection
class. Think of it as a movable bookmark.
• It has methods to return the collection's
members one at a time.
• Iterators can also implement methods that
allow the collection to be modified relative
to the current position of the iterator.
9
SListIterator Interface
public interface SListIterator<E>
{
public boolean hasNext();
public E next()
throws NoSuchElementException;
public void remove()
throws IllegalStateException;
public void add( E e );
public void set( E e )
throws IllegalStateException
}
10
5
Iterator Methods
• The type of iterator we present here returns a new element
and advances to the next with the same operation, next().
There is no way to go back using this interface. The Java
ListIterator allows you to go forwards and backwards.
• The most recent element returned by next() is the current
element.
• remove() will delete the current element from the
underlying collection. set() will change it.
• add() will insert a new element after the current element
and before the element that would be returned by the
following call to next(). After a call to add(), the inserted
element becomes the new current. A call to next() will
return the element after the inserted one.
• The first call to next() returns the first element of the list.
Before the first call to next(), there is no current element.
11
Iterator and its Underlying List
• An iterator is an object based on an
underlying collection so we need a way to
create an iterator for a collection.
• We do this by adding a method to our
List interface:
public SListIterator slistIterator();
• Can you have 2 iterators over the same
List?
12
6
Using an Iterator
List<String> myList = new SLinkedList<String>();
. . .
SListIterator<String> iter = myList.slistIterator();
. . .
while ( iter.hasNext() )
{
String s = iter.next();
. . .
}
13
An SListIterator in Action
New Iterator
green
current
undefined
red
After 1st call to
next()
green
red
purple
purple
orange
orange
After adding
black
green
black
current is
black
After 2nd call to
next()
green
current is
green
current is
red
black
red
red
purple
purple
orange
orange
14
7
An SListIterator in Action, 2
After calling
remove()
After 3rd call to
next()
green
green
current is
undefined
black
current is
purple
black
purple
purple
orange
orange
15
List Implementation Stategies
• There are many ways to implement a list.
• In array-based implementations like the Java
Arraylist inserting an element at any place
except the end of the list is very expensive
because all the elements from the point of
insertion until the end must be moved back
to make room for the new entry. There is a
similar problem with deletion.
• For this reason, lists frequently use a linked
implementation (e.g., Java’s LinkedList)
16
8
Singly Linked Lists
• Linked lists are like freight trains.
• Each item to be put on the list is contained in an
instance of a new type of object called the link
corresponding to a freight car.
• In a singly linked list, the link not only contains
the listed item, but it also points to the next item
in the list as one freight car is coupled to the
next.
• The last link in the list points to nothing.
17
Singly Linked List Diagram
List
first
last
Link 1
Link 2
Item 1
Item 2
...
Link n
null
Item n
18
9
Singly Linked Lists, 2
• The list object itself points to the link holding the
first listed item, and often holds an additional
reference to the last link of the list to make it
easier to append items.
• We say that a link “contains” or “points to”, and
the list instance “points” or “holds a pointer”. In
Java these are all synonyms for holding a
reference.
• So the link doesn't really contain the item. It
simply holds a reference to the listed item.
• The last link holds a null reference in the field that
points to the next element.
19
A Singly Linked List Demonstration
List:
SListIterator:
SLinkedList:
SLinkedListApp:
SLinkedListView:
IteratorView:
List interface
List iterator interface
List implementation
main() application
List GUI
List iterator GUI
20
10
The Link Inner Class
public class SLinkedList<E> implements List<E> {
private static class SLink<E>
{
E item;
SLink<E> next;
SLink( E e, SLink<E> n )
{ item = e; next = n; }
SLink( E e )
{ this( e, null ); }
}
. . .
21
The SLinkedList Data Members
• Only first is necessary.
• last and length could be found by traversing
the list, but having these members and keeping
them up to date makes the calls size() and
append() much faster.
private int length = 0;
private SLink<E> first = null;
private SLink<E> last = null;
22
11
== vs the Object equals method
• contains( E e ) and remove( E e ) must search for E e
in the list. What does it means to find it?
• Must the list contain a reference to the identical object (==)?
Or is it sufficient that the list contain a reference to an object
that is equal but possibly distinct?
static private <E> boolean
objectEquals( E a, E b )
{
if ( a == null )
return ( b == null );
else
return a.equals( b );
}
23
Beware the Special Case
• The tricky part about implementing a linked list is
not implementing the normal case for each of the
methods, for instance, removing an object from
the middle of the list.
• What's tricky is making sure that your methods
will work in the exceptional and boundary cases.
• For each method, you should think through
whether the implementation will work on
– an empty list,
– a list with only one or two elements,
– on the first element of a list,
– on the last element of a list.
24
12
addFirst(), before
List
first
last
Link 1
...
Item 1
Link n
null
Item n
25
addFirst(), after
List
first
last
Link 0
Link 1
Item 0
Item 1
...
Link n
null
Item n
26
13
addFirst(), special case
before
after
List
List
first
last
null
first
last
Link 1
null
Item 1
27
addFirst(Object o)
public void addFirst(E e)
{
if ( first == null )
// if the list is empty
{
first = last = new SLink<E>( e );
}
else
{
first = new SLink<E>( e, first );
}
length++;
}
28
14
removeFirst(), before
List
first
last
Link 1
Link 2
Item 1
Item 2
...
Link n
null
Item n
29
removeFirst(), after
List
first
last
Link 1
Link 2
Item 1
Item 2
...
Link n
null
Item n
30
15
removeFirst(), special case
before
after
List
first
List
last
Link 1
Item 1
first
null
last
Link 1
null
Item 1
31
removeFirst()
public E removeFirst()
throws NoSuchElementException
{
if ( first == null )
// if list is empty
throw new NoSuchElementException();
else {
SLink<E> t = first;
first = first.next;
// if list had 1 element and is now empty
if ( first == null ) last = null;
length--;
return t.item;
}
}
32
16
SListIterator as Inner Class
public class SLinkedList<E> {
. . .
public SListIterator<E> slistIterator()
{
return new SLinkedListIterator();
}
public class SLinkedListIterator
implements SListIterator<E>
{
. . .
}
}
33
contains()
public boolean contains(E e) {
SListIterator<E> iter = new SLinkedListIterator();
while ( iter.hasNext() ) {
if ( objectEquals( iter.next(), e )) {
return true;
}
}
return false;
}
34
17
Linked List Uses and Variations
• Since our List interface possesses
addLast() and removeFirst() methods
you can implement a trivial queue on top
of the SLinkedList concrete data type.
• How would the implementation be
changed if each link had a back reference
(previous) as well as a forward reference
(next)? Such lists are called (you guessed
it) doubly linked lists. What operations
become easier?
35
Linked Lists vs. Array Lists
• Lists that are relatively static (few additions or
deletions) are frequently more efficient as array
lists.
• Lists accessed through ordinal position are
usually implemented as array lists.
• Highly dynamic lists are often more efficient as
linked lists.
• The time for many basic operations on a linked
list (adding an element, removing an element) are
fixed, but can vary widely for array lists.
36
18
Iterator Caution
Let's carry out a thought experiment. You create and populate
a list. You create an iterator over that list. You call
removeFirst() on the list to delete the first element of the
list. Now call next() on the iterator. What happens? What
should happen?
Although our implementation is reasonably robust, iterators
assume that they are called over a fixed list. That is, they do
NOT guarantee correct results if you modify the list after
iterator construction using either list or another iterator
instances methods. How might you "fix" this? What does
fixing mean? Would it be better to have an iterator that
always gave "correct" results or an iterator that threw an
exception if the underlying list had been modified?
37
LinkedList, Exercise
• Download the Java files in Lecture26.zip from
the class web site and save them to a new
directory.
• Create a new Eclipse project based on the
Lecture26 files. Compile and run.
SLinkedListApp contains main().
• Play with the simulation. Look at the
implementation in SLinkedList and List. The
rest of the files manage the simulation. You don't
need to look at them unless you are curious.
• The addLast and remove buttons don't work
because the corresponding method
implementations have been removed.
38
19
LinkedList, Exercise, 2
• Look at the addFirst() method, and then write
addLast(). Beware of special cases (what are they?).
• Compile and test using the simulation.
• Now look at the contains() method, and then write
remove(). Compile and test.
• The main List view also has a new red button labelled
"double". Click it. It doesn't do anything. Yet.
• Clicking the double button calls a method in a new class
ListUtil:
public static void doubleList(
SLinkedList<integer> l )
• doubleList() is almost empty. Write an implementation
for doubleList() that uses an iterator for the list, l, and
doubles each Integer on the list.
• Compile and test.
39
20
Download