class Node
{
public Object data;
public Node next;
public Node previous;
}
public class LinkedList
{
private Node first;
private Node last;
...
}
Singly-Linked:
public class LinkedList
{
. . .
public Object removeFirst()
{
if (first == null) { throw new NoSuchElementException(); }
Object element = first.data;
first = first.next;
return element;
}
. . .
}
What do we need to do to make this method work for doubly-linked lists?
previous reference of the initial node after removallast reference of the LinkedList if the last element was removedpublic class LinkedList
{
. . .
public Object removeFirst()
{
if (first == null) { throw new NoSuchElementException(); }
Object element = first.data;
first = first.next;
if (first == null) { last = null; } // List is now empty
else { first.previous = null; }
return element;
}
. . .
}
Singly-linked:
public void addFirst(Object element)
{
Node newNode = new Node();
newNode.data = element;
newNode.next = first;
first = newNode;
}
What do we need to do to make this method work for doubly-linked lists?
previous reference of the second nodelast reference of the LinkedList if the last element was insertedpublic void addFirst(Object element)
{
Node newNode = new Node();
newNode.data = element;
newNode.next = first;
if (first == null) { last = newNode; }
else { first.previous = newNode; }
first = newNode;
}
public void addLast(Object element)
{
Node newNode = new Node();
newNode.data = element;
newNode.previous = last;
if (last == null) { first = newNode; }
else { last.next = newNode; }
last = newNode;
}
addFirstfirst and lastprevious and nextYou've seen the “removing at head” method. We also need to implement “removing at tail”.
Does the “mirror image” technique from the previous slide work?
previous methodLinkedList lst = new LinkedList();
lst.addLast("A");
lst.addLast("B");
lst.addLast("C");
ListIterator iter = lst.listIterator();
// The iterator is before the first element |ABC
iter.next();
// Returns “A”; the iterator is after the first element A|BC
iter.next();
// Returns “B”; the iterator is after the second element AB|C
iter.previous();
// Returns “B”; the iterator is after the first element A|BC
remove removes the last element that was returned by next or previousremove—add adds before the iteratorremove just remove the element before the iterator? while (iter.hasNext())
if (we don't like iter.next()) iter.remove();
while (iter.hasPrevious())
if (we don't like iter.previous()) iter.remove();
public Object previous()
{
if (!hasPrevious()) { throw new NoSuchElementException(); }
isAfterNext = false;
isAfterPrevious = true;
Object result = position.data;
position = position.previous;
return result;
}
isAfterNext, isAfterPrevious flagsremoveprivate Node lastPosition()
{
if (isAfterNext) { return position; }
else if (isAfterPrevious)
{
if (position == null) { return first; }
else { return position.next; }
}
else { throw new IllegalStateException(); }
}
removeNode positionToRemove = lastPosition();
positionToRemove.previous.next = positionToRemove.next; positionToRemove.next.previous = positionToRemove.previous;
if (isAfterNext) { position = position.previous; }
In the singly-linked list implementation, the list iterator had two references: position and previous. What should be done about the previous reference in the doubly-linked case?
next reference should also be added to support backwards movementposition.previousprevious reference, but we do need to add a boolean flag to tell whether the iterator is at the end of the list.@Test public void testAddLast()
{
LinkedList lst = new LinkedList();
check("", lst, "Constructing empty list");
lst.addLast("A");
check("A", lst, "Adding last to empty list");
lst.addLast("B");
check("AB", lst, "Adding last to non-empty list");
}
The check method tests that the list contains the letters in the string, and that all links are intact.
In this program, leave the LinkedList class alone. In the LinkedListTest class, observe how the first six lines of the commented-out main method (from the textbook) are in a JUnit test method, just like on the preceding slide.
Add a second JUnit test method. Use the next five lines of the commented-out code.
Did your test case pass?
check methodpublic static void check(String expected, LinkedList actual, String message)
{
int n = expected.length();
if (n > 0)
{
// Check first and last reference
assertEquals(message, expected.substring(0, 1), actual.getFirst());
assertEquals(message, expected.substring(n - 1), actual.getLast());
// Check next references
ListIterator iter = actual.listIterator();
for (int i = 0; i < n; i++)
{
assertTrue(message, iter.hasNext());
assertEquals(message, expected.substring(i, i + 1), iter.next());
}
assertEquals(false, iter.hasNext());
// Check previous references
for (int i = n - 1 ; i >= 0; i--)
{
assertTrue(message, iter.hasPrevious());
assertEquals(message, expected.substring(i, i + 1), iter.previous());
}
assertFalse(message, iter.hasPrevious());
}
else
{
// Check that first and last are null
try
{
actual.getFirst();
throw new IllegalStateException("first not null");
}
catch (NoSuchElementException ex)
{
}
try
{
actual.getLast();
throw new IllegalStateException("last not null");
}
catch (NoSuchElementException ex)
{
}
}
}
public or private, then any method in the same package can access.public class LinkedList
{
private Node first;
private Node last;
public static void check(String expected, LinkedList actual, String message)
{
int n = expected.length();
if (n > 0)
{
int i = 0;
for (LinkedList.Node current = actual.first; current != null; current = current.next)
{
assertEquals(message,
expected.substring(i, i + 1), current.data);
i++;
}
assertEquals(message, n, i);
for (LinkedList.Node current = actual.last; current != null; current = current.previous)
{
i--;
assertEquals(message,
expected.substring(i, i + 1), current.data);
}
assertEquals(message, 0, i);
}
else
{
assertNull(message, actual.first);
assertNull(message, actual.last);
}
}Make those changes in the CodeCheck run that you just did. In LinkedList, remove private from the instance variables. In LinkedListTest, replace the check method with the one from the preceding slide. Repeat the test run. What happens now?
Of course, the LinkedList code can't be quite the same code as in the book. The test should pass. Have a look at the addFirst method. Can you spot the error?