20 Java - Queue (Deque), List, LinkedList, Vector, and Stack

Deque

Stands for Double Ended Queue. Means addition and removal can be done from both the sides of the queue.

Methods available in Deque Interface

Queue: add(), offer(), remove(), poll(), element(), peek()

Deque:

Note

  • Using these methods, we can even use Deque to implement STACK (LIFO) and QUEUE (FIFO) both.

  • To use it as Stack push() and pop() methods are available.

    • push(): Internally calls addFirst()

    • pop(): Internally calls removeFirst()

ArrayDeque

ArrayDeque (Concrete Class), implements the methods which are available in Queue and Deque Interface.

public class Main {
    public static void main(String[] args) {

        ArrayDeque<Integer> arrayDequeAsQueue = new ArrayDeque<>();

        //insertion
        arrayDequeAsQueue.addLast(1);
        arrayDequeAsQueue.addLast(5);
        arrayDequeAsQueue.addLast(10);

        //deletion
        int element = arrayDequeAsQueue.removeFirst();
        System.out.println(element);

        //LIFO (Last in First out)
        ArrayDeque<Integer> arrayDequeAsStack = new ArrayDeque<>();
        arrayDequeAsStack.addFirst(1);
        arrayDequeAsStack.addFirst(5);
        arrayDequeAsQueue.addFirst(10);

        //Deletion
        int removedElement = arrayDequeAsQueue.removeFirst();
        System.out.println(removedElement);
    }
}

/*
1
10
*/

Queue methods in ArrayDeque

  • add(): internally clls addLast() method.

  • offer(): calls offerLast() method.

  • remove(): calls removeFirst() method

  • poll(): calls pollFirst() method.

  • element(): calls getFirst() method.

  • peek(): calls peekFirst() method.

Time Complexity

  • Insertion: Amortized (Most of the time or Average) complexity is O(1) except few cases like

    • O(n): When queue size threshold reached and try to insert and element at end or front, then its O(n) as values are copied to new queue with bigger size.
  • Deletion: O(1)

  • Search: O(1)

  • Space Complexity: O(n)

Thread Safe Version of Queues

PriorityQueue

  • Thread Safe: No

  • Maintains Insertion Order: No

  • Null Elements allowed: No

  • Duplicate Elements allowed: Yes

Thread safe version

public class Main {
    public static void main(String[] args) {
        PriorityBlockingQueue<Integer> pq = new PriorityBlockingQueue<>();
        pq.add(5);
        pq.add(2);

        System.out.println(pq.peek());
    }
}
//2

ArrayDeque

  • Thread Safe: No

  • Maintains Insertion Order: Yes

  • Null Elements allowed: No

  • Duplicate Elements allowed: Yes

public class Main {
    public static void main(String[] args) {
        ConcurrentLinkedDeque<Integer> ob = new ConcurrentLinkedDeque<>();
        ob.addFirst(2);
        ob.addLast(1);
        System.out.println(ob.removeLast());
    }
}
//1

List

List is a ordered collection of an objects. In which duplicate values can be stored.

List vs Queue

  • Queue is also a collection of an objects but in queue insertion/removal/access can only happen either at start or end of the queue.

  • While in List, data can be inserted, removed or access from anywhere.

  • In List, user can decide where to insert or access using index (starting from 0)

Methods available in List interface

Methods available in Collection Interface + new Methods defined in List Interface

Collection Methods (12)

12 main methods (link)

size, isEmpty, equals, contains

toArray, clear

add, addAll, remove, removeAll,

stream & parallelStream, Iterator

New Methods defined in List interface (10)

MethodsUsage
add(int index, E element)Insert the element at the specific position in the list. If there is any element present at that postion, it shift it to its Right
addAll(int index, Collection c)Insert all the elements of the specified collection into this list at specific index, and shift the element at this index and subsequent element to the right.
replaceAll(UnaryOperator op)replaces each element of the list, with the result of applying the operator the element.
sort(Comparator c)sort based on the order provided by the comparator
get(int index)Return the element at the specified position in the list.
set(int index, Element e)Replace the element at the specified index in the list with the element provided.
remove(int index)Remove the elemnt from the index and shift subsequent elements to the left
indexOf(Object o)Returns the index of the first occurrence of the specified element in the list. -1 if list does not contains the element
lastIndexOf(Object o)Returns the index of the last ocurrence of the specified element in the list. -1 if list does not contains the element
ListIterator<E> listIterator()return the object of ListIterator.
ListIterator<E> listerator(int index)Start the iterator from the specific index. Specified index indicates the first element that would be returned by an initial call to next.
List<E> sublist(int fromIndex, int toIndex)return the portion of the list. fromIndex - Inclusive, toIndex - Exclusive. If fromIndex == toIndex, return sublist is empty. Any changes in sublist, will change in main list also and viceversa

Example

public class Main {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();

        //add(int index, Element e)
        list1.add(0,100);
        list1.add(1,200);
        list1.add(2,300);

        //addAll(int index, Collection c)
        List<Integer> list2 = new ArrayList<>();
        list2.add(0,400);
        list2.add(1,500);
        list2.add(2,600);

        list1.addAll(2,list2);
        list1.forEach((Integer val) -> System.out.println(val));

        //replaceAll(UnaryOpeartor op)
        list1.replaceAll((Integer val) -> -1*val);
        System.out.println("after replace all");
        list1.forEach((Integer val)-> System.out.println(val));

        //sort(Comparator c)
        list1.sort((Integer val1, Integer val2) -> val1 - val2);
        System.out.println("after sorting in increasing order");
        list1.forEach((Integer val)-> System.out.println(val));

        //get(int index)
        System.out.println("value present at index 2 is:" + list1.get(2));

        //set(int index, Element e)
        list1.set(2, -40000);
        System.out.println("after set method");

        list1.forEach((Integer val)-> System.out.println(val));

        //remove(int index)
        list1.remove(2);
        System.out.println("after removing");
        list1.forEach((Integer val)-> System.out.println(val));

        //indexOf(Object o)
        System.out.println("index of -200 Integer object is: "+ list1.indexOf(-200));

        //need to provide the index in listIterator, from where it has to start.
        ListIterator<Integer> listIterator1 = list1.listIterator(list1.size());

        //traversing backward direction
        while (listIterator1.hasPrevious()){
            int previousVal = listIterator1.previous();
            System.out.println("traversing backward: "+previousVal + " nextIndex: "+ listIterator1.nextIndex() + "previous index: " +listIterator1.previousIndex());
            if (previousVal == -100){
                listIterator1.set(-50);
            }
        }

        //traversing forward direction
        ListIterator<Integer> listIterator2 = list1.listIterator();
        while (listIterator2.hasNext()){
            int val = listIterator2.next();
            System.out.println("traversing forward: "+ val + " nextIndex: "+ listIterator2.nextIndex() + "previous index: "+listIterator2.previousIndex());
            if (val == -200){
                listIterator2.add(-100);
            }
        }

        list1.forEach(val -> System.out.println("after add: "+val));

        List<Integer> subList = list1.subList(1,4);
        subList.forEach(val -> System.out.println("sublist: "+val));

        subList.add(-900);
        list1.forEach(val -> System.out.println("after value added in subList: "+ val));
    }
}
//output
100
200
400
500
600
300
after replace all
-100
-200
-400
-500
-600
-300
after sorting in increasing order
-600
-500
-400
-300
-200
-100
value present at index 2 is:-400
after set method
-600
-500
-40000
-300
-200
-100
after removing
-600
-500
-300
-200
-100
index of -200 Integer object is: 3
after add: -600
after add: -500
after add: -300
after add: -200
after add: -100
sublist: -500
sublist: -300
sublist: -200
after value added in subList: -600
after value added in subList: -500
after value added in subList: -300
after value added in subList: -200
after value added in subList: -900
after value added in subList: -100

ListIterator<E> listIterator() - List method

  • listIterator return the object of ListIterator.

  • ListIterator (interface) extends from the Iterator (interface)

  • It has all the methods which are available in Iterator and helps to iterate in forward direction like:

Method NameUsage
hasNext()Returns true; if there are more elements in collection
next()Returns the next element in the iteration
remove()Removes the last element returned by iterator

Pus the following methods

New methods which are introduced in ListIterator, which help to iterate in backward direction:

Method NameUsage
boolean hasPrevious()Returns true, if there are more elements in list while traversing in reverse order. Else throw exception
E previous()Returns the previous element and move the cursor position backward.
int nextIndex()Returns the index of the next element. (return the size of the list, if its at the end of the list)
int previousIndex()Returns the index of the previous element. (return -1 of the list, if its at the begining of the list)
set(E e)Replaces the last element returned by next or previous with the specified element.
add(E e)Insert the specified element immediately before the element that would be returned by the next and after the element that would be returned by the previous. Subsequent call to next() would be unaffected.

Iterator Explanation

  • The iterator will always be at the edge of the cell. So, iterator.next() gives the value and moves one step further.

  • Similary for iterator.previous()

  • Addition of element using iterator

Example

public class Main {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();

        //add(int index, Element e)
        list1.add(0,-600);
        list1.add(1,-500);
        list1.add(2,-400);
        list1.add(0,-300);
        list1.add(1,-200);
        list1.add(2,-100);

        //need to provide the index in listIterator, from where it has to start.
        ListIterator<Integer> listIterator1 = list1.listIterator(list1.size());

        //traversing backward direction
        while (listIterator1.hasPrevious()){
            int previousVal = listIterator1.previous();
            System.out.println("traversing backward: "+previousVal + " nextIndex: "+ listIterator1.nextIndex() + " previous index: " +listIterator1.previousIndex());
            if (previousVal == -100){
                listIterator1.set(-50);
            }
        }

        //traversing forward direction
        ListIterator<Integer> listIterator2 = list1.listIterator();
        while (listIterator2.hasNext()){
            int val = listIterator2.next();
            System.out.println("traversing forward: "+ val + " nextIndex: "+ listIterator2.nextIndex() + " previous index: "+listIterator2.previousIndex());
            if (val == -200){
                listIterator2.add(-100);
            }
        }
    }
}
traversing backward: -400 nextIndex: 5 previous index: 4
traversing backward: -500 nextIndex: 4 previous index: 3
traversing backward: -600 nextIndex: 3 previous index: 2
traversing backward: -100 nextIndex: 2 previous index: 1
traversing backward: -200 nextIndex: 1 previous index: 0
traversing backward: -300 nextIndex: 0 previous index: -1
traversing forward: -300 nextIndex: 1 previous index: 0
traversing forward: -200 nextIndex: 2 previous index: 1
traversing forward: -50 nextIndex: 4 previous index: 3
traversing forward: -600 nextIndex: 5 previous index: 4
traversing forward: -500 nextIndex: 6 previous index: 5
traversing forward: -400 nextIndex: 7 previous index: 6

Time Complexity

Insertion

  • O(1): when inserting the element at the end of the array. And space is sufficent.

  • O(n): When inserting the element at the particular index of the array, then it require shifting of the values.

  • O(n): When array size threshold reached and try to insert an element at the end, the also its O(n) as values are copied to new array with bigger array size.

Deletion: O(n), we have to shift all the values

Search: O(1)

Thread Safe Version

  • Thread Safe: No

  • Maintains Insertion Order: Yes

  • Null Elements allowed: Yes

  • Duplicate Elements allowed: Yes

public class Main {
    public static void main(String[] args) {
        List<Integer> list = new CopyOnWriteArrayList<>();
        list.add(0,100);
        System.out.println(list.get(0));
    }
}

LinkedList

  • Data structure used is Linked List.

  • LinkedList implements both Deque and List interface.

  • Means it support Deque methoeds like getFirst, getLast, removeFirst, removeLast etc... (plus)

  • It also support index based operations like List: get(index), add(index, object) etc.

public class Main {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();

        //using deque functionality
        list.addLast(200);
        list.addLast(300);
        list.addLast(400);
        list.addLast(100);
        System.out.println(list.getFirst());

        //using list functionality
        LinkedList<Integer> list2 = new LinkedList<>();
        list2.add(0,100);
        list2.add(1,300);
        list2.add(2,400);
        list2.add(1, 200);
        System.out.println(list2.get(1) + " and "+list2.get(2));
    }
}
/*
200
200 and 300
*/

Time Complexity

  • Insertion at start and end: O(1)

  • Insertion at particular index: O(n) for lookup of the index + O(1) for adding

  • Search: O(n)

  • Deletion at start or end: O(1)

  • Deletion at specific index: O(n) for the lookup of the index + O(1) for removal.

Space Complexity - O(n)

Thread Safe Version

  • Thread Safe: No

  • Maintains Insertion Order: Yes

  • Null Elements allowed: Yes

  • Duplicate Elements allowed: Yes

public class Main {
    public static void main(String[] args) {
        List<Integer> list = new CopyOnWriteArrayList<>();
        list.add(0,100);
        System.out.println(list.get(0));
    }
}

Vector

  • Exactly same as ArrayList, elements can be access via index.

  • But its Thread Safe

  • Puts lock when operation is performed on vector.

  • Less efficient then ArrayList as for each operation it do lock/unlock internally.

public class Main {
    public static void main(String[] args) {

        Vector<Integer> obj = new Vector<>();
        obj.add(0, 200);
        System.out.println(obj.get(0));
    }
}

Thread Safe Version

  • Thread Safe: Yes

  • Maintains Insertion Order: Yes

  • Null Elements allowed: Yes

  • Duplicate Elements allowed: Yes

Vector is already thread safe.

Stack

  • Represent LIFO (Last in First out) Operation

  • Since it extend vector, its method is also synchronized.

Stack vs Deque

Deque is not thread safe, stack is thread safe.

Example

public class Main {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        stack.push(1);
        stack.push(2);

        stack.pop(); //removes the top element
        stack.peek(); //Just views the top element
        System.out.println(stack.pop());


    }
}

Time complexity

  • Insertion - O(1)

  • Deletion - O(1)

  • Search - O(n)

Space Complexity - O(n)

Thread Safe Version

  • Thread Safe: Yes

  • Maintains Insertion Order: Yes

  • Null Elements allowed: Yes

  • Duplicate Elements allowed: Yes

Stack is already thread safe.