20 Java - Queue (Deque), List, LinkedList, Vector, and Stack
Table of contents
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() methodpoll()
: 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)
Methods | Usage |
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 Name | Usage |
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 Name | Usage |
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.
Subscribe to my newsletter
Read articles from Chetan Datta directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Chetan Datta
Chetan Datta
I'm someone deeply engrossed in the world of software developement, and I find joy in sharing my thoughts and insights on various topics. You can explore my exclusive content here, where I meticulously document all things tech-related that spark my curiosity. Stay connected for my latest discoveries and observations.