Week5

Lists and queues

In this section we will consider two subtypes of the Collection interface that model data that is stored sequentially. Remember that the semantics (the meaning) of the Collection interface only states that something is a container of multiple objects, but doesn't give any information on how these objects are organized. Subtypes of Collection give more details on the organization of objects. The most common, and perhaps, intuitive way to organize objects is to treat them as sequential data. Lists, arrays, queues, vectors and series are all examples of concepts that imply organization in a sequential way.

In this part we consider two subtypes of Collection that further specify how the data is organized: Deque and List.

Deque<E> interface

The Double Ended Queue, Deque in short, extends the Collection interface via the Queue interface (which we won’t discuss for the sake of brevity). It allows us to store and remove elements at both the front and the back of the queue, and the insertion order of elements matters.

Deque is a subtype of the Collection interface, and defines the following additional methods:

void addFirst(E arg0);
void addLast(E arg0);
E getFirst();
E getLast();
E removeFirst();
E removeLast();

Note that if we insert elements at the front of the queue and remove them at the end, they have a first in, first out (FIFO) order. For example:

Deque<String> q = new ArrayDeque<>();
q.addFirst("Hello");
q.addFirst("World");
System.out.println(q.removeLast());
System.out.println(q.removeLast());

will print

Sample output

Hello World

If we add and remove at the same end of the queue, we get what is called a last in, first out (LIFO) order:

Dequeue<String> q = new ArrayDeque<>();
q.addFirst("Hello");
q.addFirst("World");
System.out.println(q.removeFirst());
System.out.println(q.removeFirst());
Sample output

World Hello

As you can see, the order in which items are inserted into the Deque matters. However, the Deque interface only allows us to interact with the front or the end of the queue. We do not have any methods that allow us to access a particular element at a certain index in the middle of the queue. Indices are not part of the semantics, the meaning, of the Deque interface. If we do want to be able to work with indices, we can use data structures that implement the List interface.

List<E>

The List<E> interface also extends the Collection<E> interface. The semantics, or meaning, of the List interface is that a list organizes its data sequentially, and also states that elements in the list have indices (starting at 0 for the first element). Elements of a List are indexed by integers starting at 0 and ending at size()-1. It adds the following methods :

boolean add(int arg0, E arg1);
E get(int arg0);
int indexOf(Object arg0);
E remove(int arg0);
List <E> subList(int arg0, int arg1);
E set(int arg0, E arg1);

As you can see, all these methods involve some int that refers to an index. While you can just perform add without an index on a List, that particular method actually comes from the Collection interface and is not specific to List types. However, inserting an item at a specific index, or retrieving an item stored a specific index can not be performed with all types of Collection, and therefore, these methods are only included in List types.

ArrayList<E> class

The List you are most familiar with is the ArrayList. It implements the List<E> interface. As a result it implements all methods in List<E> and its super interfaces Collection<E> and Iterable<E>. Two of its constructors are:

public ArrayList() { ... }
public ArrayList(Collection<? extends E> c) { ... }

Under the hood it uses an array, which makes it very efficient to obtain an element at a random index. However, adding elements to the front of an ArrayList<E> requires it to shift all elements by one position, which may take a lot of time if the ArrayList is very long.

Another point to be aware of is that the ArrayList does not implement the Deque interface, and therefore does not provide methods such as addFirst and removeFirst.

LinkedList<E> class

A different type of List is the LinkedList. It stores every element in its own object, which keeps a reference to the objects associated with the next and previous elements. if no next or previous element exists, it keeps a null reference. It implements both the List<E> interface and the Deque<E> interface. Thus, contrary to the ArrayList, it does implement Deque<E> methods such as addFirst and removeFirst.

It has two constructors:

public LinkedList() { ... }
public LinkedList(Collection<? extends E> c) { ... }

Due to the way it works, adding and removing elements at the front or the end of the list (or in fact, anywhere in the list) is very efficient, but accessing an element at a fixed index requires it to walk over the elements in the list. Since it needs to create objects for every element it is often slower than ArrayList for many list-related applications.

The description of the diagram that depicts an example of a linked list, is directly under the picture.

This figure shows schematically how a LinkedList with four Integer objects is organized in the computer's memory. The LinkedList object contains a reference to the first and last element objects of the list and also holds an integer that represents its size. Each object contains a reference to its previous and next object and the value it contains. The reference of the first object to its previous object is null, because it is the first object. The next reference of the last object is also null, because no next object exists. If another element would be added, another element object would be created, and the last (or first) node could update the next/previous references.

Collection hierarchy with List objects

Let us finally have a look at the Collections hierarchy until now, which we will update during the rest of this section.

The diagram shows graphically which interfaces inherit each other, as was already described in the text.
Exercise

Test your knowledge

In this quiz, you can test your knowledge on the subjects covered in this chapter.

Do all classes that implement the List interface also implement the Deque interface?


Suppose we have a Deque<String> stored in a variable myQueue. How can you obtain the third element from the start? Can you make sure that myQueue is in it's original state if you are done?

Example: if the queue contains ["first", "second", "third", ...] you should retrieve "third".


The Collection interface has an add method with a single argument, whereas the List interface has an add method with two arguments. Can you call both methods on a List object? What is the difference between the two methods?


What is the difference between a LinkedList and an ArrayList?

You have reached the end of this section! Continue to the next section: