ch02 List
ch02 List
Section 2.1
List Interface and ArrayList
Class
An array is an indexed structure
In an indexed structure,
elements may be accessed in any order using subscript values
elements can be accessed in sequence using a loop that
increments the subscript
With the Java Array object, you cannot
increase or decrease its length (length is fixed)
add an element at a specified position without shifting elements
to make room
remove an element at a specified position and keep the elements
contiguous without shifting elements to fill in the gap
List Interface and ArrayList
Class (cont.)
Java provides a List interface as part of its API java.util
Classes that implement the List interface provide the functionality
of an indexed data structure and offer many more operations
A sample of the operations:
Obtain an element at a specified position
Replace an element at a specified position
Find a specified target value
Add an element at either end
Remove an element from either end
Insert or remove an element at any position
Traverse the list structure without managing a subscript
All classes introduced in this chapter support these operations, but
they do not support them with the same degree of efficiency
java.util.List Interface and its
Implementers
List Interface and ArrayList
Class
Unlike the Array data structure, classes that
implement the List interface cannot store primitive
types
Classes must store values as objects
This requires you to wrap primitive types, such an
int and double in object wrappers, in these
cases, Integer and Double
8
ArrayList Class
The simplest class that implements the List interface
An improvement over an array object
Use when:
you will be adding new elements to the end of a list
you need to access elements quickly in any order
ArrayList Class (cont.)
To declare a List “object” whose elements will
reference String objects:
List<String> myList = new ArrayList<String>();
myList.remove(1);
myList.set(2, "Sneezy");
ArrayList Class (cont.)
myList.indexOf("Jumpy");
Section 2.2
Example Application of ArrayList
ArrayList<Integer> someInts = new ArrayList<Integer>();
int[] nums = {5, 7, 2, 15};
for (int i = 0; i < nums.length; i++) {
someInts.add(nums[i]);
}
if (index != -1)
dE = theDirectory.get(index);
else
dE = null;
Implementation of an ArrayList
Class
Section 2.3
Implementing an ArrayList Class
E returnValue = theData[index];
size--;
return returnValue;
}
reallocate Method
Create a new array that is twice the size of the
current array and then copy the contents of the new
array
private void reallocate () {
capacity *= 2;
theData = Arrays.copyOf(theData, capacity);
}
reallocate Method (cont.)
private void reallocate () {
capacity *= 2;
theData = Arrays.copyOf(theData, capacity);
}
T(n) = 3(n – 1) + 3 (n – 2) + … + 3
Factoring out the 3,
3(n – 1 + n – 2 + n – 3 + … + 1)
1 + 2 + … + n – 1 = (n x (n-1))/2
Big-O Example 2 (cont.)
Therefore T(n) = 1.5n2 – 1.5n
When n = 0, the polynomial has the value 0
For values of n > 1, 1.5n2 > 1.5n2 – 1.5n
Therefore T(n) is O(n2) when n0 is 1 and c is 1.5
Big-O Example 2 (cont.)
Symbols Used in Quantifying
Performance
Common Growth Rates
Different Growth Rates
Effects of Different Growth Rates
Algorithms with Exponential and
Factorial Growth Rates
Algorithms with exponential and factorial growth
rates have an effective practical limit on the size of
the problem they can be used to solve
With an O(2n) algorithm, if 100 inputs takes an
hour then,
101 inputs will take 2 hours
105 inputs will take 32 hours
tom.next = dick;
dick.next = harry;
harry.next = sam;
A Single-Linked List Class
Generally, we do not have individual references to
each node.
A SingleLinkedList object has a data field
head, the list head, which references the first list
node
Node<String>
next =
data = "Ann"
The element
added to the list
Implementing SLList.addFirst(E item)
(cont.)
private void addFirst (E item) {
Node<E> temp = new Node<E>(item, head);
head = temp;
size++;
}
Node<String>
next =
data = "Ann"
The element
added to the list
Implementing addAfter(Node<E> node, E
item) (cont.)
Node<String>
The Node parameter
next =
data = "Ann"
Implementing removeAfter(Node<E> node)
(cont.)
private E removeAfter (Node<E> node) {
Node<E> temp = node.next;
if (temp != null) {
node.next = temp.next;
size--;
return temp.data;
} else {
return null;
}
}
Implementing
SLList.removeFirst()
SLList<String> Node<String>
head = next =
data = "Dick"
Node<String>
next =
temp data = "Tom"
Implementing
SLList.removeFirst() (cont.)
private E removeFirst () {
Node<E> temp = head;
if (head != null) {
head = head.next;
}
if (temp != null) {
size--;
return temp.data
} else {
return null;
}
}
Traversing a Single-Linked List
Do something
with nodeRef
Node<String>
nodeRef
next = null
data = "Ann"
Traversing a Single-Linked List (cont.)
toString()can be implemented with a traversal:
public String toString() {
Node<String> nodeRef = head;
StringBuilder result = new StringBuilder();
while (nodeRef != null) {
result.append(nodeRef.data);
if (nodeRef.next != null) {
result.append(" ==> ");
}
nodeRef = nodeRef.next;
}
return result.toString();
}
SLList.getNode(int)
In order to implement methods required by the List
interface, we need an additional helper method:
Node Node
sharon
Node<E> sharon = new Node<E>("Sharon");
sharon.next = sam;
sharon.prev = sam.prev; Node
sam.prev.next = sharon;
sam.prev = sharon next =
= prev
data = "Sharon"
Removing from a Double-Linked List
harry
Node Node
next = next =
= prev = prev
data = "Dick" data = "Sharon"
Node
next =
harry.prev.next = harry.next = prev
harry.next.prev = harry.prev data = "Harry"
A Double-Linked List Class
So far we have worked only
with internal nodes
As with the single-linked class,
it is best to access the internal
nodes with a double-linked list
object
A double-linked list object has data fields:
head (a reference to the first list Node)
tail (a reference to the last list Node)
size
Insertion at either end is O(1); insertion elsewhere is still
O(n)
Circular Lists
Circular double-linked list:
Link last node to the first node, and
Link first node to the last node
Section 2.7
The LinkedList Class
The Iterator
An iterator can be viewed as a moving place marker
that keeps track of the current position in a particular
linked list
An Iterator object for a list starts at the first node
The programmer can move the Iterator by calling its
next method
The Iterator stays on its current list item until it is
needed
An Iterator traverses in O(n) while a list traversal
using get() calls in a linked list is O(n2)
Iterator Interface
The Iterator interface is defined in java.util
The List interface declares the method
iterator which returns an Iterator object
that iterates over the elements of that list
Iterator Interface (cont.)
An Iterator is conceptually between elements; it
does not refer to a particular object at any given
time
Iterator Interface (cont.)
In the following loop, we process all items in
List<Integer> through an Iterator
Iterator limitations
Traverses List only in the forward direction
Provides a remove method, but no add method
You must advance the Iterator using your own loop if
you do not start from the beginning of the list
ListIterator extends Iterator, overcoming
these limitations
ListIterator Interface (cont.)
As with Iterator, ListIterator is conceptually
positioned between elements of the list
ListIterator positions are assigned an index from
0 to size
ListIterator Interface (cont.)
ListIterator Interface (cont.)
Comparison of Iterator and
ListIterator
ListIterator is a subinterface of Iterator
Classes that implement ListIterator must provide
the features of both
Iterator:
Requires fewer methods
Can iterate over more general data structures
count = 0;
for (String nextStr : myList) {
if (target.equals(nextStr)) {
count++;
}
}
Enhanced for Statement (cont.)
In list myList of type LinkedList<Integer>, each
Integer object is automatically unboxed:
sum = 0;
for (int nextInt : myList) {
sum += nextInt;
}
Enhanced for Statement (cont.)
The enhanced for statement also can be used with
arrays, in this case, chars or type char[]
. . .
Add Method
1. Obtain a reference, nodeRef, to the node at position /** Add an item at the specified
index index.
2. Insert a new Node containing obj before the node
referenced by nodeRef @param index The index at
To use a ListIterator object to implement add:
which the object is
to be inserted
1. Obtain an iterator that is positioned just before the Node
at position index @param obj The object to be
2. Insert a new Node containing obj before the Node inserted
currently referenced by this iterator @throws
IndexOutOfBoundsException
if the index is out
of range
(i < 0 || i > size())
*/
public void add(int index, E obj)
{
It is not necessary to declare a listIterator(index).add(obj);
local ListIterator; the }
method call listIterator
returns an anonymous
listIterator object
Get Method
/** Get the element at position
1. Obtain a reference, index.
nodeRef, to the node at @param index Position of
position index item to be retrieved
@return The item at index
2. Return the contents of the
*/
Node referenced by
public E get(int index) {
nodeRef return
listIterator(index).next();
}
Other Add and Get Methods
public void addFirst(E item) {
add(0, item);
}
public E getFirst() {
return head.data;
}
public E getLast() {
return tail.data;
}
Implementing the ListIterator
Interface
KWListIter is an inner class of KWLinkedList
which implements the ListIterator interface
Implementing the ListIterator
Interface (cont.)
Implementing the ListIterator
Interface (cont.)
private class KWListIter implements ListIterator<E> {
private Node <E> nextItem;
private Node <E> lastItemReturned;
private int index = 0;
...
Constructor
public KWListIter(int i) {
// Validate i parameter.
if (i < 0 || i > size) {
throw new IndexOutOfBoundsException("Invalid index " + i);
}
lastItemReturned = null; // No item returned yet.
// Special case of last item
if (i == size) {
index = size;
nextItem = null;
}
else { // Start at the beginning
nextItem = head;
for (index = 0; index < i; index++) {
nextItem = nextItem.next;
}
}
}
The hasNext()Method
tests to see if nextItem is null
public boolean hasnext() {
return nextItem != null;
}
Advancing the Iterator
KWLinkedList Node Node Node
KWListIter
public E next() {
if (!hasNext()) {
nextItem throw new NoSuchElementException();
lastItemReturned }
index 12 lastItemReturned = nextItem;
nextItem = nextItem.next;
index++;
return lastItemReturned.data;
}
Previous Methods
public boolean hasPrevious() {
return (nextItem == null && size != 0)
|| nextItem.prev != null;
}
public E previous() {
if (!hasPrevious()) {
throw new NoSuchElementException();
}
if (nextItem == null) { // Iterator past the last element
nextItem = tail;
}
else {
nextItem = nextItem.prev;
}
lastItemReturned = nextItem;
index--;
return lastItemReturned.data;
}
The Add Method
When adding, there are four cases to address:
Add to an empty list
Add to the head of the list
(after insertion)
if (head == null) {
head = new Node<E>(obj);
tail = head;
}
...
size++
Adding to the Head of the
KWListIter
List
nextItem =
lastItemReturned = null
1
index = 0
Node Node Node
head = null
tail = null
4
size = 3
head = null
tail = null
4
size = 3
Node
else {
Node<E> newNode = new Node<E>(obj); next = null
newNode.prev = nextItem.prev; null = prev
nextItem.prev.next = newNode; data = "Bob"
newNode.next = nextItem;
nextItem.prev = newNode;
} newNode
...
size++;
index++;
Inner Classes: Static and Nonstatic
KWLinkedList contains two inner classes:
Node<E> is declared static: there is no need for it to access
the data fields of its parent class, KWLinkedList
KWListIter cannot be declared static because its methods
access and modify data fields of KWLinkedList’s parent
object which created it
An inner class which is not static contains an implicit
reference to its parent object and can reference the
fields of its parent object
Since its parent class is already defined with the
parament <E>, KWListIter cannot be declared as
KWListIter<E>; if it were, an incompatible types
syntax error would occur
The Collections Framework Design
Section 2.9
The Collection Interface
Specifies a subset of methods in the List interface,
specifically excluding
add(int, E)
get(int)
remove(int)
set(int, E)
but including
add(E)
remove(Object)
iterator()
Extend AbstractList<E>
You need to implement only:
add(int, E)
get(int)
remove(int)
set(int, E)
size()
if (TESTING) {
// Code for output statements
}
Remove these features when you are satisfied with the testing
results
You can define different boolean flags for different tests
Developing the Test Data
In black-box testing, test data should check for all
expected inputs as well as unanticipated data
In white-box testing, test data should be designed
to ensure all combinations of paths through the code
are executed
Testing Boundary Conditions
Example
for (int i = 0; i < x.length; i++) {
if (x[i] == target)
return i;
}
Test the boundary conditions (for white-box and black-
box testing) when target is:
first element (x[0] == target is true)
last element (x[length-1] == target is true)
not in array (x[i] == target is always false)
present multiple times (x[i] == target for more than one
value of i)
Testing Boundary Conditions (cont.)
for (int i = 0; i < x.length; i++) {
if (x[i] == target)
return i;
}
import java.util.*;