Building Java Programs: Linked Lists
Building Java Programs: Linked Lists
Chapter 16
Linked Lists
int x = 5;
int y = x; // x = 5, y = 5
y = 17; // x = 5, y = 17
x = 8; // x = 8, y = 17
3
Reference semantics
• reference semantics: Behavior where variables actually store
the address of an object in memory.
– When one reference variable is assigned to another, the object is
not copied; both variables refer to the same object.
a1 index 0 1 2 3 4 5 6
value 7
4 5 2 12 14 14 9
a2
4
References and objects
• In Java, objects and arrays use reference semantics. Why?
– efficiency. Copying large objects slows down a program.
– sharing. It's useful to share an object's data among methods.
panel1
panel2
5
References as fields
• Objects can store references to other objects as fields.
Example: Homework 3 (HTML Validator)
– HtmlValidator stores a reference to a Queue
– the Queue stores many references to HtmlTag objects
– each HtmlTag object stores a reference to its element String
String h t m l
String b o d y 6
Null references
• null : A value that does not refer to any object.
index 0 1 2 3 4
words value null null null null null
– not the same as the empty string "" or the string "null"
– Why does Java have null ? What is it used for?
7
Null references
– Unset reference fields of an object are initialized to null.
public class Student {
String name;
int id;
}
name null
timmy id 0
8
Things you can do w/ null
• store null in a variable or an array element
String s = null;
words[2] = null;
Student String
name null
timmy
id 0
Output:
Exception in thread "main"
java.lang.NullPointerException
at Example.main(Example.java:8)
11
References to same type
• What would happen if we had a class that declared one of its
own type as a field?
12
Linked data structures
• All of the collections we will use and implement in this course
use one of the following two underlying data structures:
– an array of all elements
• ArrayList, Stack, HashSet, HashMap
42 -3 17 9
front 42 -3 17 9 null
13
A list node class
public class ListNode {
int data;
ListNode next;
}
14
List node client example
public class ConstructList1 {
public static void main(String[] args) {
ListNode list = new ListNode();
list.data = 42;
list.next = new ListNode();
list.next.data = -3;
list.next.next = new ListNode();
list.next.next.data = 17;
list.next.next.next = null;
System.out.println(list.data + " " + list.next.data
+ " " + list.next.next.data);
// 42 -3 17
}
}
15
List node w/ constructor
public class ListNode {
int data;
ListNode next;
• Into this?
data next data next data next
list
10 20 30
17
Linked node problem 2
• What set of statements turns this picture:
data next data next
list
10 20
• Into this?
data next data next data next
list
30 10 20
18
Linked node problem 3
• What set of statements turns this picture:
data next data next
list1
10 20
• Into this?
data next data next data next
list1
10 30 20
data next
list2
40
19
Linked node problem 4
• What set of statements turns this picture:
data next data next
list
10 ... 990
• Into this?
data next data next data next
list
10 ... 990 1000
20
References vs. objects
variable = value;
2
• For the list at right:
data next data next
a
– a.next = value; 10 20
1
means to adjust where 1 points
– variable = a.next;
means to make variable point at 2
21
Reassigning references
• when you say:
– a.next = b.next;
22
Linked node question
• Suppose we have a long chain of list nodes:
23
Algorithm pseudocode
• Start at the front of the list.
• While (there are more nodes to print):
– Print the current node's data.
– Go to the next node.
24
Traversing a list?
• One (bad) way to print every value in the list:
while (list != null) {
System.out.println(list.data);
list = list.next; // move to next node
}
25
A current reference
• Don't change list. Make another variable, and change that.
– A ListNode variable is NOT a ListNode object
current
27
Linked list vs. array
• Algorithm to print list values: • Similar to array code:
28
A LinkedIntList class
• Let's write a collection class named LinkedIntList.
– Has the same methods as ArrayIntList:
• add, add, get, indexOf, remove, size, toString
LinkedIntList
ListNode ListNode ListNode
front
add(value)
data next data next data next
add(index, value) 42 -3 17
indexOf(value)
remove(index) element 0 element 1 element 2
size()
toString()
29
LinkedIntList class v1
public class LinkedIntList {
private ListNode front;
LinkedIntList
public LinkedIntList() {
front = null; front =
}
methods go here
30
Implementing add
// Adds the given value to the end of the list.
public void add(int value) {
...
}
31
Adding to an empty list
• Before adding 20: After:
data next
front = front = 20
element 0
32
The add method, 1st try
// Adds the given value to the end of the list.
public void add(int value) {
if (front == null) {
// adding to an empty list
front = new ListNode(value);
} else {
// adding to the end of an existing list
...
}
}
33
Adding to non-empty list
• Before adding value 20 to end of list:
• After:
data next data next data next
front = 42 -3 20
element 0 element 1 element 2
34
Don't fall off the edge!
• To add/remove from a list, you must modify the next
reference of the node before the place you want to change.
35
The add method
// Adds the given value to the end of the list.
public void add(int value) {
if (front == null) {
// adding to an empty list
front = new ListNode(value);
} else {
// adding to the end of an existing list
ListNode current = front;
while (current.next != null) {
current = current.next;
}
current.next = new ListNode(value);
}
}
36
Implementing get
// Returns value in list at given index.
public int get(int index) {
...
}
37
The get method
// Returns value in list at given index.
// Precondition: 0 <= index < size()
public int get(int index) {
ListNode current = front;
for (int i = 0; i < index; i++) {
current = current.next;
}
return current.data;
}
38
Implementing add (2)
// Inserts the given value at the given index.
public void add(int index, int value) {
...
}
39
The add method (2)
// Inserts the given value at the given index.
// Precondition: 0 <= index <= size()
public void add(int index, int value) {
if (index == 0) {
// adding to an empty list
front = new ListNode(value, front);
} else {
// inserting into an existing list
ListNode current = front;
for (int i = 0; i < index - 1; i++) {
current = current.next;
}
current.next = new ListNode(value,
current.next);
}
}
40
Conceptual questions
• What is the difference between a LinkedIntList and a
ListNode?
41
Conceptual answers
• A list consists of 0 to many node objects.
– Each node holds a single data element value.
• It's okay that the node fields are public, because client code
never directly interacts with ListNode objects.
43
Removing front element
• Before removing front element:
44
remove solution
// Removes and returns the first value.
// Throws a NoSuchElementException on empty list.
public int remove() {
if (front == null) {
throw new NoSuchElementException();
} else {
int result = front.data;
front = front.next;
return result;
}
}
45
Implementing remove (2)
// Removes value at given index from list.
// Precondition: 0 <= index < size
public void remove(int index) {
...
}
46
Removing from a list
• Before removing element at index 1:
• After:
data next data next
front = 42 20
element 0 element 1
47
Removing from the front
• Before removing element at index 0:
• After:
data next data next
front = -3 20
element 0 element 1
48
Removing the only element
• Before: After:
data next
front = 20 front =
element 0
49
remove (2) solution
// Removes value at given index from list.
// Precondition: 0 <= index < size()
public void remove(int index) {
if (index == 0) {
// special case: removing first element
front = front.next;
} else {
// removing from elsewhere in the list
ListNode current = front;
for (int i = 0; i < index - 1; i++) {
current = current.next;
}
current.next = current.next.next;
}
}
50
Exercise
• Write a method addSorted that accepts an integer value as a
parameter and adds that value to a sorted list in sorted order.
– Before addSorted(17) :
data next data next data next
front =
-4 8 22
element 0 element 1 element 2
– After addSorted(17) :
data next data next data next data next
front =
-4 8 17 22
element 0 element 1 element 2 element 3
51
The common case
• Adding to the middle of a list:
addSorted(17)
52
First attempt
• An incorrect loop:
ListNode current = front;
while (current.data < value) {
current = current.next;
} current
54
Another case to handle
• Adding to the end of a list:
addSorted(42)
55
Multiple loop tests
• A correction to our loop:
ListNode current = front;
while (current.next != null &&
current.next.data < value) {
current = current.next; current
}
57
Handling the front
• Another correction to our code:
if (value <= front.data) {
// insert at front of list
front = new ListNode(value, front);
} else {
// insert in middle of list
ListNode current = front;
while (current.next != null &&
current.next.data < value) {
current = current.next;
}
}
front =
59
Final version of code
// Adds given value to list in sorted order.
// Precondition: Existing elements are sorted
public void addSorted(int value) {
if (front == null || value <= front.data) {
// insert at front of list
front = new ListNode(value, front);
} else {
// insert in middle of list
ListNode current = front;
while (current.next != null &&
current.next.data < value) {
current = current.next;
}
}
}
60
Other list features
• Add the following methods to the LinkedIntList:
– size
– isEmpty
– clear
– toString
– indexOf
– contains
• Add a size field to the list to return its size more efficiently.
– Example:
public class Bicycle implements Vehicle {
...
}
62
Interface requirements
public class Banana implements Shape {
// haha, no methods! pwned
}
63
Interfaces + polymorphism
• Interfaces benefit the client code author the most.
– they allow polymorphism
(the same code can work with different types of objects)
– LinkedIntList
data next data next data next data next
front 42 -3 17 9
70
Why use ADTs?
• Why would we want more than one kind of list, queue, etc.?
– HashSet can search a huge data set for a value in short time;
TreeSet is slower but keeps the set of data in a sorted order.
– You choose the optimal implementation for your task, and if the
rest of your code is written to use the ADT interfaces, it will work.
71
Our list classes
• We implemented the following two list classes:
– ArrayIntList
index 0 1 2
value 42 -3 17
– Problems:
• We should be able to treat them the same way in client code.
• Some of their methods are implemented the same way (redundancy).
• Linked list carries around a clunky extra node class.
• They can store only int elements, not any type of value.
• It is inefficient to get or remove each element of a linked list.
72
Recall: ADT interfaces (11.1)
– Problems:
• We should be able to treat them the same way in client code.
• Some methods are implemented the same way (redundancy).
• Linked list carries around a clunky extra node class.
• They can store only int elements, not any type of value.
• It is inefficient to get or remove each element of a linked list.
75
Common code
• Notice that some of the methods are implemented the same
way in both the array and linked list classes.
– add(value)
– contains
– isEmpty
76
Abstract classes (9.6)
77
Abstract class syntax
// declaring an abstract class
public abstract class name {
...
79
An abstract list class
// Superclass with common code for a list of integers.
public abstract class AbstractIntList implements IntList {
public void add(int value) {
add(size(), value);
}
80
Abstract class vs. interface
• Why do both interfaces and abstract classes exist in Java?
– An abstract class can do everything an interface can do and more.
– So why would someone ever use an interface?
81
Our list classes
• We have implemented the following two list collection classes:
– ArrayIntList
index 0 1 2
value 42 -3 17
– Problems:
• We should be able to treat them the same way in client code.
• Some of their methods are implemented the same way (redundancy).
• Linked list carries around a clunky extra node class.
• They can store only int elements, not any type of value.
• It is inefficient to get or remove each element of a linked list.
82
Inner classes
• inner class: A class defined inside of another class.
– can be created as static or non-static
– we will focus on standard non-static ("nested") inner classes
• usefulness:
– inner classes are hidden from other classes (encapsulated)
– inner objects can access/modify the fields of the outer object
83
Inner class syntax
// outer (enclosing) class
public class name {
...
– Only this file can see the inner class or make objects of it.
– Each inner object is associated with the outer object that created it, so
it can access/modify that outer object's methods/fields.
• If necessary, can refer to outer object as OuterClassName.this
– Problems:
• We should be able to treat them the same way in client code.
• Some of their methods are implemented the same way (redundancy).
• Linked list carries around a clunky extra node class.
• They can store only int elements, not any type of value.
• It is inefficient to get or remove each element of a linked list.
85
Type Parameters (Generics)
ArrayList<Type> name = new ArrayList<Type>();
86
Implementing generics
// a parameterized (generic) class
public class name<Type> {
...
}
– By putting the Type in < >, you are demanding that any client that
constructs your object must supply a type parameter.
• You can require multiple type parameters separated by commas.
– The rest of your class's code can refer to that type by name.
myField = param; // ok
T[] a2 = (T[]) (new Object[10]); // ok
}
}
89
Our list classes
• We have implemented the following two list collection classes:
– ArrayIntList
index 0 1 2
value 42 -3 17
– Problems:
• We should be able to treat them the same way in client code.
• Some of their methods are implemented the same way (redundancy).
• Linked list carries around a clunky extra node class.
• They can store only int elements, not any type of value.
• It is inefficient to get or remove each element of a linked list.
90
Linked list iterator
• The following code is particularly slow on linked lists:
List<Integer> list = new LinkedList<Integer>();
...
for (int i = 0; i < list.size(); i++) {
int value = list.get(i);
if (value % 2 == 1) {
list.remove(i);
}
}
– Why?
– What can we do to improve the runtime?
91
Recall: Iterators (11.1)
• Exercise: Write iterators for our array list and linked list.
– You don't need to support the remove operation.
93
Array list iterator
public class ArrayList<E> extends AbstractIntList<E> {
...
// not perfect; doesn't forbid multiple removes in a row
private class ArrayIterator implements Iterator<E> {
private int index; // current position in list
public ArrayIterator() {
index = 0;
}
public boolean hasNext() {
return index < size();
}
public E next() {
index++;
return get(index - 1);
}
public void remove() {
ArrayList.this.remove(index - 1);
index--;
}
}
}
94
Linked list iterator
public class LinkedList<E> extends AbstractIntList<E> {
...
// not perfect; doesn't support remove
private class LinkedIterator implements Iterator<E> {
private ListNode current; // current position in list
public LinkedIterator() {
current = front;
}
public boolean hasNext() {
return current != null;
}
public E next() {
E result = current.data;
current = current.next;
return result;
}
public void remove() { // not implemented for now
throw new UnsupportedOperationException();
}
}
}
95
for-each loop and Iterable
• Java's collections can be iterated using a "for-each" loop:
List<String> list = new LinkedList<String>();
...
for (String s : list) {
System.out.println(s);
}
– Our collections do not work in this way.
96
Final List interface (15.3, 16.5)
97