First part
Objectives
- Create a doubly linked list containing a dummy node
- Modify the implementation of a linked list to add methods
- Resolve problems that require knowledge of interfaces
- Understand the principle of iterators.
Iterators
Iterators are a data structure that allows us to traverse a collection of elements in order to interact with those elements. An iterator allows us to access an element of a collection, and then access the next element until the collection is empty.
In Java, the interface Iterator has the methods: hasNext(), next() and remove().
-
hasNext() : Returns true if the iterator has a next element
next() : Returns the next element or throws NoSuchElementException if no next element exists
remove() : Removes the element from the collection; has to be called after a the methodnext().
To illustrate the use of an iterator, we will use the class java.util.LinkedList. LinkedList is a linked list that implements the interface iterable, which specifies the method iterator(). The method iterator() returns an object implementing the interface Iterator.
Once we have that iterator, we can go through all the elements in the list al. To do so, we use a while loop with the condition that hasNext() returns true. We can then print every elements of the list.
import java.util.*;
public class IteratorDemo{
public static void main(String args[]) {
// Create an array list
LinkedList<String> al = new LinkedList<String>();
// add elements to the array list
al.add("dog");
al.add("bird");
al.add("fish");
al.add("cat");
al.add("monkey");
al.add("lizard");
// Use iterator to display contents of al
System.out.print("Contents of al: ");
Iterator<String> itr = al.iterator();
while(itr.hasNext()) {
String element = itr.next();
System.out.print(element + " ");
}
}
}
Note that with a basic operator, we cannot modify the elements of the list. However, many implementations of the interface Iterator have additional methods that allow us to modify the elements, and much more. For example, the class ListIterator that implements Iterator has additional methods such as add, hasPrevious, previous, set and many others.
Exercice (not to hand in)
Modify the code above to print only the elements of the list al that precedes the String “cat” in the list (print all elements until you encounter “cat”).
Hint: modify the condition of the while loop.
For each loop
The for each loops act as an iterator, and it allows us to directly act on the elements of the collection (we do not have to use methods such as set())
The syntax for the for each loop is as follows:
for(type elem:collection){
//more code
}
Where :
- type : The type of the elements of the collection
- elem : The name of the variable representing each element in the loop
- collection : The name of the variable of the collection that we what to iterate through.
By default this loop will go through every elements of the collection. However if we want to stop the loop after a certain condition we can use the command break that will immediately exit the loop.
Here is a solution of the exercise 1 above using the for each loop.
import java.util.*;
public class IteratorDemo{
public static void main(String args[]) {
// Create an array list
LinkedList<String> al = new LinkedList<String>();
// add elements to the array list
al.add("dog");
al.add("bird");
al.add("fish");
al.add("cat");
al.add("monkey");
al.add("lizard");
System.out.println("\nSolving using augmented for loop: ");
for(String element:al){
if(element.equals("cat")){
break;
}
System.out.print(element + " ");
}
}
}
Circular queue
Circular queues are a type of queues that implements the principle of First In First Out (FIFO). In these queues, the last element is linked back to the first in order to create a loop. These queues use one instance variable for the head of the queue and one for the tail of the queue.
When we add an element using enqueue(), we add the element at the position of the tail and increment the pointer tail. When we remove an element using dequeue(), we remove and return the element at the position of the head pointer and increase that pointer.
Here is a visual representation of a circular queue and its use. We can represent it via an array where the last element links to the first, or like a circular array.
Figure 1 : Visual representation of a circular queue.
Singly and doubly linked list
Reminder: we have seen in class that there are several implementations for a list using linked elements. We have discussed singly- and doubly-linked list. These implementations, however similar they might be, have some important differences.
Singly-linked lists link each elements to the next one in the list, up to the last element, whose next is null.
Doubly-linked lists allow faster implementation of some methods thanks to the link between each element and the previous one. The next of the last element will be null just like the prev of the first element will be null.
It is also possible to implement a (doubly- or singly-)linked list using a dummy node. A dummy node has a null value and is placed at the beginning of the list. This allows for a simpler implementation of the methods by reducing the number of special cases and allows the list to be circular since the first element is linked to the last one via the dummy node.
Test your knowledge of the different implementations.
Question 1.0 :
Describe the following image. Particularly, is this a singly or doubly linked list? What is this technique called ( a … node)?
See imageQuestion 1.1 :
True or false? It is possible go through a singly linked list in both directions efficiently.
AnswerQuestion 1.3 :
True or false? A circular list using a dummy node will have many special cases.
AnswerQuestion 1.4 :
In what cases is the use of a list implemented using an array more efficient than with a list using singly-linked elements? (name 2 methods).
AnswerQuestion 1.7 :
Here is the constructor of the nested class node for a list. Is the list singly or doubly linked?
//constructor
private Node(T value, Node next) {
this.value = value;
this.next = next;
}
AnswerQuestion 1.8 :
True or false? The use of a list with linked elements has an advantage compared to an array because there is no maximum number of elements in the list.
AnswerQuestion 1.9 :
What does the following code do in a circular doubly-linked list? Consider that the variable current was previously declared.
current = head.prev;
while (current != head) {
current = current.prev;
}
Answer