CSCI 151 Lab 4
Doubly-Linked Lists and Iterators

Due by 6PM on Friday April 1

In this lab you will implement a doubly linked list and an iterator for it.

The purpose of this lab is to:

 

 

 

Part 1 - Doubly Linked Lists

Your first task is to implement a doubly linked list called MyLinkedList<T> and a corresponding ListIterator for your class.

I have some starter  code for you here: Lab4.zip Unzip this file into your csci151 lab folder and change the name of the project folder to Lab4<your last name> before you create your Lab4 project in Eclipse.  This has a start of the code for your MyLinkedList class, a test program for Part 3, and some data files.

In this lab you create a class called MyLinkedList<T>. Your class should extend AbstractList<T>.

When you have finished your implementation, be sure to test it thoroughly before continuing. In doubly linked lists, the addition or removal of items can be especially tricky as you need to be sure to properly update all of the pointers of the next and previous elements, as well as handle the special cases for removal from the front or tail. Keep a piece of paper with you and draw pictures to help with your coding. Nobody writes this code without referring to pictures.

 

Class Node:

You'll probably want to create a nested class (that is, a class inside a class) to represent a node in your linked list:
public class MyLinkedList<T> extends AbstractList<T> {
    class Node {
        T data;
        Node next;
        Node prev;

        // more code here for class Node
    }

}

Constructors for MyLinkedList:

You should only need a single no-argument constructor that creates an empty list and initializes all the necessary variables. We will use sentinal nodes at each end of the list to reduce the number of special cases we have to deal with

Empty linked list

Private Methods for MyLinkedList:

Node getNth(int index)
a method that returns the actual Node at a specified index, not the content.
 
 
 
 
 

Feel free to add other private methods if you find it useful to do so.

Public Methods for MyLinkedLists:

boolean add(T data)
void add(int index, T data)
add an element into this list (either at end or by index)
throw IndexOutOfBoundsException as needed (same rules as MyArrayList)
Note: the boolean add method will always return true; it is a boolean function due to the method definition in AbstractList
T get(int i)
get contents at position i
throw IndexOutOfBoundsException as needed
T set(int i,T data)
set the value at index i to data, return old value
throw IndexOutOfBoundsException as needed
T remove(int i)
remove (and return) the element from position i in this list
throw IndexOutOfBoundsException as needed
void clear()
remove all elements from the list
boolean isEmpty()
determine if the list is empty
int size()
return the number of elements being stored

Testing your List

You should be able to re-use the tests you wrote in Lab 2 for MyArrayList. Copy the MyArrayListTest.java file into your lab4 folder and rename it to be MyLinkedListTest.java. You will need to delete and re-create your Lab4 project to make the file appear in Eclipse. Or just have Eclipse create the JUnit source file MyLinkedListTest.java and copy and paste from your MyArrayListTest.java file. Then you can open up the file, rename the public class to be "MyLinkedListTest", and then update the methods to use MyLinkedLists instead of MyArrayLists.

Part 2 - ListIterator

Once your MyLinkedList class is working, you will need to create a ListIterator which is returned by the factory listIterator() method. Use a helper class MyLinkedListIterator nested in the same file as MyLinkedList in the same way the Node class is nested within MyLinkedList.

class MyLinkedListIterator implements ListIterator<T> {

    // class variables here

    public boolean hasNext() {
            // your code here
    }

    // more methods, etc.
}
    

The text talks about nested classes on p. 161. If you want more, here is Oracle's discussion of nested classes. You can read about anonymous classes here. Most people new to Java, and even some experienced ones, find nested classes easier to handle than anonymous classes. You are free to use either technique,thogh the supplied starter code builds a nested class.

The ListIterator is able to traverse the list by moving across one element at a time in either direction. It might be helpful to consider that the iterator has size+1 positions in the list: just before the first element, between that element and the next, ..., and just after the final element.When first constructed, the iterator is logically in the state shown below.

Linked list iterator at construction

You will need to implement all of the methods of a listIterator -- hasNext()/next(), hasPrevious()/previous(), nextIndex()/previousIndex(), remove(), set(x), and add(x) as discussed below:

Public Methods to add to MyLinkedListIterator

boolean hasNext()
Return true if there are more elements when going in the forward direction.
T next()
Return the next element in the list when going forward.
Throw NoSuchElementException if there is no such element
boolean hasPrevious()
Return true if there are more elements when going in the reverse direction.
T previous()
Return the next element in the list when going backwards.
Throw NoSuchElementException if there is no such element
int nextIndex()
Return the index of the element that would be returned by a call to next()
Return the list size if at the end of the list
int previousIndex()
Return the index of the element that would be returned by a call to previous()
Return -1 if at the start of the list
void set(T x)
Change the value in the node returned by the most recent next/previous with the new value.
Throw an IllegalStateException if neither next nor previous were called
Throw an IllegalStateException if add or remove have been called since the most recent next/previous
void remove()
Remove the last element returned by the most recent call to either next/previous
Throw an IllegalStateException if neither next nor previous were called
Throw an IllegalStateException if add has been called since the most recent next/previous
void add(T x)
Insert the given item into the list immediately before whatever would have been returned by a call to next()
The new item is inserted before the current cursor, so it would be returned by a call to previous() immediately following.
The value of nextIndex or previousIndex both are increased by one

Programming Hints

There are many ways to keep track of where the iterator currently is. Some people keep pointers to the next node and the previous node. I find it easier to keep a variable left that points to the node just to the left of the current position, and to calculate next() and previous() from that. Since set() and remove() both change based on what direction we were last traversing, I keep boolean flags to tell me if we were moving in the next-direction or the previous-direction.

It is possible, and sometimes useful, to have several iterators working on the same list. If they both try to change the structure of the list you could get into an unpredictable state. Therefore you should check that the list hasn't been modified by someone else before you let an iterator make a modification. An easy way to do this is to have both the list and the iterator keep track of the number of modifications that have been made to the list. When the iterator is created it grabs the list's modification count. Each change the iterator makes to the list increases both its and the lists modification count. However, if you have two iterators modifying the list, one of them will find that its modification count will be different from the list's, so you will know that the list structure has been changed and that iterator is no longer viable. See the text for additional details and suggestions.

Public Methods to add to MyLinkedList.java

We access iterators through the following factory methods in MyLinkedList. Each of these should just create a new MyLinkedListIterator and return it.

ListIterator<T> myListIterator()
Iterator<T> myIterator()
these factory methods return an object of your MyLinkedListIterator class for the current list.

Note: You inherit a working ListIterator from AbstractList, but the one you create will be more efficient. For this lab we will depart from the usual Java terminology and call the factory methods myIterator( ) and myListIterator( ) rather than iterator( ) and listIterator( ). We will run grading scripts using the "my" names. Be sure to have methods myIterator( ) and myListIterator( ).

Part 3 - Why do we need iterators?

Students often miss the point of iterators. Especially in a structure like a linked list, where you can always access the data through indexing with the get( ) method, iterators seem superfulous. This part of the lab might convince you that they are actually quite useful.

The starter code for this lab includes an application program IterationTester.java. This program opens up the text file whose name is given in args[0] and makes a list of all of the different words that appear in the file. Each time it reads a word it checks whether its wordlist contains this word; if not, it adds it to the list. The program performs this algorithm three times, and prints the number of milliseconds each attempt takes:

  1. The first version maintains the list of words as an ArrayList<String>. To see if word is an element of the list, it uses w=list.get(i) for each value of i between 0 and the length of the list. If word.equals(w) is true for any i, the word is contained in the list, otherise it is not.
  2. The second version maintains the list of words as a MyLinkedList<String> and uses an iterator it to walk through the words of the list. At each step w=it.next(). Again, if any word.equals(w) is true, then the list contains the word.
  3. The third version again maintains the list of words as a MyLinkedList<String>, but as with the ArrayList version it uses w=list.get(i) to look for the word in the list. If you think about what you did to implement get() for MyLinkedList you should see that this is not very efficient. This exercise should help you to see just how inefficient this is.

Use program IterationTester to complete the following table. Put a README.txt file in your project folder that contains the three times for each of these files. To save you a little time, I have included a start on this README file in the starter code:

File Words

Unique Words

ArrayList Time Iterator Tiome Indexing Time
Prufrock.txt 1084 501 44 ms 35 ms 81 ms
IHaveADream.txt 1589 630      
EveOfStAgnes.txt 2902 1587      
Hamlet.txt 32,006 7718      
PrideAndPrejudice.txt 121,557 13,065      

Note that you might need to let the program run overnight to complete the last file. Even Hamlet, which has half of the number of unique words, takes about 25 minutes in my implemeentation of MyLinkedList. I thought about giving you WarAndPeace..... You should see that the linked list with iterators runs in time that is similar to that of the ArrayList with indexing, while the linked list with indexing is far, far worse.

By the way, here are those files:

The files are in a folder textFiles in the project folder. Your arguments forthe Run Configurations file should be similar to "textFiles/Prufrock.txt".

 

Handing In Your Work

You should hand in a zipped copy of the project folder for this lab. Before you zip the project folder please remove the textFiles folder from it. The project folder should contain a lab4 package with files MyLinkedList.java and IterationTester.java. The projectd folder should also contain a README file with your times from Part 3. . If you adhered to the honor code in this assignment, add the following statement to your README file

I have adhered to the Honor Code in this assignment.

If you have worked with a partner you should both sign the README file, and both of your names should be in a comment on your MyLinkedList.java. Only one of you should hand in the lab; you will both receive the same score for the lab.

You want to keep the graders happy. If there are portions of this code that you know are not working correctly, it would be helful if you included information about what does and what doesn't work in your README file.

In case you don't remember how to make a compressed file, go to your csci151 folder and make a compressed version of your project folder. On a Mac select this folder and select Compress from the File menu. On Windows right-click on the project folder and select Send to -> Compressed (zipped) folder. In either case you should get a zip file. Open the Blackboard course for our class. Click on the Course Material link, then on the Lab 4 link. In the Assignment Submission section click on the Browse My Computer button. this will bring up the usual navigation controls that should allow you to select your zipped project file for this assignment.


Last Modified: October 24, 2021 Bob Geitz from previous versions by Ben Kuperman and Alexa SharpVI Powered