Java Collections Framework: List: ArrayList, LinkedList, Set: HashSet, TreeSet,
Map: HashMap, TreeMap, Queue: PriorityQueue.
Generics: Generic Classes and Methods.
Exploring Java Collections: A
Guide to Lists, Sets, Queues,
and Maps
Collections are a fundamental part of the Java Collections
Framework, which provides a unified architecture for
storing and manipulating groups of objects. The framework
includes various interfaces and classes that allow you to
work with different types of collections, such as lists, sets,
queues, and maps. Here’s an overview of the main
components:
Press enter or click to view image in full size
1. Collection : The root interface of the collection
hierarchy. It defines the most common methods for
collections, such as add, remove, contains, size,
and iterator.
2. List : An ordered collection (also known as a
sequence). It allows duplicate elements and provides
positional access and insertion of elements.
ArrayList : Resizable-array implementation of
the List interface.
LinkedList : Doubly-linked list implementation of
the List interface.
Vector : Synchronized resizable-array implementation
of the List interface
3. Set : A collection that does not allow duplicate
elements.
HashSet : Implementation of the Set interface backed
by a hash table.
LinkedHashSet : Hash table and linked list
implementation of the Set interface, with predictable
iteration order.
TreeSet : Implementation of the Set interface that
uses a tree for storage, providing sorted order.
4. Queue : A collection designed for holding elements
prior to processing.
PriorityQueue : An unbounded queue based on a
priority heap.
LinkedList : Can be used as a queue.
5. Deque : A linear collection that supports element
insertion and removal at both ends.
ArrayDeque : Resizable-array implementation of
the Deque interface.
LinkedList : Can be used as a deque
6. Map : An object that maps keys to values. A Map cannot
contain duplicate keys; each key can map to at most one
value.
HashMap : Implementation of the Map interface
backed by a hash table.
LinkedHashMap : Hash table and linked list
implementation of the Map interface, with predictable
iteration order.
TreeMap : Implementation of the Map interface that
uses a tree for storage, providing sorted order.
Hashtable : Synchronized implementation of
the Map interface.
ArrayList
ArrayList is a part of the Java Collections Framework and
is found in the java.util package. It is a resizable
array implementation of the List interface. Unlike arrays,
which have a fixed size, ArrayList can grow and shrink
dynamically as elements are added or removed.
Key Characteristics:
1. Dynamic Size: Unlike arrays, ArrayList can
automatically resize itself when elements are added
or removed.
2. Order of Elements: It maintains the order of
insertion, meaning elements are stored in the order
in which they are added.
3. Random Access: Elements can be accessed directly
using indexes, offering fast random access to
elements.
4. Null Values: ArrayList allows null values.
5. Not Thread-Safe: It is not synchronized, meaning
multiple threads modifying
the ArrayList simultaneously may lead to
unpredictable results unless explicitly synchronized.
Basic Syntax:
import java.util.ArrayList;
ArrayList<Type> list = new ArrayList<>();
Commonly Used Constructors:
ArrayList(): Creates an empty list with an initial
capacity of 10.
ArrayList(Collection<? extends E> c): Creates a list
initialized with the elements of the given collection.
ArrayList(int initialCapacity): Creates an empty list with
the specified initial capacity.
Methods in ArrayList
Below is a list of commonly used methods in ArrayList:
add(E e): Appends the specified element to the end of
this list.
add(int index, E element): Inserts the specified element at
the specified position in this list.
get(int index): Returns the element at the specified
position in this list.
set(int index, E element): Replaces the element at the
specified position in this list with the specified
element.
remove(int index): Removes the element at the specified
position in this list.
remove(Object o): Removes the first occurrence of the
specified element from this list.
clear(): Removes all of the elements from this list.
contains(Object o): Returns true if this list contains the
specified element.
indexOf(Object o): Returns the index of the first
occurrence of the specified element.
lastIndexOf(Object o): Returns the index of the last
occurrence of the specified element.
isEmpty(): Returns true if this list contains no elements.
size(): Returns the number of elements in this list.
toArray(): Returns an array containing all of the
elements in this list.
subList(int fromIndex, int toIndex): Returns a view of the
portion of this list between fromIndex, inclusive,
and toIndex, exclusive.
sort(Comparator<? super E> c): Sorts the list according to
the order induced by the specified comparator.
binarySearch(List<? extends T> list, T key): Searches the
specified list for the specified object using the binary
search algorithm (requires the list to be sorted first).
iterator(): Returns an iterator over the elements in this
list.
listIterator(): Returns a list iterator over the elements in
this list.
stream(): Returns a sequential stream with this list as
its source.
forEach(Consumer<? super E> action): Performs the given
action for each element of the list.
addAll(Collection<? extends E> c): Appends all of the
elements in the specified collection to the end of this
list.
removeAll(Collection<?> c): Removes from this list all of
its elements that are contained in the specified
collection.
retainAll(Collection<?> c): Retains only the elements in
this list that are contained in the specified collection.
removeIf(Predicate<? super E> filter): Removes all
elements of this list that satisfy the given predicate.
Example Usage of ArrayList
import java.util.ArrayList;
public class ArrayListExample {
public static void main(String[] args) {
// Creating an ArrayList of String type
ArrayList<String> fruits = new ArrayList<>();
// Adding elements to the ArrayList
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Mango");
System.out.println("Fruits List: " + fruits);
// Adding an element at a specific index
fruits.add(1, "Orange");
System.out.println("After adding Orange at index 1: " + fruits);
// Accessing an element at a specific index
String fruit = fruits.get(2);
System.out.println("Element at index 2: " + fruit);
// Removing an element from the ArrayList
fruits.remove("Banana");
System.out.println("After removing Banana: " + fruits);
// Finding the index of an element
int index = fruits.indexOf("Mango");
System.out.println("Index of Mango: " + index);
// Checking if an element exists
boolean containsApple = fruits.contains("Apple");
System.out.println("Does the list contain Apple? " +
containsApple);
// Replacing an element
fruits.set(2, "Pineapple");
System.out.println("After replacing Mango with Pineapple: " +
fruits);
// Getting the size of the ArrayList
int size = fruits.size();
System.out.println("Size of the list: " + size);
// Checking if the list is empty
boolean isEmpty = fruits.isEmpty();
System.out.println("Is the list empty? " + isEmpty);
// Clearing the list
fruits.clear();
System.out.println("List after clearing: " + fruits);
}
}
OUTPUT :
Fruits List: [Apple, Banana, Mango]
After adding Orange at index 1: [Apple, Orange, Banana, Mango]
Element at index 2: Banana
After removing Banana: [Apple, Orange, Mango]
Index of Mango: 2
Does the list contain Apple? true
After replacing Mango with Pineapple: [Apple, Orange, Pineapple]
Size of the list: 3
Is the list empty? false
List after clearing: []
LinkedList
LinkedList is a part of the Java Collections
Framework and is found in the java.util package. It
implements the List and Deque interfaces, providing
both list and queue operations. Unlike ArrayList, which is
based on a dynamic array, LinkedList is implemented using
a doubly linked list. This means that each element (node)
in the list contains a reference to both its previous and
next elements. This structure allows for efficient insertion
and deletion operations, particularly when dealing with
large lists.
Key Characteristics:
1. Doubly Linked List: Each element (node) contains
pointers to both the previous and next nodes. This
allows for efficient insertion and removal operations
at both ends (head and tail).
2. Slower Random Access: Unlike ArrayList, which
allows constant-time access via
index, LinkedList requires traversal from the head or
tail to reach a specific element, making random
access slower.
3. Efficient Insertions and Deletions: Adding or
removing elements from the beginning or end of the
list is efficient, as it only involves updating a few
pointers.
4. Implements Queue and Deque
Interfaces: LinkedList can be used as both a list
(like ArrayList) and a double-ended queue (deque),
meaning you can perform operations at both ends of
the list.
5. Non-Synchronized: Not thread-safe, so you need to
handle synchronization manually if used in a multi-
threaded environment.
6. Allows Duplicates : You can have multiple elements
with the same value.
Basic Syntax:
import java.util.LinkedList;
LinkedList<Type> list = new LinkedList<>();
Commonly Used Constructors:
LinkedList(): Creates an empty linked list.
LinkedList(Collection<? extends E> c): Creates a linked list
initialized with the elements of the given collection.
Methods in LinkedList
Below is a list of commonly used methods in LinkedList,
including methods inherited from List and Deque:
1. add(E e): Adds the specified element to the end of the
list.
2. add(int index, E element): Inserts the specified element
at the specified position in the list.
3. addAll(Collection<? extends E> c): Appends all the
elements in the specified collection to the end of the
list.
4. addFirst(E e): Inserts the specified element at the
beginning of the list.
5. addLast(E e): Appends the specified element to the end
of the list.
6. clear(): Removes all elements from the list.
7. contains(Object o): Returns true if the list contains the
specified element.
8. get(int index): Returns the element at the specified
index.
9. getFirst(): Returns the first element in the list.
10. getLast(): Returns the last element in the list.
11. remove(int index): Removes the element at the
specified index.
12. remove(Object o): Removes the first occurrence of
the specified element.
13. removeFirst(): Removes and returns the first
element of the list.
14. removeLast(): Removes and returns the last
element of the list.
15. size(): Returns the number of elements in the list.
16. isEmpty(): Returns true if the list is empty.
17. indexOf(Object o): Returns the index of the first
occurrence of the specified element.
18. lastIndexOf(Object o): Returns the index of the last
occurrence of the specified element.
19. peek(): Retrieves, but does not remove, the first
element of the list.
20. poll(): Retrieves and removes the first element of
the list, or returns null if empty.
21. offer(E e): Adds the specified element as the last
element (queue operation).
22. toArray(): Converts the LinkedList into an array of
objects.
Example Usage of LinkedList
import java.util.LinkedList;
public class LinkedListExample {
public static void main(String[] args) {
// Creating a LinkedList of String type
LinkedList<String> cities = new LinkedList<>();
// Adding elements to the LinkedList
cities.add("New York");
cities.add("London");
cities.add("Paris");
System.out.println("Cities List: " + cities);
// Adding elements at the beginning and end
cities.addFirst("Tokyo");
cities.addLast("Berlin");
System.out.println("After adding Tokyo and Berlin: " + cities);
// Accessing the first and last elements
String firstCity = cities.getFirst();
String lastCity = cities.getLast();
System.out.println("First City: " + firstCity);
System.out.println("Last City: " + lastCity);
// Removing elements from the LinkedList
cities.removeFirst(); // Removes "Tokyo"
cities.removeLast(); // Removes "Berlin"
System.out.println("After removing first and last cities: " +
cities);
// Retrieving and removing the first element (queue operation)
String polledCity = cities.poll();
System.out.println("Polled City: " + polledCity);
System.out.println("After polling: " + cities);
// Checking if the list contains an element
boolean containsLondon = cities.contains("London");
System.out.println("Does the list contain London? " +
containsLondon);
// Converting the LinkedList to an array
Object[] citiesArray = cities.toArray();
System.out.print("Cities array: ");
for (Object city : citiesArray) {
System.out.print(city + " ");
}
}
}
Output :
Cities List: [New York, London, Paris]
After adding Tokyo and Berlin: [Tokyo, New York, London, Paris, Berlin]
First City: Tokyo
Last City: Berlin
After removing first and last cities: [New York, London, Paris]
Polled City: New York
After polling: [London, Paris]
Does the list contain London? true
Cities array: London Paris
When to Use LinkedList vs ArrayList
Use LinkedList when:
You need fast insertions and deletions at the
beginning, middle, or end of the list.
You want to implement a queue or deque.
Use ArrayList when:
You need fast random access to elements via index.
You don’t expect many insertions or deletions,
especially in the middle of the list.
Vector
Vector is a legacy class in Java that is part of
the java.util package. It was introduced before the Java
Collections Framework (JCF) and has since been
retrofitted to implement the List interface. Vector is similar
to ArrayList. It is a resizable array that can grow as needed
to accommodate new elements. but with some important
differences, primarily in thread safety.
Key Characteristics:
1. Synchronized: Vector is synchronized, meaning it
is thread-safe. Multiple threads can access it
concurrently without causing issues. However, this
synchronization introduces a performance overhead,
making it slower compared to ArrayList for single-
threaded operations.
2. Dynamic Array: Like ArrayList, Vector uses a dynamic
array to store elements. It grows automatically when
it reaches its capacity.
3. Legacy Class: Even though Vector implements List,
it's considered a legacy class and is generally
replaced by ArrayList in modern applications unless
thread safety is required.
Basic Syntax:
import java.util.Vector;
Vector<Type> vector = new Vector<>();
Commonly Used Constructors:
Vector(): Creates an empty vector with an initial
capacity of 10.
Vector(int initialCapacity): Creates a vector with the
specified initial capacity.
Vector(int initialCapacity, int capacityIncrement): Creates a
vector with the specified initial capacity and capacity
increment.
Vector(Collection<? extends E> c): Creates a vector
containing the elements of the specified collection.
Methods in Vector
Below are commonly used methods of the Vector class:
1. add(E e): Adds the specified element to the end of the
vector.
2. add(int index, E element): Inserts the specified element
at the specified position.
3. addAll(Collection<? extends E> c): Adds all elements from
the specified collection to the end of the vector.
4. capacity(): Returns the current capacity of the vector.
5. clear(): Removes all elements from the vector.
6. contains(Object o): Returns true if the vector contains
the specified element.
7. elementAt(int index): Returns the element at the
specified index.
8. firstElement(): Returns the first element of the vector.
9. lastElement(): Returns the last element of the vector.
10. get(int index): Returns the element at the specified
index.
11. remove(int index): Removes the element at the
specified index.
12. remove(Object o): Removes the first occurrence of
the specified element.
13. removeAllElements(): Removes all elements from the
vector (same as clear()).
14. isEmpty(): Returns true if the vector contains no
elements.
15. size(): Returns the number of elements in the
vector.
16. trimToSize(): Trims the capacity of the vector to
the current size.
17. indexOf(Object o): Returns the index of the first
occurrence of the specified element.
18. lastIndexOf(Object o): Returns the index of the last
occurrence of the specified element.
19. toArray(): Converts the vector to an array of
objects.
20. clone(): Creates a copy of the vector.
21. set(int index, E element): Replaces the element at
the specified position with the specified element.
22. ensureCapacity(int minCapacity): Increases the
capacity of the vector if necessary to ensure it can
hold at least the specified number of elements.
Example Usage of Vector
import java.util.Vector;
public class VectorExample {
public static void main(String[] args) {
// Creating a Vector of String type
Vector<String> vector = new Vector<>();
// Adding elements to the Vector
vector.add("Apple");
vector.add("Banana");
vector.add("Cherry");
System.out.println("Vector: " + vector);
// Adding an element at a specific index
vector.add(1, "Mango");
System.out.println("After adding Mango at index 1: " + vector);
// Accessing an element at a specific index
String element = vector.get(2);
System.out.println("Element at index 2: " + element);
// Checking the first and last elements
String firstElement = vector.firstElement();
String lastElement = vector.lastElement();
System.out.println("First Element: " + firstElement);
System.out.println("Last Element: " + lastElement);
// Removing an element
vector.remove("Banana");
System.out.println("After removing Banana: " + vector);
// Checking the size and capacity of the Vector
int size = vector.size();
int capacity = vector.capacity();
System.out.println("Size of the vector: " + size);
System.out.println("Capacity of the vector: " + capacity);
// Trimming the capacity to the current size
vector.trimToSize();
System.out.println("Capacity after trim: " + vector.capacity());
}
}
Output :
Vector: [Apple, Banana, Cherry]
After adding Mango at index 1: [Apple, Mango, Banana, Cherry]
Element at index 2: Banana
First Element: Apple
Last Element: Cherry
After removing Banana: [Apple, Mango, Cherry]
Size of the vector: 3
Capacity of the vector: 10
Capacity after trim: 3
When to Use Vector vs ArrayList
Use Vector when:
You need a thread-safe collection without explicit
synchronization.
You require a dynamic array but need it to be
synchronized by default.
Use ArrayList when:
You don’t need thread safety.
You prefer a higher-performance dynamic array in
single-threaded scenarios.
HashSet
HashSet is part of the Java Collections Framework and
implements the Set interface. It is backed by a hash
table (technically a HashMap instance) and is used to store
elements in a collection that does not allow duplicates.
Key Characteristics:
1. No Duplicates: A HashSet does not allow duplicate
elements. If you attempt to add a duplicate element,
it will not be added.
2. Unordered: The elements in a HashSet are not
ordered. The order in which elements are inserted is
not maintained.
3. Null Values: HashSet allows one null value.
4. Fast Operations: The operations like adding,
removing, and checking elements happen in constant
time (O(1)), assuming there are no hash collisions.
5. Non-Synchronized: HashSet is not synchronized, so it
is not thread-safe. For thread-safe operations, you
can use Collections.synchronizedSet(new HashSet<E>()).
Basic Syntax:
import java.util.HashSet;
HashSet<Type> set = new HashSet<>();
Commonly Used Constructors:
HashSet(): Creates an empty set with an initial capacity
of 16 and load factor of 0.75.
HashSet(int initialCapacity): Creates an empty set with
the specified initial capacity.
HashSet(int initialCapacity, float loadFactor): Creates a set
with the specified initial capacity and load factor.
HashSet(Collection<? extends E> c): Creates a set
containing the elements of the specified collection.
Methods in HashSet
1. add(E e): Adds the specified element to the set if it is
not already present.
2. addAll(Collection<? extends E> c): Adds all of the elements
in the specified collection to the set.
3. clear(): Removes all elements from the set.
4. contains(Object o): Returns true if the set contains the
specified element.
5. isEmpty(): Returns true if the set contains no elements.
6. remove(Object o): Removes the specified element from
the set if it is present.
7. size(): Returns the number of elements in the set.
8. iterator(): Returns an iterator over the elements in the
set.
9. toArray(): Returns an array containing all of the
elements in the set.
10. retainAll(Collection<?> c): Retains only the elements
in the set that are contained in the specified
collection (intersection of sets).
11. removeAll(Collection<?> c): Removes from the set all
of its elements that are contained in the specified
collection.
12. clone(): Returns a shallow copy of the HashSet.
Example Usage of HashSet
import java.util.HashSet;
public class HashSetExample {
public static void main(String[] args) {
// Creating a HashSet of String type
HashSet<String> set = new HashSet<>();
// Adding elements to the HashSet
set.add("Apple");
set.add("Banana");
set.add("Cherry");
System.out.println("HashSet: " + set);
// Trying to add a duplicate element
boolean isAdded = set.add("Apple");
System.out.println("Is 'Apple' added again? " + isAdded);
// Checking if an element exists in the HashSet
boolean containsCherry = set.contains("Cherry");
System.out.println("Does the set contain 'Cherry'? " +
containsCherry);
// Removing an element from the HashSet
set.remove("Banana");
System.out.println("After removing 'Banana': " + set);
// Getting the size of the HashSet
int size = set.size();
System.out.println("Size of the HashSet: " + size);
// Iterating over the elements of the HashSet
System.out.println("Elements in the HashSet:");
for (String element : set) {
System.out.println(element);
}
}
}
Output:
HashSet: [Apple, Banana, Cherry]
Is 'Apple' added again? false
Does the set contain 'Cherry'? true
After removing 'Banana': [Apple, Cherry]
Size of the HashSet: 2
Elements in the HashSet:
Apple
Cherry
When to Use HashSet
1. No Duplicates: Use HashSet when you want to ensure
no duplicate elements are stored in the collection.
2. Fast Operations: Since HashSet is based on a hash
table, the operations are very fast with constant time
complexity (O(1)) for basic operations like add,
remove, and contains.
3. Unordered Collection: If you don’t care about the
order of elements, HashSet is a great choice.
LinkedHashSet
A LinkedHashSet in Java is a part of the Java Collections
Framework and is a combination of a HashSet and
a LinkedList. It maintains a doubly linked list running
through all of its entries, which allows it to preserve the
insertion order of elements. Like HashSet, LinkedHashSet does
not allow duplicate elements and provides constant-time
performance for basic operations such as adding,
removing, and checking for the presence of elements.
Key Characteristics:
1. No Duplicates: Like HashSet, LinkedHashSet does not
allow duplicate elements.
2. Preserves Insertion Order: Unlike HashSet, which
does not guarantee any order, LinkedHashSet maintains
the insertion order of elements.
3. Null Elements: LinkedHashSet allows a
single null element.
4. Moderate Performance: While LinkedHashSet is
slightly slower than HashSet (due to maintaining the
linked list), its performance is still fast for basic
operations like add(), remove(), and contains().
5. Non-Synchronized: LinkedHashSet is not synchronized,
so it is not thread-safe. For thread-safe operations,
you can use Collections.synchronizedSet(new
LinkedHashSet<E>()).
Basic Syntax:
import java.util.LinkedHashSet;
LinkedHashSet<Type> set = new LinkedHashSet<>();
Commonly Used Constructors:
LinkedHashSet(): Creates an empty linked hash set with
an initial capacity of 16 and a load factor of 0.75.
LinkedHashSet(int initialCapacity): Creates a linked hash
set with the specified initial capacity.
LinkedHashSet(int initialCapacity, float loadFactor): Creates a
linked hash set with the specified initial capacity and
load factor.
LinkedHashSet(Collection<? extends E> c): Creates a linked
hash set containing the elements of the specified
collection.
Methods in LinkedHashSet
Since LinkedHashSet extends HashSet, it inherits all the
methods from HashSet. Here are the most commonly used
ones:
1. add(E e): Adds the specified element to the set if it is
not already present.
2. addAll(Collection<? extends E> c): Adds all the elements
from the specified collection to this set.
3. clear(): Removes all the elements from the set.
4. contains(Object o): Returns true if the set contains the
specified element.
5. remove(Object o): Removes the specified element from
the set if it is present.
6. size(): Returns the number of elements in the set.
7. isEmpty(): Returns true if the set contains no elements.
8. iterator(): Returns an iterator over the elements in the
set.
9. toArray(): Returns an array containing all of the
elements in the set.
10. retainAll(Collection<?> c): Retains only the elements
in the set that are contained in the specified
collection.
11. removeAll(Collection<?> c): Removes from the set all
of its elements that are contained in the specified
collection.
12. clone(): Returns a shallow copy of the LinkedHashSet.
Example Usage of LinkedHashSet
import java.util.LinkedHashSet;
public class LinkedHashSetExample {
public static void main(String[] args) {
// Creating a LinkedHashSet of String type
LinkedHashSet<String> set = new LinkedHashSet<>();
// Adding elements to the LinkedHashSet
set.add("Apple");
set.add("Banana");
set.add("Cherry");
System.out.println("LinkedHashSet: " + set);
// Trying to add a duplicate element
boolean isAdded = set.add("Apple");
System.out.println("Is 'Apple' added again? " + isAdded);
// Checking if an element exists in the LinkedHashSet
boolean containsCherry = set.contains("Cherry");
System.out.println("Does the set contain 'Cherry'? " +
containsCherry);
// Removing an element from the LinkedHashSet
set.remove("Banana");
System.out.println("After removing 'Banana': " + set);
// Getting the size of the LinkedHashSet
int size = set.size();
System.out.println("Size of the LinkedHashSet: " + size);
// Iterating over the elements of the LinkedHashSet
System.out.println("Elements in the LinkedHashSet:");
for (String element : set) {
System.out.println(element);
}
}
}
Output:
LinkedHashSet: [Apple, Banana, Cherry]
Is 'Apple' added again? false
Does the set contain 'Cherry'? true
After removing 'Banana': [Apple, Cherry]
Size of the LinkedHashSet: 2
Elements in the LinkedHashSet:
Apple
Cherry
When to Use LinkedHashSet
1. Preserving Insertion Order: Use LinkedHashSet when
you need a collection that maintains the insertion
order of elements. This is useful when you need
unique elements but also care about the order in
which they are processed.
2. No Duplicates: Like all sets, LinkedHashSet ensures
that there are no duplicate elements.
3. Moderate Performance: Though it is slightly slower
than HashSet due to the linked list overhead, it still
provides constant-time performance for basic
operations.
TreeSet
TreeSet is a class in Java that implements the Set interface
and is part of the Java Collections Framework. It is
backed by a TreeMap and stores elements in a sorted
and ascending order by default.
Unlike HashSet or LinkedHashSet, TreeSet ensures that the
elements are sorted in natural order or by a specified
comparator.
Key Characteristics:
1. Sorted Order: Elements in a TreeSet are always
sorted in ascending order (natural order) or by a
comparator provided at set creation.
2. No Duplicates: Like all sets, TreeSet does not allow
duplicate elements.
3. Null Elements: TreeSet does not allow null elements,
as it uses comparisons to sort elements
and null cannot be compared.
4. Implements NavigableSet: The TreeSet class
implements the NavigableSet interface, which provides
additional methods for navigation like getting the
closest match for a given element.
5. Slower Performance: Compared
to HashSet and LinkedHashSet, TreeSet operations have a
time complexity of O(log n) due to its underlying tree
structure (typically a Red-Black tree).
6. Non-Synchronized: TreeSet is not synchronized, so it
is not thread-safe. For thread-safe operations, you
can use Collections.synchronizedSortedSet(new
TreeSet<E>()).
Basic Syntax:
import java.util.TreeSet;
TreeSet<Type> set = new TreeSet<>();
Commonly Used Constructors:
TreeSet(): Creates an empty tree set, sorted according
to the natural ordering of its elements.
TreeSet(Comparator<? super E> comparator): Creates an
empty tree set, sorted according to the specified
comparator.
TreeSet(Collection<? extends E> c): Creates a tree set
containing the elements of the specified collection,
sorted according to the natural ordering of its
elements.
TreeSet(SortedSet<E> s): Creates a tree set containing
the elements of the specified sorted set, sorted in the
same order.
Methods in TreeSet
TreeSet inherits methods from the NavigableSet, SortedSet,
and Set interfaces. Here are the most commonly used
methods:
1. add(E e): Adds the specified element to the set if it is
not already present.
2. addAll(Collection<? extends E> c): Adds all the elements
from the specified collection to the set.
3. clear(): Removes all elements from the set.
4. contains(Object o): Returns true if the set contains the
specified element.
5. remove(Object o): Removes the specified element from
the set if it is present.
6. size(): Returns the number of elements in the set.
7. isEmpty(): Returns true if the set contains no elements.
8. iterator(): Returns an iterator over the elements in the
set in ascending order.
9. toArray(): Returns an array containing all of the
elements in the set.
10. first(): Returns the first (lowest) element currently
in the set.
11. last(): Returns the last (highest) element currently
in the set.
12. higher(E e): Returns the least element in this set
strictly greater than the given element, or null if
there is no such element.
13. lower(E e): Returns the greatest element in this set
strictly less than the given element, or null if there is
no such element.
14. ceiling(E e): Returns the least element in this set
greater than or equal to the given element, or null if
there is no such element.
15. floor(E e): Returns the greatest element in this set
less than or equal to the given element, or null if
there is no such element.
16. pollFirst(): Retrieves and removes the first (lowest)
element, or returns null if the set is empty.
17. pollLast(): Retrieves and removes the last (highest)
element, or returns null if the set is empty.
Example Usage of TreeSet
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
// Creating a TreeSet of String type
TreeSet<String> set = new TreeSet<>();
// Adding elements to the TreeSet
set.add("Apple");
set.add("Banana");
set.add("Cherry");
System.out.println("TreeSet: " + set);
// Trying to add a duplicate element
boolean isAdded = set.add("Apple");
System.out.println("Is 'Apple' added again? " + isAdded);
// Checking if an element exists in the TreeSet
boolean containsCherry = set.contains("Cherry");
System.out.println("Does the set contain 'Cherry'? " +
containsCherry);
// Removing an element from the TreeSet
set.remove("Banana");
System.out.println("After removing 'Banana': " + set);
// Getting the size of the TreeSet
int size = set.size();
System.out.println("Size of the TreeSet: " + size);
// Getting the first and last elements
System.out.println("First element: " + set.first());
System.out.println("Last element: " + set.last());
// Getting higher and lower elements
System.out.println("Higher than 'Apple': " + set.higher("Apple"));
System.out.println("Lower than 'Cherry': " + set.lower("Cherry"));
// Iterating over the elements of the TreeSet
System.out.println("Elements in the TreeSet:");
for (String element : set) {
System.out.println(element);
}
}
}
Output:
TreeSet: [Apple, Banana, Cherry]
Is 'Apple' added again? false
Does the set contain 'Cherry'? true
After removing 'Banana': [Apple, Cherry]
Size of the TreeSet: 2
First element: Apple
Last element: Cherry
Higher than 'Apple': Cherry
Lower than 'Cherry': Apple
Elements in the TreeSet:
Apple
Cherry
When to Use TreeSet
1. Sorted Collection: Use TreeSet when you need
elements to be stored in a sorted order (either
natural or based on a comparator).
2. No Duplicates: Like other sets, TreeSet prevents
duplicate elements from being added.
3. Range Operations: If you need operations such as
finding the closest matches to an element or fetching
elements in a certain range (headSet, tailSet), TreeSet is
a good option.
4. Moderate Performance: Operations
like add(), remove(), and contains() take O(log n) time,
making it slower than HashSet, but it offers the benefit
of maintaining a sorted set.
Queue
In Java, the Queue interface, part of the Java Collections
Framework, represents a collection designed for holding
elements prior to processing. A queue follows FIFO
(First-In-First-Out) ordering, where elements are
inserted at the rear and removed from the front.
Key Characteristics:
1. FIFO Ordering: The queue typically processes
elements in the order they were added (though
variations like priority queues don’t follow FIFO).
2. No Direct Access: Unlike lists, queues do not
provide indexed access to elements. You can only
interact with the head and tail.
3. Null Values: Most queue implementations
(like PriorityQueue, ArrayDeque) do not allow null values.
4. Blocking and Non-blocking Queues:
The Queue interface has several subinterfaces
like BlockingQueue that support blocking operations for
thread-safe access.
5. Non-Synchronized: Most queue implementations
are not synchronized, so they are not thread-safe. For
thread-safe operations, you can use concurrent queue
implementations like ConcurrentLinkedQueue.
Queue Implementations:
LinkedList: Implements Queue and Deque interfaces.
Provides a FIFO structure when used as a queue.
PriorityQueue: A queue that orders its elements
based on their natural ordering or by a provided
comparator.
ArrayDeque: A resizable array that
implements Queue and Deque. More efficient
than LinkedList for queues.
Commonly Used Constructors:
For a LinkedList queue
Queue<Type> queue = new LinkedList<>();
For a PriorityQueue queue
Queue<Type> queue = new PriorityQueue<>();
Commonly Used Methods in Queue
Here are some of the most commonly used methods in
the Queue interface:
1. add(E e): Inserts the specified element into the queue.
Throws IllegalStateException if the queue is full.
2. offer(E e): Inserts the specified element into the queue.
Returns true if successful; false if the queue is full
(more commonly used than add() because it doesn't
throw an exception).
3. remove(): Retrieves and removes the head of the
queue. Throws NoSuchElementException if the queue is
empty.
4. poll(): Retrieves and removes the head of the queue,
or returns null if the queue is empty (more commonly
used than remove() because it doesn’t throw an
exception).
5. element(): Retrieves, but does not remove, the head of
the queue. Throws NoSuchElementException if the queue is
empty.
6. peek(): Retrieves, but does not remove, the head of
the queue, or returns null if the queue is empty.
7. size(): Returns the number of elements in the queue.
8. isEmpty(): Returns true if the queue contains no
elements.
9. clear(): Removes all elements from the queue.
Example Usage of Queue
import java.util.LinkedList;
import java.util.Queue;
public class QueueExample {
public static void main(String[] args) {
// Creating a queue using LinkedList
Queue<String> queue = new LinkedList<>();
// Adding elements to the queue
queue.add("Apple");
queue.add("Banana");
queue.add("Cherry");
System.out.println("Queue: " + queue);
// Removing the head of the queue
String removedElement = queue.remove();
System.out.println("Removed: " + removedElement);
System.out.println("Queue after removal: " + queue);
// Accessing the head element without removing it
String headElement = queue.peek();
System.out.println("Head of the queue: " + headElement);
// Adding another element using offer()
queue.offer("Date");
System.out.println("Queue after adding 'Date': " + queue);
// Removing all elements
queue.clear();
System.out.println("Queue after clearing: " + queue.isEmpty());
}
}
Output:
Queue: [Apple, Banana, Cherry]
Removed: Apple
Queue after removal: [Banana, Cherry]
Head of the queue: Banana
Queue after adding 'Date': [Banana, Cherry, Date]
Queue after clearing: true
When to Use Queue
1. Processing Tasks in Order: Use Queue when you
need to process elements in the order they were
added (FIFO).
2. Priority-Based Processing: If you need elements
processed in a specific order other than FIFO (e.g.,
by priority), you can use PriorityQueue.
3. Thread Safety: Use implementations
like BlockingQueue in multi-threaded environments
where thread safety and blocking operations are
important.
ArrayDeque
The ArrayDeque class in Java is a resizable, array-based
implementation of both the Deque (Double-Ended
Queue) interface and the Queue interface. It can be used to
create a stack, a queue, or a deque depending on the
operations performed on it. It is more efficient
than LinkedList for queue and stack operations due to its
dynamic resizing nature and array-based structure.
ArrayDeque in Java is a part of the Java Collections
Framework and implements the Deque interface. It is a
resizable array-based implementation of a deque (double-
ended queue), which allows elements to be added or
removed from both ends. ArrayDeque is not synchronized,
making it not thread-safe, but it provides better
performance compared to LinkedList for most deque
operations.
Key Characteristics:
1. Resizable Array: Unlike ArrayList, the ArrayDeque does
not have a fixed size. It automatically grows as
elements are added.
2. No Null Elements: Unlike some other collections
(like LinkedList), ArrayDeque does not permit null values.
3. Efficient: It is generally faster than LinkedList for
implementing stacks and queues, as it doesn't have
the overhead of node management.
4. Deque Functionality: It can operate as both
a queue (FIFO) and a stack (LIFO) by
adding/removing elements from both ends.
5. No Capacity Restrictions: While ArrayDeque is based
on an array, it dynamically resizes, so there is no
need to worry about exceeding an initial capacity.
Commonly Used Constructors:
ArrayDeque(): Creates an empty deque.
ArrayDeque(int numElements): Creates a deque with an
initial capacity specified by numElements.
Key Methods in ArrayDeque
1. addFirst(E e): Inserts the element at the front of the
deque.
2. addLast(E e): Inserts the element at the rear of the
deque.
3. offerFirst(E e): Inserts the element at the front of the
deque, returning false if it cannot be added.
4. offerLast(E e): Inserts the element at the rear of the
deque, returning false if it cannot be added.
5. removeFirst(): Removes and returns the element at the
front. Throws NoSuchElementException if the deque is
empty.
6. removeLast(): Removes and returns the element at the
rear. Throws NoSuchElementException if the deque is
empty.
7. pollFirst(): Retrieves and removes the element at the
front, or returns null if the deque is empty.
8. pollLast(): Retrieves and removes the element at the
rear, or returns null if the deque is empty.
9. getFirst(): Retrieves but does not remove the element
at the front. Throws NoSuchElementException if the deque
is empty.
10. getLast(): Retrieves but does not remove the
element at the rear. Throws NoSuchElementException if
the deque is empty.
11. peekFirst(): Retrieves but does not remove the
element at the front, or returns null if the deque is
empty.
12. peekFirst(): Retrieves but does not remove the
element at the front, or returns null if the deque is
empty.
13. size(): Returns the number of elements in the
deque.
14. isEmpty(): Returns true if the deque contains no
elements.
15. clear(): Removes all elements from the deque.
16. iterator(): Returns an iterator over the elements in
the deque in proper sequence.
17. descendingIterator(): Returns an iterator over the
elements in reverse order.
Example Usage of ArrayDeque
import java.util.ArrayDeque;
import java.util.Deque;
public class ArrayDequeExample {
public static void main(String[] args) {
// Creating an ArrayDeque
Deque<String> deque = new ArrayDeque<>();
// Adding elements to the deque (acting as a stack and queue)
deque.addFirst("Element 1 (Stack)");
deque.addFirst("Element 2 (Stack)");
deque.addLast("Element 3 (Queue)");
deque.addLast("Element 4 (Queue)");
System.out.println("Deque after adding elements: " + deque);
// Removing elements from the deque
String first = deque.removeFirst(); // Acts like a stack (LIFO)
String last = deque.removeLast(); // Acts like a queue (FIFO)
System.out.println("Removed first (LIFO): " + first);
System.out.println("Removed last (FIFO): " + last);
System.out.println("Deque after removal: " + deque);
// Peeking at elements without removing them
String peekFirst = deque.peekFirst();
String peekLast = deque.peekLast();
System.out.println("Peek first element: " + peekFirst);
System.out.println("Peek last element: " + peekLast);
// Checking if the deque is empty
boolean isEmpty = deque.isEmpty();
System.out.println("Is deque empty? " + isEmpty);
// Clearing the deque
deque.clear();
System.out.println("Deque after clearing: " + deque);
}
}
Output:
Deque after adding elements: [Element 2 (Stack), Element 1 (Stack),
Element 3 (Queue), Element 4 (Queue)]
Removed first (LIFO): Element 2 (Stack)
Removed last (FIFO): Element 4 (Queue)
Deque after removal: [Element 1 (Stack), Element 3 (Queue)]
Peek first element: Element 1 (Stack)
Peek last element: Element 3 (Queue)
Is deque empty? false
Deque after clearing: []
Use Cases for ArrayDeque
Queue Operations: When you need to implement a
queue (FIFO) with efficient insertions/removals at
both ends.
Stack Operations: When you need a stack (LIFO)
with faster access than Stack (which is synchronized
and slower).
Deque Operations: When you need to insert/remove
elements from both ends, such as implementing a
deque or a double-ended queue.
PriorityQueue
The PriorityQueue class in Java is a part of the Java
Collections Framework and implements
the Queue interface. Unlike a regular queue, where
elements are processed in FIFO (First-In-First-
Out) order, a PriorityQueue orders its elements
according to their natural ordering (for example,
integers from low to high) or based on
a Comparator provided at the queue's construction time.
PriorityQueue in Java is a part of the Java Collections
Framework and implements the Queue interface. It is a
resizable array-based implementation of a priority queue,
which orders its elements according to their natural
ordering or by a specified comparator. The head of the
queue is the least element with respect to the specified
ordering.
Key Characteristics:
1. Priority-Based Ordering: Elements are ordered
based on their priority. By default, a min-
heap structure is used where the smallest element is
at the head of the queue. For custom ordering, you
can provide a comparator.
2. No Null Elements: PriorityQueue does not
allow null elements.
3. Not Thread-Safe: PriorityQueue is not synchronized.
For thread-safe priority queues, consider
using PriorityBlockingQueue.
4. Dynamic Resizing: Internally, PriorityQueue is backed
by a binary heap, and it resizes dynamically as more
elements are added.
5. Iterating in Any Order: The iterator provided
by PriorityQueue does not guarantee any specific order
of iteration.
Constructors of PriorityQueue:
1. PriorityQueue(): Creates an empty priority queue with
an initial capacity of 11 and orders elements
according to their natural ordering.
2. PriorityQueue(int initialCapacity): Creates an empty
priority queue with a specified initial capacity.
3. PriorityQueue(int initialCapacity, Comparator<? super E>
comparator): Creates a priority queue with the
specified initial capacity that orders its elements
according to the specified comparator.
4. PriorityQueue(int initialCapacity, Comparator<? super E>
comparator): Creates a priority queue with the
specified initial capacity that orders its elements
according to the specified comparator.
Commonly Used Methods in PriorityQueue
Adding and Inserting Elements:
1. add(E e): Inserts the specified element into the priority
queue. Throws IllegalStateException if the queue is full.
2. offer(E e): Inserts the specified element into the
priority queue. Returns true if successful, false if the
queue is full.
3. remove(): Retrieves and removes the head of the
queue. Throws NoSuchElementException if the queue is
empty.
4. poll(): Retrieves and removes the head of the queue,
or returns null if the queue is empty.
5. peek(): Retrieves, but does not remove, the head of
the queue, or returns null if the queue is empty.
6. element(): Retrieves, but does not remove, the head of
the queue. Throws NoSuchElementException if the queue is
empty.
7. size(): Returns the number of elements in the priority
queue.
8. isEmpty(): Returns true if the priority queue contains
no elements.
9. clear(): Removes all elements from the priority queue.
10. contains(Object o): Returns true if the priority queue
contains the specified element.
11. iterator(): Returns an iterator over the elements in
this priority queue, but the iterator does not
guarantee any specific order.
Example Usage of PriorityQueue
import java.util.PriorityQueue;
public class PriorityQueueExample {
public static void main(String[] args) {
// Creating a PriorityQueue
PriorityQueue<Integer> pq = new PriorityQueue<>();
// Adding elements
pq.add(30);
pq.add(20);
pq.add(10);
pq.add(40);
System.out.println("PriorityQueue: " + pq);
// Peeking at the head element
int head = pq.peek();
System.out.println("Head of the queue: " + head);
// Removing the head element (smallest element)
int removed = pq.poll();
System.out.println("Removed: " + removed);
// Printing the queue after removal
System.out.println("PriorityQueue after removal: " + pq);
// Iterating over the queue (order is not guaranteed)
System.out.print("Iterating over the queue: ");
for (Integer element : pq) {
System.out.print(element + " ");
}
}
}
PriorityQueue in Java: Detailed Explanation
The PriorityQueue class in Java is a part of the Java
Collections Framework and implements
the Queue interface. Unlike a regular queue, where
elements are processed in FIFO (First-In-First-
Out) order, a PriorityQueue orders its elements
according to their natural ordering (for example,
integers from low to high) or based on
a Comparator provided at the queue's construction time.
Key Characteristics:
1. Priority-Based Ordering: Elements are ordered
based on their priority. By default, a min-
heap structure is used where the smallest element is
at the head of the queue. For custom ordering, you
can provide a comparator.
2. No Null Elements: PriorityQueue does not
allow null elements.
3. Not Thread-Safe: PriorityQueue is not synchronized.
For thread-safe priority queues, consider
using PriorityBlockingQueue.
4. Dynamic Resizing: Internally, PriorityQueue is backed
by a binary heap, and it resizes dynamically as more
elements are added.
5. Iterating in Any Order: The iterator provided
by PriorityQueue does not guarantee any specific order
of iteration.
Constructors of PriorityQueue:
1. PriorityQueue(): Creates an empty priority queue with
an initial capacity of 11 and orders elements
according to their natural ordering.
java
Copy code
PriorityQueue<Integer> pq = new PriorityQueue<>();
1. PriorityQueue(int initialCapacity): Creates an empty
priority queue with a specified initial capacity.
java
Copy code
PriorityQueue<Integer> pq = new PriorityQueue<>(20);
1. PriorityQueue(int initialCapacity, Comparator<? super E>
comparator): Creates a priority queue with the
specified initial capacity that orders its elements
according to the specified comparator.
java
Copy code
PriorityQueue<Integer> pq = new
PriorityQueue<>(Comparator.reverseOrder());
1. PriorityQueue(Collection<? extends E> c): Creates a
priority queue containing the elements of the
specified collection.
java
Copy code
List<Integer> list = Arrays.asList(5, 2, 1, 4);
PriorityQueue<Integer> pq = new PriorityQueue<>(list);
Commonly Used Methods in PriorityQueue
Adding and Inserting Elements:
1. add(E e): Inserts the specified element into the priority
queue. Throws IllegalStateException if the queue is full.
java
Copy code
pq.add(10);
1. offer(E e): Inserts the specified element into the
priority queue. Returns true if successful, false if the
queue is full.
java
Copy code
pq.offer(20);
Removing and Retrieving Elements:
1. remove(): Retrieves and removes the head of the
queue. Throws NoSuchElementException if the queue is
empty.
java
Copy code
int removed = pq.remove();
1. poll(): Retrieves and removes the head of the queue,
or returns null if the queue is empty.
java
Copy code
Integer polled = pq.poll();
1. peek(): Retrieves, but does not remove, the head of
the queue, or returns null if the queue is empty.
java
Copy code
Integer head = pq.peek();
1. element(): Retrieves, but does not remove, the head of
the queue. Throws NoSuchElementException if the queue is
empty.
java
Copy code
Integer head = pq.element();
Other Utility Methods:
1. size(): Returns the number of elements in the priority
queue.
java
Copy code
int size = pq.size();
1. isEmpty(): Returns true if the priority queue contains
no elements.
java
Copy code
boolean isEmpty = pq.isEmpty();
1. clear(): Removes all elements from the priority queue.
java
Copy code
pq.clear();
1. contains(Object o): Returns true if the priority queue
contains the specified element.
java
Copy code
boolean containsTen = pq.contains(10);
1. iterator(): Returns an iterator over the elements in this
priority queue, but the iterator does not guarantee
any specific order.
java
Copy code
Iterator<Integer> iterator = pq.iterator();
Example Usage of PriorityQueue
java
Copy code
import java.util.PriorityQueue;
public class PriorityQueueExample {
public static void main(String[] args) {
// Creating a PriorityQueue
PriorityQueue<Integer> pq = new PriorityQueue<>();
// Adding elements
pq.add(30);
pq.add(20);
pq.add(10);
pq.add(40);
System.out.println("PriorityQueue: " + pq);
// Peeking at the head element
int head = pq.peek();
System.out.println("Head of the queue: " + head);
// Removing the head element (smallest element)
int removed = pq.poll();
System.out.println("Removed: " + removed);
// Printing the queue after removal
System.out.println("PriorityQueue after removal: " + pq);
// Iterating over the queue (order is not guaranteed)
System.out.print("Iterating over the queue: ");
for (Integer element : pq) {
System.out.print(element + " ");
}
}
}
Output:
PriorityQueue: [10, 20, 30, 40]
Head of the queue: 10
Removed: 10
PriorityQueue after removal: [20, 40, 30]
Iterating over the queue: 20 40 30
Use Cases for PriorityQueue:
1. Task Scheduling: In scenarios where tasks have
different priorities, you can use PriorityQueue to
process tasks with the highest priority first.
2. Dijkstra’s Shortest Path Algorithm: Used in
graph-based algorithms where nodes with lower costs
are processed first.
3. Event-Driven Simulation: When events need to be
processed based on their scheduled time or priority.
4. Job Scheduling in Operating Systems: Tasks or
processes are executed based on their priority.
HashMap
A HashMap in Java is a part of the Java Collections
Framework and implements the Map interface. It stores
key-value pairs, allowing you to retrieve a value based on
its key. HashMap provides constant-time performance for
basic operations such as adding, removing, and accessing
elements, as it uses a hash table for storing the data.
HashMap in Java is a part of the Java Collections Framework
and is used to store key-value pairs. It is implemented as a
hash table, which allows for constant-time performance for
basic operations such as adding, removing, and retrieving
elements. HashMap does not maintain any order of the
elements; it neither maintains insertion order nor sorts the
elements.
Key Characteristics:
1. Stores Key-Value Pairs: Each entry in a HashMap is a
key-value pair.
2. No Duplicates for Keys: HashMap does not allow
duplicate keys. If you insert a duplicate key, the old
value is replaced by the new value.
3. Allows Null Values: A HashMap can store one null key
and multiple null values.
4. Unordered: The elements are not stored in any
particular order. The order of elements may change
over time, especially when resizing happens.
5. Non-Synchronized: HashMap is not thread-safe. For
synchronized operations, consider
using ConcurrentHashMap or manually synchronizing
the HashMap.
6. Performance: Average time complexity for basic
operations like get and put is O(1).
Constructors of HashMap:
1. HashMap(): Creates an empty HashMap with the default
initial capacity (16) and load factor (0.75).
2. HashMap(int initialCapacity): Creates a HashMap with the
specified initial capacity.
3. HashMap(int initialCapacity, float loadFactor): Creates
a HashMap with the specified initial capacity and load
factor.
4. HashMap(Map<? extends K,? extends V> m): Creates
a HashMap with the same mappings as the specified
map.
Commonly Used Methods in HashMap
Adding and Inserting Elements:
1. put(K key, V value): Inserts the specified key-value pair
into the HashMap. If the key already exists, its old value
is replaced by the new value.
2. putAll(Map<? extends K, ? extends V> m): Copies all the
key-value mappings from the specified map to
this HashMap.
3. putIfAbsent(K key, V value): Inserts the key-value pair
only if the specified key is not already associated with
a value.
4. get(Object key): Returns the value to which the
specified key is mapped, or null if the HashMap contains
no mapping for the key.
5. getOrDefault(Object key, V defaultValue): Returns the value
to which the specified key is mapped, or the default
value if the key is not found.
6. remove(Object key): Removes the mapping for the
specified key if present.
7. remove(Object key, Object value): Removes the entry for
the specified key only if it is currently mapped to the
specified value.
8. containsKey(Object key): Returns true if
the HashMap contains a mapping for the specified key.
9. containsValue(Object value): Returns true if
the HashMap maps one or more keys to the specified
value.
10. size(): Returns the number of key-value mappings
in the HashMap.
11. isEmpty(): Returns true if the HashMap contains no
key-value mappings.
12. clear(): Removes all key-value mappings from
the HashMap.
13. keySet(): Returns a Set view of the keys contained
in this HashMap.
14. values(): Returns a Collection view of the values
contained in this HashMap.
15. entrySet(): Returns a Set view of the key-value
mappings contained in this HashMap.
Example Usage of HashMap
import java.util.HashMap;
public class HashMapExample {
public static void main(String[] args) {
// Creating a HashMap
HashMap<String, Integer> map = new HashMap<>();
// Adding elements
map.put("John", 25);
map.put("Alice", 30);
map.put("Bob", 20);
map.put("Mike", 35);
// Accessing elements
System.out.println("John's age: " + map.get("John"));
// Checking if a key exists
if (map.containsKey("Alice")) {
System.out.println("Alice is in the map.");
}
// Removing an element
map.remove("Bob");
// Iterating over the map
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
// Using entrySet() to iterate over key-value pairs
for (HashMap.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " => " +
entry.getValue());
}
}
}
Output:
John's age: 25
Alice is in the map.
John: 25
Alice: 30
Mike: 35
John => 25
Alice => 30
Mike => 35
Use Cases for HashMap:
1. Cache Implementations: HashMap is frequently used
to store cached data for quick lookups.
2. Counting Frequencies: You can use HashMap to count
the frequency of elements in an array or list.
3. Associative Arrays: It’s often used to simulate
associative arrays or dictionaries where key-value
pairs are needed.
4. Object Mapping: Used in object serialization, where
properties of objects are stored as key-value pairs.
LinkedHashMap
A LinkedHashMap in Java is a part of the Java Collections
Framework and implements the Map interface. It
extends HashMap and maintains a doubly linked list of the
entries in the map, preserving the insertion
order or access order of elements.
LinkedHashMap in Java is a part of the Java Collections
Framework and extends HashMap. It maintains a doubly
linked list running through all of its entries, which allows it
to preserve the insertion order of elements.
Additionally, LinkedHashMap can be configured to order its
entries based on access order, where the most recently
accessed entries are moved to the end of the map.
Key Characteristics:
1. Ordered Entries: Unlike HashMap, which does not
guarantee any order, LinkedHashMap maintains the order
in which elements were inserted or accessed.
2. Faster Iteration: Since LinkedHashMap maintains a
linked list of entries, iteration over the keys, values,
or entries is faster compared to HashMap.
3. Performance: Like HashMap, LinkedHashMap offers
constant time O(1) performance for operations such
as insertion, deletion, and access, with an extra
overhead of maintaining the linked list for ordering.
4. Access Order: You can create a LinkedHashMap to
maintain access order, meaning that the last
accessed element will be moved to the end of the
order.
Constructors of LinkedHashMap:
1. LinkedHashMap(): Creates an empty LinkedHashMap with
the default initial capacity (16) and load factor (0.75).
2. LinkedHashMap(int initialCapacity): Creates
a LinkedHashMap with the specified initial capacity.
3. LinkedHashMap(int initialCapacity, float loadFactor): Creates
a LinkedHashMap with the specified initial capacity and
load factor.
4. LinkedHashMap(int initialCapacity, float loadFactor, boolean
accessOrder): Creates a LinkedHashMap with the specified
initial capacity, load factor, and ordering mode
(insertion order or access order). If accessOrder is true,
the map is ordered based on the last access.
5. LinkedHashMap(Map<? extends K,? extends V> m): Creates
a LinkedHashMap with the same mappings as the
specified map.
Commonly Used Methods in LinkedHashMap
The methods in LinkedHashMap are almost identical to those
in HashMap, but it provides additional ordering behavior.
Adding and Inserting Elements:
1. put(K key, V value): Inserts the specified key-value pair
into the LinkedHashMap. If the key already exists, its old
value is replaced by the new value. The insertion
order is maintained.
Commonly Used Methods in LinkedHashMap
The methods in LinkedHashMap are almost identical to those
in HashMap, but it provides additional ordering behavior.
Adding and Inserting Elements:
1. put(K key, V value): Inserts the specified key-value pair
into the LinkedHashMap. If the key already exists, its old
value is replaced by the new value. The insertion
order is maintained.
2. putIfAbsent(K key, V value): Inserts the key-value pair
only if the specified key is not already associated with
a value.
3. get(Object key): Returns the value to which the
specified key is mapped, or null if
the LinkedHashMap contains no mapping for the key. The
access order is updated if accessOrder is true.
4. getOrDefault(Object key, V defaultValue): Returns the value
to which the specified key is mapped, or the default
value if the key is not found.
5. remove(Object key): Removes the mapping for the
specified key if present
6. containsKey(Object key): Returns true if
the LinkedHashMap contains a mapping for the specified
key.
7. containsValue(Object value): Returns true if
the LinkedHashMap maps one or more keys to the
specified value.
8. size(): Returns the number of key-value mappings in
the LinkedHashMap.
9. isEmpty(): Returns true if the LinkedHashMap contains no
key-value mappings.
10. clear(): Removes all key-value mappings from
the LinkedHashMap.
11. keySet(): Returns a Set view of the keys contained
in this LinkedHashMap.
12. values(): Returns a Collection view of the values
contained in this LinkedHashMap.
13. entrySet(): Returns a Set view of the key-value
mappings contained in this LinkedHashMap.
Example Usage of LinkedHashMap
import java.util.LinkedHashMap;
public class LinkedHashMapExample {
public static void main(String[] args) {
// Creating a LinkedHashMap
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
// Adding elements
map.put("John", 25);
map.put("Alice", 30);
map.put("Bob", 20);
map.put("Mike", 35);
// Accessing elements
System.out.println("John's age: " + map.get("John"));
// Checking if a key exists
if (map.containsKey("Alice")) {
System.out.println("Alice is in the map.");
}
// Removing an element
map.remove("Bob");
// Iterating over the map in insertion order
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
// Using entrySet() to iterate over key-value pairs
for (LinkedHashMap.Entry<String, Integer> entry : map.entrySet())
{
System.out.println(entry.getKey() + " => " +
entry.getValue());
}
}
}
Output :
John's age: 25
Alice is in the map.
John: 25
Alice: 30
Mike: 35
John => 25
Alice => 30
Mike => 35
Use Cases for LinkedHashMap:
1. Caches with Access Order: LinkedHashMap is useful for
building LRU (Least Recently Used) caches. By
setting accessOrder = true, you can easily implement a
cache that removes the least recently accessed entry.
2. Maintaining Insertion Order: When you need to
store mappings in the order they were inserted (e.g.,
for a user interface that displays items in the order
they were added).
3. Predictable Iteration Order: It is useful when you
need predictable iteration order for testing, logging,
or debugging.
TreeMap
A TreeMap in Java is part of the Java Collections
Framework and implements
the NavigableMap interface. It is a sorted map that
orders its elements based on their keys, either by
their natural ordering (if the keys implement Comparable)
or by a custom comparator provided at the time of map
creation.
TreeMap in Java is a part of the Java Collections Framework
and implements the NavigableMap interface, which extends
the SortedMap interface. It stores key-value pairs in a sorted
order based on the natural ordering of the keys or by a
specified comparator. TreeMap is implemented using a Red-
Black tree, which ensures that the map remains balanced
and provides log(n) time complexity for basic
Key Characteristics:
1. Sorted Order: The keys are always sorted in
ascending order by default. You can specify a custom
comparator to sort in a different order.
2. Navigable: TreeMap provides methods to navigate
through the keys
(e.g., firstKey(), lastKey(), headMap(), tailMap()).
3. Red-Black Tree: Internally, TreeMap is implemented
as a Red-Black Tree, which guarantees O(log(n))
time complexity for common operations
like put(), get(), remove(), etc.
4. No Null Keys: TreeMap does not allow null keys (it
will throw a NullPointerException), but null values are
permitted.
Constructors of TreeMap:
1. TreeMap(): Creates an empty TreeMap with natural
ordering of its keys.
2. TreeMap(Comparator<? super K> comparator): Creates an
empty TreeMap that orders its keys according to the
specified comparator.
3. TreeMap(Map<? extends K,? extends V> m): Constructs a
new TreeMap containing the same mappings as the
given map, sorted according to the keys' natural
ordering.
4. TreeMap(SortedMap<K,? extends V> m): Constructs a
new TreeMap with the same mappings as the specified
sorted map, with the same ordering.
5. put(K key, V value): Associates the specified value with
the specified key in this TreeMap. If the map previously
contained a mapping for the key, the old value is
replaced.
6. putIfAbsent(K key, V value): Associates the specified key
with the specified value only if it is not already
present in the map.
7. get(Object key): Returns the value to which the
specified key is mapped, or null if this map contains
no mapping for the key.
8. getOrDefault(Object key, V defaultValue): Returns the value
to which the specified key is mapped, or the default
value if the map contains no mapping for the key.
9. getOrDefault(Object key, V defaultValue): Returns the value
to which the specified key is mapped, or the default
value if the map contains no mapping for the key.
10. containsKey(Object key): Returns true if this map
contains a mapping for the specified key.
11. containsValue(Object value): Returns true if this map
maps one or more keys to the specified value.
12. size(): Returns the number of key-value mappings
in the TreeMap.
13. isEmpty(): Returns true if the TreeMap contains no
key-value mappings.
14. clear(): Removes all key-value mappings from
the TreeMap.
15. keySet(): Returns a Set view of the keys contained
in this map, in ascending order.
16. values(): Returns a Collection view of the values
contained in this map, in the order corresponding to
ascending key order.
17. entrySet(): Returns a Set view of the key-value
mappings contained in this map, in ascending key
order.
Navigational Methods in TreeMap
1. firstKey(): Returns the first (lowest) key currently in
this map.
2. lastKey(): Returns the last (highest) key currently in
this map.
3. headMap(K toKey): Returns a view of the portion of this
map whose keys are strictly less than toKey.
4. tailMap(K fromKey): Returns a view of the portion of this
map whose keys are greater than or equal to fromKey.
5. subMap(K fromKey, K toKey): Returns a view of the
portion of this map whose keys range
from fromKey (inclusive) to toKey (exclusive).
6. lowerKey(K key): Returns the greatest key strictly less
than the given key, or null if there is no such key.
7. higherKey(K key): Returns the least key strictly greater
than the given key, or null if there is no such key.
8. floorKey(K key): Returns the greatest key less than or
equal to the given key, or null if there is no such key.
9. ceilingKey(K key): Returns the least key greater than or
equal to the given key, or null if there is no such key.
Example Usage of TreeMap
import java.util.TreeMap;
public class TreeMapExample {
public static void main(String[] args) {
// Creating a TreeMap
TreeMap<String, Integer> map = new TreeMap<>();
// Adding elements to the TreeMap
map.put("John", 25);
map.put("Alice", 30);
map.put("Bob", 20);
map.put("Mike", 35);
// Accessing elements
System.out.println("John's age: " + map.get("John"));
// Removing an element
map.remove("Bob");
// Iterating over the TreeMap (in sorted order)
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
// Navigating using firstKey and lastKey
System.out.println("First Key: " + map.firstKey());
System.out.println("Last Key: " + map.lastKey());
}
}
Output
John's age: 25
Alice: 30
John: 25
Mike: 35
First Key: Alice
Last Key: Mike
Custom Comparator Example
You can use a custom comparator to sort the keys in
reverse order.
import java.util.TreeMap;
import java.util.Comparator;
public class TreeMapCustomComparatorExample {
public static void main(String[] args) {
// Creating a TreeMap with a custom comparator (reverse order)
TreeMap<String, Integer> map = new
TreeMap<>(Comparator.reverseOrder());
// Adding elements
map.put("John", 25);
map.put("Alice", 30);
map.put("Bob", 20);
map.put("Mike", 35);
// Iterating over the map (in reverse order)
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
}
}
Output:
Mike: 35
John: 25
Bob: 20
Alice: 30
Use Cases for TreeMap:
When you need to maintain a sorted order of keys.
If you need to efficiently retrieve elements in a sorted
manner, for instance, generating reports, rankings,
or maintaining historical data.
When you need efficient range queries based on keys
Hashtable in Java: Detailed Explanation
A Hashtable is a part of the Java Collections
Framework and is one of the original implementations of
a Map. It stores key-value pairs in a hash table and works
on the principle of hashing. Unlike HashMap, which is non-
synchronized, Hashtable is synchronized, making it thread-
safe for concurrent access.
Hashtable in Java is a part of the Java Collections Framework
and is used to store key-value pairs. It is similar to HashMap,
but with some key differences. Hashtable is synchronized,
making it thread-safe, and it does not allow null keys or
values.
Key Characteristics:
1. Synchronized: All operations on a Hashtable are
synchronized, meaning it is thread-safe and can be
shared across multiple threads. However,
synchronization makes it relatively slower
than HashMap.
2. No Null Keys or Values:
Unlike HashMap, Hashtable does not allow null keys or
values. If you try to insert a null key or value, it will
throw a NullPointerException.
3. Legacy Class: Hashtable was part of the original
version of Java (before the introduction
of Collections framework in Java 2) and is considered
a legacy class. Though still used, ConcurrentHashMap is
often preferred for concurrent operations.
4. Thread-Safe: Since Hashtable is synchronized,
multiple threads can access it simultaneously without
causing issues, but it can lead to performance
bottlenecks due to contention for locks.
5. Unordered: The elements (key-value pairs) in
a Hashtable are not ordered in any specific manner,
unlike TreeMap, which sorts them based on keys.
Constructors
Hashtable(): Constructs a new, empty hashtable with
a default initial capacity (11) and load factor (0.75).
Hashtable(int initialCapacity): Constructs a new,
empty hashtable with the specified initial capacity
and default load factor (0.75).
Hashtable(int initialCapacity, float
loadFactor): Constructs a new, empty hashtable
with the specified initial capacity and load factor.
Hashtable(Map<? extends K, ? extends V>
t): Constructs a new hashtable with the same
mappings as the specified map.
Commonly Used Methods in Hashtable
Adding and Inserting Elements:
1. put(K key, V value): Associates the specified value with
the specified key in this Hashtable. If the map
previously contained a mapping for the key, the old
value is replaced.
2. putIfAbsent(K key, V value): Associates the specified key
with the specified value only if it is not already
present in the map.
3. get(Object key): Returns the value to which the
specified key is mapped, or null if this map contains
no mapping for the key.
4. getOrDefault(Object key, V defaultValue): Returns the value
to which the specified key is mapped, or the default
value if the map contains no mapping for the key.
5. remove(Object key): Removes the mapping for the
specified key from this Hashtable if present.
6. clear(): Removes all of the mappings from
this Hashtable.
7. containsKey(Object key): Returns true if this map
contains a mapping for the specified key.
8. containsValue(Object value): Returns true if this map
maps one or more keys to the specified value.
9. isEmpty(): Returns true if this Hashtable contains no key-
value mappings.
10. size(): Returns the number of key-value mappings
in this Hashtable.
Example Usage of Hashtable
import java.util.Hashtable;
public class HashtableExample {
public static void main(String[] args) {
// Creating a Hashtable
Hashtable<String, Integer> table = new Hashtable<>();
// Adding elements to the Hashtable
table.put("John", 25);
table.put("Alice", 30);
table.put("Bob", 20);
table.put("Mike", 35);
// Accessing elements
System.out.println("John's age: " + table.get("John"));
// Checking presence of keys and values
System.out.println("Contains key 'John'? " +
table.containsKey("John"));
System.out.println("Contains value 30? " +
table.containsValue(30));
// Removing an element
table.remove("Bob");
// Iterating over the Hashtable
for (String key : table.keySet()) {
System.out.println(key + ": " + table.get(key));
}
// Size of Hashtable
System.out.println("Size of Hashtable: " + table.size());
}
}
Output:
John's age: 25
Contains key 'John'? true
Contains value 30? true
Alice: 30
John: 25
Mike: 35
Size of Hashtable: 3
Use Cases for Hashtable:
Hashtable is useful when you need to store key-value
pairs and ensure thread safety. However, due to the
performance impact of synchronization, it's often
recommended to use ConcurrentHashMap for more
efficient thread-safe operations.
It can be used in legacy applications or when working
in environments that require synchronized data
structures by default.
ConcurrentHashMap
ConcurrentHashMap is part of
the java.util.concurrent package and provides a highly
efficient, thread-safe implementation of a hash-based
Map. Unlike Hashtable, which synchronizes all
methods, ConcurrentHashMap improves performance
by partitioning the map into segments and locking only
specific segments during updates, allowing multiple
threads to read and write concurrently.
ConcurrentHashMap in Java is a part of the Java Collections
Framework and is designed for concurrent access. It is a
thread-safe implementation of the Map interface that
provides better performance and scalability compared to
synchronized collections
like Hashtable or Collections.synchronizedMap.
Key Characteristics:
1. Thread-Safe: ConcurrentHashMap allows safe, concurrent
access by multiple threads without the need for
external synchronization.
2. No Null Keys or Values: Similar
to Hashtable, ConcurrentHashMap does not allow null keys
or values. It will throw a NullPointerException if an
attempt is made to add a null key or value.
3. Segmented Locking (Fine-Grained Locking):
Instead of locking the entire map
like Hashtable, ConcurrentHashMap locks portions of the
map, enabling higher throughput in multi-threaded
environments.
4. Performance: It is faster than Hashtable in multi-
threaded environments due to reduced
contention (less time spent waiting for locks). It
scales better as the number of threads increases.
5. Weakly Consistent Iterators: Iterators returned by
the keySet(), entrySet(), and values() methods do not
throw ConcurrentModificationException. However, the
iterators are weakly consistent, meaning they
reflect the state of the map at the time of their
creation and may not show subsequent modifications
during iteration.
Constructors
ConcurrentHashMap(): Constructs a new, empty
map with a default initial table size (16).
ConcurrentHashMap(int
initialCapacity): Constructs a new, empty map with
an initial table size based on the given initial
capacity.
ConcurrentHashMap(int initialCapacity, float
loadFactor): Constructs a new, empty map with an
initial table size based on the given initial capacity,
and the given load factor.
ConcurrentHashMap(int initialCapacity, float
loadFactor, int concurrencyLevel): Constructs a
new, empty map with an initial table size based on
the given initial capacity, and the given load factor
and concurrency level.
ConcurrentHashMap(Map<? extends K, ?
extends V> m): Constructs a new map containing
the same mappings as the given map.
Commonly Used Methods in ConcurrentHashMap
Adding and Updating Elements:
1. put(K key, V value): Associates the specified value with
the specified key. If the map previously contained a
mapping for the key, the old value is replaced.
2. putIfAbsent(K key, V value): Associates the specified key
with the value only if it is not already present in the
map.
3. replace(K key, V oldValue, V newValue): Replaces the entry
for a key only if currently mapped to a specific value.
4. get(Object key): Returns the value associated with the
specified key, or null if no mapping is found.
5. getOrDefault(Object key, V defaultValue): Returns the value
associated with the specified key, or the default value
if no mapping is found.
6. remove(Object key): Removes the mapping for the
specified key.
7. clear(): Removes all the mappings from
the ConcurrentHashMap.
8. remove(Object key, Object value): Removes the entry for a
key only if currently mapped to the specified value.
Example Usage of ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
// Creating a ConcurrentHashMap
ConcurrentHashMap<String, Integer> map = new
ConcurrentHashMap<>();
// Adding elements to the ConcurrentHashMap
map.put("John", 25);
map.put("Alice", 30);
map.put("Bob", 20);
map.putIfAbsent("Mike", 35);
// Accessing elements
System.out.println("John's age: " + map.get("John"));
// Using atomic operations
map.compute("Alice", (key, val) -> val != null ? val + 5 : 30);
// Checking presence of keys and values
System.out.println("Contains key 'John'? " +
map.containsKey("John"));
System.out.println("Contains value 30? " + map.containsValue(30));
// Removing an element
map.remove("Bob");
// Iterating over the ConcurrentHashMap
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
// Size of ConcurrentHashMap
System.out.println("Size of ConcurrentHashMap: " + map.size());
}
}
Output:
John's age: 25
Contains key 'John'? true
Contains value 30? true
Alice: 35
John: 25
Mike: 35
Size of ConcurrentHashMap: 3
ConcurrentHashMap vs Hashtable vs HashMap:
ConcurrentHashMap: Concurrent, thread-safe, and offers
better performance than Hashtable due to fine-grained
locking.
Hashtable: Synchronized, but has performance
limitations due to locking the entire map for every
operation.
HashMap: Non-synchronized and not thread-safe, but
offers the best performance in single-threaded
environments.