Doubly-linked lists
Doubly-linked lists; Sorted lists
In a doubly-linked list, each node has a forward pointer to the next
node and a backward pointer to the prior node.Implementation of a circular, doubly-linked list:
public class DoublyLinkedList implements SimpleList
{
private int nitems;
private ListNode sentinel;
private class ListNode {
private Object datum;
private ListNode prior,next;
ListNode(Object datum){
this.datum = datum;
prior = next = null;
}
ListNode(Object datum, ListNode prior, ListNode next){
this.datum = datum;
this.prior = prior;
this.next = next;
}
public Object getDatum(){
return datum;
}
public ListNode getNext(){
return next;
}
public ListNode getPrior(){
return prior;
}
}
DoublyLinkedList()
{
nitems = 0;
sentinel = new ListNode(null);
sentinel.next = sentinel.prior = sentinel;
}
private ListNode getnth(int index)
{
ListNode target = sentinel.next;
if(index<0 || index>=nitems)
throw new IndexOutOfBoundsException();
for(int k=0; k<index; k++)
target = target.next;
return target;
}
public void clear()
{
nitems = 0;
sentinel.next = sentinel.prior = sentinel;
}
public int size()
{
return nitems;
}
public boolean isEmpty()
{
return nitems==0;
}
public boolean add(Object item)
{
ListNode newNode = new ListNode(item,sentinel.prior,sentinel);
sentinel.prior = newNode;
newNode.prior.next = newNode;
++nitems;
return true;
}
public Object get(int index)
{
return getnth(index).datum;
}
public Object set(int index, Object obj)
{
ListNode node = getnth(index);
Object temp = node.datum;
node.datum = obj;
return temp;
}
public Object remove(int index)
{
Object target;
if(index<0 || index>=nitems)
throw new IndexOutOfBoundsException();
else {
ListNode node = getnth(index);
target = node.datum;
node.prior.next = node.next;
node.next.prior = node.prior;
}
--nitems;
return target;
}
public void add(int index, Object item)
{
if(index<0 || index>nitems)
throw new IndexOutOfBoundsException();
else {
ListNode node = getnth(index);
ListNode newNode = new ListNode(item,node.prior,node);
node.prior = newNode;
newNode.prior.next = newNode;
++nitems;
}
}
public Iterator iterator()
{
return new Iterator(){
ListNode current = sentinel.next;
public boolean hasNext(){
return current!=sentinel;
}
public Object next(){
Object o = current.datum;
current = current.next;
return o;
}
public void remove(){
ListNode prior = current.prior;
prior.prior.next = current;
current.prior = prior.prior;
--nitems;
}
};
}
}
Sorted Lists
In the List interface, the user is given methods which allow data to be entered in any desired order. A new data item being added can be added at the beginning or end of the list, or somewhere in the middle. In many cases, however, the data itself has its own natural ordering (e.g. ordering by magnitude for numbers, lexicographic ordering for strings, etc.). In those cases, we may want to design a class that represents a sorted list; that is, a list whose order is always maintained based on the natural ordering of the data. We could use such a class to store a list of names in alphabetical order or a list of numbers in increasing order.Would such a collection be useful?
To begin the design, answer the following questions:
- What properties would a Sorted List have?
- What (if anything) would be different about a Sorted List in comparison with a List?
- What (if any) requirements might be imposed on the objects in a Sorted List?
- We need a way of comparing objects
- Only one version of add is permitted: add in order
- An iterator for Sorted List should return its objects in increasing order
the Comparable interface
For comparing objects, Java defines a Comparable interface, with a single methodint compareTo(Object obj);
It returns 0 if this == obj, an integer value < 0 if this < obj, and an integer value > 0 if this > obj.
Classes in the Java API with a natural ordering, such as Integer, String, etc., implement Comparable. This makes it possible to use objects from any of those classes in a Sorted List. It's a good idea to do the same for classes you write yourself.
example Define an Employee class, ordered by name.
class Employee implements Comparable {
String name;
String ssn;
.
.
.
int compareTo(Object obj) {
if(! (obj instanceof Employee))
throw new IllegalArgumentException();
Employee employee = (Employee) obj;
return name.compareTo(employee.name);
}
}
the SortedList interface
Define the SortedList interfaceinterface SortedList {
boolean add(Object obj);
Iterator iterator();
void clear();
boolean contains(Object obj);
int indexOf(Object obj);
Object get(int i);
boolean isEmpty();
Object remove(int i);
boolean remove(Object obj);
int size();
String toString();
}
Note the differences from List:
No add(int i, Object obj) method
No set(int i, Object obj) method
Question: Which of these methods can be identical to the same methods in List and which cannot?
Implementation
Like List, a SortedList can be implemented with an array, a head-tail linked list, a circular list, a doubly-linked list, or a recursive linked list. We'll consider the add and indexOf methods of these implementations, beginning with SortedArrayList.Consider an array implementation. Assume that a SortedArrayList has an array of Objects called array and an integer variable called nitems.
boolean add(Object obj){
if(!(obj instanceof Comparable))
throw new IllegalArgumentException();
int i = nitems;
while( i > 0 && ((Comparable)obj).compareTo(array[i-1]){
array[i] = array[i-1];
i = i - 1;
}
array[i] = obj;
++nitems;
return true;
}
int indexOf(Object obj){
int left = 0;
int right = nitems -1;
int found = 0;
while(!found && left<=right){
int mid = (left+right)/2;
if(obj.equals(array[mid]))
found = true;
else if (((Comparable)obj).compareTo(array[mid]) < 0)
right = mid -1;
else
left = mid + 1;
}
if(found)
return mid;
else
return -1;
}
This version of the indexOf method is called a Binary Search.
Binary search can also be written recursively, using a helper function that has the array endpoints as parameters:
int indexOf(Object obj){
return indexOf(obj, 0, nitems-1);
}
int indexOf(Object obj, int left, int right){
int result;
if(right-left < 0)
result = -1; // empty array; the item cannot be found
else {
int mid = (left+right)/2; // find the midpoint
if(obj.equals(array[mid]))
result = mid; // if there's a match, we can return without a recursive call
else if (((Comparable)obj).compareTo(array[mid]) < 0)
result = indexOf(obj, left, mid-1); // apply indexOf to the left half of the array
else
result = indexOf(obj, mid+1, right); // apply indexOf to the right half of the array
}
return result;
}
Comparators
Question: What if we want to add objects to a SortedList which do not implement Comparable?
Question: What if we want to put the same objects into two SortedLists, with two different orderings?
These questions can be answered by the use of a Comparator. Comparator is a Java interface which contains methods which are used to compare objects. The interface defines the compare method:
int compare(Object o1, Object o2);compare returns 0 if o1 equals o2, an integer < 0 if o1 < o2, and an integer > 0 if o1 > o2.
A Comparator can be used in an implementation of SortedList, say SortedArrayList, as follows:
- Provide two constructors for SortedArrayList, one which has a Comparator as an argument, one which does not.
- If no Comparator is supplied when the SortedArrayList is created, then it is implied that the objects in the list must implement Comparable, and that the compareTo method will be used to compare them.
- If a Comparator is supplied when the SortedArrayList is created, then it is used in comparing elements.
- The add method uses a Comparator if one is provided, and Comparable.compareTo if it is not.
example Write a Comparator for the Employee class which orders employees by their social security numbers.