0% found this document useful (0 votes)
10 views39 pages

Java Collections Framework Deep Dive

The document is a comprehensive guide to the Java Collections Framework, aimed at developers with over three years of experience. It covers various collection types, including List, Set, and Map interfaces, along with their implementations and performance characteristics. The guide includes coding examples, best practices, and advanced topics to enhance understanding and application of Java collections.

Uploaded by

Tushar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views39 pages

Java Collections Framework Deep Dive

The document is a comprehensive guide to the Java Collections Framework, aimed at developers with over three years of experience. It covers various collection types, including List, Set, and Map interfaces, along with their implementations and performance characteristics. The guide includes coding examples, best practices, and advanced topics to enhance understanding and application of Java collections.

Uploaded by

Tushar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Java Collections Framework

From Basic to Advanced

Complete Guide with Coding Examples

For 3+ Year Experienced Developers


Table of Contents
1. Introduction to Collections Framework
2. Collection Interface Hierarchy
3. List Interface - ArrayList, LinkedList, Vector
4. Set Interface - HashSet, LinkedHashSet, TreeSet
5. Queue and Deque Interfaces
6. Map Interface - HashMap, LinkedHashMap, TreeMap
7. ConcurrentHashMap and Thread-Safe Collections
8. Iterators and Iteration Techniques
9. Sorting and Comparators
10. Performance Comparison and Best Practices
11. Advanced Topics and Real-world Examples
1. Introduction to Collections Framework
The Java Collections Framework provides a unified architecture for storing and manipulating groups of objects. It
includes interfaces, implementations, and algorithms that allow developers to work with data structures efficiently.

Why Use Collections?


• Reduces programming effort by providing ready-to-use data structures
• Increases performance through optimized implementations
• Provides interoperability between unrelated APIs
• Reduces effort to learn and use new APIs
• Promotes software reuse

Basic Example:
import [Link].*;
public class CollectionsIntro {
public static void main(String[] args) {
// Creating ArrayList
List<String> names = new ArrayList<>();
// Adding elements
[Link]("Alice");
[Link]("Bob");
[Link]("Charlie");
// Iterating
for(String name : names) {
[Link](name);
}
// Size
[Link]("Total: " + [Link]());
}
}
2. Collection Interface Hierarchy
Understanding the hierarchy is crucial for choosing the right collection type.

Collection Framework Hierarchy:


Iterable (I)
■■■ Collection (I)
■■■ List (I)
■ ■■■ ArrayList (C)
■ ■■■ LinkedList (C)
■ ■■■ Vector (C)
■ ■■■ Stack (C)
■■■ Set (I)
■ ■■■ HashSet (C)
■ ■■■ LinkedHashSet (C)
■ ■■■ SortedSet (I)
■ ■■■ TreeSet (C)
■■■ Queue (I)
■■■ PriorityQueue (C)
■■■ Deque (I)
■■■ ArrayDeque (C)
Map (I) - Separate hierarchy
■■■ HashMap (C)
■■■ LinkedHashMap (C)
■■■ Hashtable (C)
■■■ SortedMap (I)
■■■ TreeMap (C)
Legend: (I) = Interface, (C) = Class
3. List Interface
List is an ordered collection that allows duplicate elements. Elements can be accessed by their index.

3.1 ArrayList
ArrayList is a resizable array implementation. Best for frequent read operations.

import [Link].*;
public class ArrayListExample {
public static void main(String[] args) {
// Creating ArrayList
ArrayList<Integer> numbers = new ArrayList<>();
// Adding elements
[Link](10);
[Link](20);
[Link](30);
[Link](1, 15); // Insert at index 1
// Accessing elements
[Link]("Element at index 0: " + [Link](0));
// Updating
[Link](2, 25);
// Removing
[Link]([Link](10)); // Remove by value
[Link](0); // Remove by index
// Checking
[Link]("Contains 20? " + [Link](20));
// Size
[Link]("Size: " + [Link]());
// Iteration using forEach (Java 8+)
[Link](num -> [Link](num));
// Sorting
[Link](numbers);
[Link]("Sorted: " + numbers);
// Clearing
[Link]();
[Link]("Is Empty? " + [Link]());
}
}
// Output:
// Element at index 0: 10
// Contains 20? true
// Size: 2
// 20
// 30
// Sorted: [20, 30]
// Is Empty? true

Time Complexity:

Operation Time Complexity

add(E element) O(1) amortized


add(int index, E element) O(n)
get(int index) O(1)
remove(int index) O(n)
contains(Object o) O(n)
3.2 LinkedList
LinkedList is a doubly-linked list implementation. Best for frequent insertions/deletions.

import [Link].*;
public class LinkedListExample {
public static void main(String[] args) {
LinkedList<String> cities = new LinkedList<>();
// Adding elements
[Link]("Delhi");
[Link]("Mumbai");
[Link]("Bangalore");
// Adding at first and last
[Link]("Kolkata");
[Link]("Chennai");
[Link]("Cities: " + cities);
// Output: [Kolkata, Delhi, Mumbai, Bangalore, Chennai]
// Getting first and last
[Link]("First: " + [Link]());
[Link]("Last: " + [Link]());
// Removing first and last
[Link]();
[Link]();
[Link]("After removal: " + cities);
// Using as Queue (FIFO)
LinkedList<String> queue = new LinkedList<>();
[Link]("First"); // Add to end
[Link]("Second");
[Link]("Third");
[Link]("Poll: " + [Link]()); // Remove from front
[Link]("Queue: " + queue);
// Using as Stack (LIFO)
LinkedList<String> stack = new LinkedList<>();
[Link]("Bottom");
[Link]("Middle");
[Link]("Top");
[Link]("Pop: " + [Link]()); // Remove from top
[Link]("Stack: " + stack);
// Peek operations (don't remove)
[Link]("Peek: " + [Link]());
[Link]("Stack after peek: " + stack);
}
}
// Output:
// Cities: [Kolkata, Delhi, Mumbai, Bangalore, Chennai]
// First: Kolkata
// Last: Chennai
// After removal: [Delhi, Mumbai, Bangalore]
// Poll: First
// Queue: [Second, Third]
// Pop: Top
// Stack: [Middle, Bottom]
// Peek: Middle
// Stack after peek: [Middle, Bottom]
3.3 ArrayList vs LinkedList Comparison
Feature ArrayList LinkedList

Data Structure Dynamic Array Doubly Linked List


Random Access Fast O(1) Slow O(n)
Insertion at End Fast O(1) Fast O(1)
Insertion at Middle Slow O(n) Fast O(1)*
Deletion Slow O(n) Fast O(1)*
Memory Less overhead More overhead (node objects)
Best Use Case Frequent reads Frequent insertions/deletions

* If you already have the node reference


4. Set Interface
Set is a collection that does not allow duplicate elements. It models the mathematical set abstraction.

4.1 HashSet
HashSet uses a hash table for storage. It offers constant time performance for basic operations (add, remove,
contains).

import [Link].*;
public class HashSetExample {
public static void main(String[] args) {
// Creating HashSet
HashSet<String> fruits = new HashSet<>();
// Adding elements
[Link]("Apple");
[Link]("Banana");
[Link]("Orange");
[Link]("Apple"); // Duplicate - will be ignored
[Link]("Fruits: " + fruits);
[Link]("Size: " + [Link]());
// Output: Size: 3 (duplicate not added)
// Checking existence
[Link]("Contains Banana? " + [Link]("Banana"));
// Removing
[Link]("Orange");
[Link]("After removal: " + fruits);
// Set Operations
HashSet<String> set1 = new HashSet<>([Link]("A", "B", "C"));
HashSet<String> set2 = new HashSet<>([Link]("B", "C", "D"));
// Union
HashSet<String> union = new HashSet<>(set1);
[Link](set2);
[Link]("Union: " + union); // [A, B, C, D]
// Intersection
HashSet<String> intersection = new HashSet<>(set1);
[Link](set2);
[Link]("Intersection: " + intersection); // [B, C]
// Difference
HashSet<String> difference = new HashSet<>(set1);
[Link](set2);
[Link]("Difference: " + difference); // [A]
// Iteration
[Link]("\nIterating:");
for(String fruit : fruits) {
[Link](fruit);
}
// Using Stream API (Java 8+)
[Link]()
.filter(f -> [Link]("A"))
.forEach([Link]::println);
}
}
4.2 LinkedHashSet
LinkedHashSet maintains insertion order. It's implemented as a hash table with a linked list running through it.

import [Link].*;
public class LinkedHashSetExample {
public static void main(String[] args) {
// Maintains insertion order
LinkedHashSet<Integer> numbers = new LinkedHashSet<>();
[Link](5);
[Link](2);
[Link](8);
[Link](1);
[Link](2); // Duplicate ignored
[Link]("LinkedHashSet: " + numbers);
// Output: [5, 2, 8, 1] - insertion order maintained
// Compare with HashSet (no order guarantee)
HashSet<Integer> hashSet = new HashSet<>();
[Link](5);
[Link](2);
[Link](8);
[Link](1);
[Link]("HashSet: " + hashSet);
// Output: May be [1, 2, 5, 8] or any order
// Use case: Removing duplicates while maintaining order
List<String> listWithDuplicates =
[Link]("A", "B", "A", "C", "B", "D");
LinkedHashSet<String> uniqueOrdered =
new LinkedHashSet<>(listWithDuplicates);
[Link]("Original: " + listWithDuplicates);
[Link]("Unique (ordered): " + uniqueOrdered);
// Output: Unique (ordered): [A, B, C, D]
}
}
4.3 TreeSet
TreeSet stores elements in sorted order. It's implemented using a Red-Black tree.

import [Link].*;
public class TreeSetExample {
public static void main(String[] args) {
// Elements automatically sorted
TreeSet<Integer> numbers = new TreeSet<>();
[Link](15);
[Link](5);
[Link](25);
[Link](10);
[Link](20);
[Link]("TreeSet: " + numbers);
// Output: [5, 10, 15, 20, 25] - sorted order
// NavigableSet methods
[Link]("First: " + [Link]()); // 5
[Link]("Last: " + [Link]()); // 25
[Link]("Lower than 15: " + [Link](15)); // 10
[Link]("Higher than 15: " + [Link](15)); // 20
[Link]("Floor of 16: " + [Link](16)); // 15
[Link]("Ceiling of 16: " + [Link](16)); // 20
// Subset operations
[Link]("HeadSet (<15): " + [Link](15));
// Output: [5, 10]
[Link]("TailSet (>=15): " + [Link](15));
// Output: [15, 20, 25]
[Link]("SubSet [10-20): " + [Link](10, 20));
// Output: [10, 15]
// Descending order
[Link]("Descending: " + [Link]());
// Output: [25, 20, 15, 10, 5]
// Poll operations (retrieve and remove)
[Link]("PollFirst: " + [Link]()); // 5
[Link]("PollLast: " + [Link]()); // 25
[Link]("After polling: " + numbers);
// Output: [10, 15, 20]
}
}
4.4 TreeSet with Custom Objects
import [Link].*;
class Employee implements Comparable<Employee> {
private int id;
private String name;
private double salary;
public Employee(int id, String name, double salary) {
[Link] = id;
[Link] = name;
[Link] = salary;
}
// Natural ordering by salary
@Override
public int compareTo(Employee other) {
return [Link]([Link], [Link]);
}
@Override
public String toString() {
return [Link]("Employee{id=%d, name='%s', salary=%.2f}",
id, name, salary);
}
// Getters
public int getId() { return id; }
public String getName() { return name; }
public double getSalary() { return salary; }
}
public class CustomTreeSetExample {
public static void main(String[] args) {
// Using natural ordering (Comparable)
TreeSet<Employee> empsBySalary = new TreeSet<>();
[Link](new Employee(1, "Alice", 60000));
[Link](new Employee(2, "Bob", 45000));
[Link](new Employee(3, "Charlie", 75000));
[Link]("Sorted by salary:");
[Link]([Link]::println);
// Using custom Comparator (by name)
TreeSet<Employee> empsByName = new TreeSet<>(
[Link](Employee::getName)
);
[Link](new Employee(1, "Alice", 60000));
[Link](new Employee(2, "Bob", 45000));
[Link](new Employee(3, "Charlie", 75000));
[Link]("\nSorted by name:");
[Link]([Link]::println);
// Multiple Comparators
TreeSet<Employee> empsByIdDesc = new TreeSet<>(
[Link](Employee::getId).reversed()
);
[Link](empsBySalary);
[Link]("\nSorted by ID (descending):");
[Link]([Link]::println);
}
}
// Output:
// Sorted by salary:
// Employee{id=2, name='Bob', salary=45000.00}
// Employee{id=1, name='Alice', salary=60000.00}
// Employee{id=3, name='Charlie', salary=75000.00}
//
// Sorted by name:
// Employee{id=1, name='Alice', salary=60000.00}
// Employee{id=2, name='Bob', salary=45000.00}
// Employee{id=3, name='Charlie', salary=75000.00}
5. Queue and Deque Interfaces
5.1 PriorityQueue
PriorityQueue orders elements based on their natural ordering or a custom Comparator. The head is the least
element according to the ordering.

import [Link].*;
public class PriorityQueueExample {
public static void main(String[] args) {
// Min heap (default)
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
[Link](15);
[Link](5);
[Link](25);
[Link](10);
[Link]("Min Heap polling:");
while(![Link]()) {
[Link]([Link]()); // 5, 10, 15, 25
}
// Max heap (using reversed comparator)
PriorityQueue<Integer> maxHeap =
new PriorityQueue<>([Link]());
[Link](15);
[Link](5);
[Link](25);
[Link](10);
[Link]("\nMax Heap polling:");
while(![Link]()) {
[Link]([Link]()); // 25, 15, 10, 5
}
// Custom objects
PriorityQueue<Task> taskQueue = new PriorityQueue<>(
[Link](Task::getPriority)
);
[Link](new Task("Low priority", 3));
[Link](new Task("High priority", 1));
[Link](new Task("Medium priority", 2));
[Link]("\nTask execution order:");
while(![Link]()) {
[Link]([Link]());
}
}
}
class Task {
private String name;
private int priority;
public Task(String name, int priority) {
[Link] = name;
[Link] = priority;
}
public int getPriority() { return priority; }
@Override
public String toString() {
return name + " (Priority: " + priority + ")";
}
}
// Output:
// Min Heap polling:
// 5
// 10
// 15
// 25
//
// Max Heap polling:
// 25
// 15
// 10
// 5
//
// Task execution order:
// High priority (Priority: 1)
// Medium priority (Priority: 2)
// Low priority (Priority: 3)
5.2 ArrayDeque
ArrayDeque is a resizable-array implementation of Deque. It's faster than LinkedList for most operations.

import [Link].*;
public class ArrayDequeExample {
public static void main(String[] args) {
// Using as Queue (FIFO)
ArrayDeque<String> queue = new ArrayDeque<>();
[Link]("First");
[Link]("Second");
[Link]("Third");
[Link]("Queue: " + queue);
[Link]("Poll: " + [Link]());
[Link]("After poll: " + queue);
// Using as Stack (LIFO)
ArrayDeque<String> stack = new ArrayDeque<>();
[Link]("Bottom");
[Link]("Middle");
[Link]("Top");
[Link]("\nStack: " + stack);
[Link]("Pop: " + [Link]());
[Link]("After pop: " + stack);
// Deque operations (both ends)
ArrayDeque<Integer> deque = new ArrayDeque<>();
[Link](1);
[Link](2);
[Link](0);
[Link](3);
[Link]("\nDeque: " + deque); // [0, 1, 2, 3]
[Link]("First: " + [Link]());
[Link]("Last: " + [Link]());
[Link]();
[Link]();
[Link]("After removal: " + deque); // [1, 2]
// Peek operations (don't remove)
[Link]("PeekFirst: " + [Link]());
[Link]("PeekLast: " + [Link]());
// Iteration in both directions
[Link]("\nForward iteration:");
for(Integer num : deque) {
[Link](num + " ");
}
[Link]("\nReverse iteration:");
Iterator<Integer> descIter = [Link]();
while([Link]()) {
[Link]([Link]() + " ");
}
}
}
6. Map Interface
Map is an object that maps keys to values. A map cannot contain duplicate keys.

6.1 HashMap
HashMap uses hashing to store key-value pairs. It provides constant-time performance for get and put
operations.

import [Link].*;
public class HashMapExample {
public static void main(String[] args) {
// Creating HashMap
HashMap<String, Integer> scores = new HashMap<>();
// Adding entries
[Link]("Alice", 95);
[Link]("Bob", 87);
[Link]("Charlie", 92);
[Link]("Alice", 98); // Updates value for existing key
[Link]("Scores: " + scores);
// Accessing values
[Link]("Alice's score: " + [Link]("Alice"));
// Checking existence
[Link]("Contains Bob? " + [Link]("Bob"));
[Link]("Contains score 92? " + [Link](92));
// Removing
[Link]("Bob");
[Link]("After removal: " + scores);
// Size
[Link]("Size: " + [Link]());
// Iterating over keys
[Link]("\nIterating keys:");
for(String name : [Link]()) {
[Link](name + ": " + [Link](name));
}
// Iterating over values
[Link]("\nValues:");
for(Integer score : [Link]()) {
[Link](score);
}
// Iterating over entries
[Link]("\nEntries:");
for([Link]<String, Integer> entry : [Link]()) {
[Link]([Link]() + " = " + [Link]());
}
// Java 8+ forEach
[Link]("\nUsing forEach:");
[Link]((name, score) ->
[Link](name + ": " + score));
// putIfAbsent
[Link]("David", 88);
[Link]("Alice", 100); // Won't update (key exists)
// getOrDefault
[Link]("Eve's score: " +
[Link]("Eve", 0));
// compute methods
[Link]("Alice", (k, v) -> v + 2); // Increment by 2
[Link]("Charlie", (k, v) -> v + 5);
[Link]("Eve", k -> 85);
[Link]("\nAfter compute operations: " + scores);
// merge
[Link]("Alice", 5, Integer::sum); // Add 5 to existing
[Link]("After merge: " + scores);
}
}
6.2 HashMap Internals
// How HashMap works internally
1. Hash Function:
- When you put(key, value), HashMap calculates hash code
- hash = [Link]()
- Applies hash function to get bucket index
2. Bucket Structure:
- Array of buckets (default size = 16)
- Each bucket contains a linked list (or tree after Java 8)
3. Collision Handling:
- If two keys hash to same bucket → collision
- Before Java 8: Linked list
- After Java 8: Linked list → Tree (if > 8 elements)
4. Load Factor:
- Default = 0.75
- When size > capacity * loadFactor, rehashing occurs
- Capacity doubles
5. Time Complexity:
- Average: O(1) for get/put
- Worst: O(n) with many collisions (rare with good hash)
- With tree: O(log n) worst case
Example of collision:

class Student {
String name;
int rollNo;
public Student(String name, int rollNo) {
[Link] = name;
[Link] = rollNo;
}
@Override
public int hashCode() {
// Poor hash function - causes collisions
return rollNo % 10; // Only 10 possible values!
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(obj == null || getClass() != [Link]()) return false;
Student other = (Student) obj;
return rollNo == [Link] &&
[Link](name, [Link]);
}
@Override
public String toString() {
return name + "(" + rollNo + ")";
}
}
public class HashMapInternals {
public static void main(String[] args) {
HashMap<Student, String> map = new HashMap<>();
// These will likely cause collisions
[Link](new Student("Alice", 101), "A Grade");
[Link](new Student("Bob", 111), "B Grade"); // Same hash!
[Link](new Student("Charlie", 121), "A Grade"); // Same hash!
[Link](map);
// Good hash function using [Link]
// return [Link](name, rollNo);
}
}
6.3 LinkedHashMap
LinkedHashMap maintains insertion order (or access order). It's a hash table with a linked list running through it.

import [Link].*;
public class LinkedHashMapExample {
public static void main(String[] args) {
// Insertion order maintained
LinkedHashMap<String, Integer> insertionOrder =
new LinkedHashMap<>();
[Link]("Banana", 2);
[Link]("Apple", 5);
[Link]("Orange", 3);
[Link]("Mango", 4);
[Link]("Insertion order:");
[Link]((k, v) ->
[Link](k + ": " + v));
// Access order (LRU Cache)
LinkedHashMap<String, Integer> accessOrder =
new LinkedHashMap<>(16, 0.75f, true); // true = access-order
[Link]("A", 1);
[Link]("B", 2);
[Link]("C", 3);
[Link]("\nInitial: " + accessOrder);
// Access element A
[Link]("A");
[Link]("After accessing A: " + accessOrder);
// A moves to end
// LRU Cache implementation
LRUCache<String, String> cache = new LRUCache<>(3);
[Link]("1", "One");
[Link]("2", "Two");
[Link]("3", "Three");
[Link]("\nCache: " + cache);
[Link]("1"); // Access 1
[Link]("4", "Four"); // Evicts least recently used (2)
[Link]("After adding 4: " + cache);
[Link]("Contains 2? " + [Link]("2"));
}
}
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true);
[Link] = capacity;
}
@Override
protected boolean removeEldestEntry([Link]<K, V> eldest) {
return size() > capacity;
}
}
// Output:
// Insertion order:
// Banana: 2
// Apple: 5
// Orange: 3
// Mango: 4
//
// Initial: {A=1, B=2, C=3}
// After accessing A: {B=2, C=3, A=1}
//
// Cache: {1=One, 2=Two, 3=Three}
// After adding 4: {2=Two, 1=One, 4=Four}
// Contains 2? false
6.4 TreeMap
TreeMap stores entries in sorted order based on keys. It's implemented using a Red-Black tree.

import [Link].*;
public class TreeMapExample {
public static void main(String[] args) {
// Natural ordering (ascending)
TreeMap<Integer, String> map = new TreeMap<>();
[Link](5, "Five");
[Link](2, "Two");
[Link](8, "Eight");
[Link](1, "One");
[Link](10, "Ten");
[Link]("TreeMap: " + map);
// Output: {1=One, 2=Two, 5=Five, 8=Eight, 10=Ten}
// NavigableMap methods
[Link]("First key: " + [Link]());
[Link]("Last key: " + [Link]());
[Link]("Lower key than 5: " + [Link](5));
[Link]("Higher key than 5: " + [Link](5));
// Entry methods
[Link]("First entry: " + [Link]());
[Link]("Last entry: " + [Link]());
// Range views
[Link]("HeadMap (<5): " + [Link](5));
[Link]("TailMap (>=5): " + [Link](5));
[Link]("SubMap [2,8): " + [Link](2, 8));
// Descending map
[Link]("Descending: " + [Link]());
// Poll operations
[Link]("PollFirstEntry: " + [Link]());
[Link]("PollLastEntry: " + [Link]());
[Link]("After polling: " + map);
// Custom comparator (descending order)
TreeMap<String, Integer> reverseMap =
new TreeMap<>([Link]());
[Link]("Alice", 95);
[Link]("Bob", 87);
[Link]("Charlie", 92);
[Link]("\nReverse order: " + reverseMap);
// Use case: Frequency map
String text = "hello world";
TreeMap<Character, Integer> freqMap = new TreeMap<>();
for(char c : [Link]()) {
if(c != ' ') {
[Link](c, 1, Integer::sum);
}
}
[Link]("\nCharacter frequency: " + freqMap);
}
}
// Output:
// TreeMap: {1=One, 2=Two, 5=Five, 8=Eight, 10=Ten}
// First key: 1
// Last key: 10
// Lower key than 5: 2
// Higher key than 5: 8
// HeadMap (<5): {1=One, 2=Two}
// TailMap (>=5): {5=Five, 8=Eight, 10=Ten}
// SubMap [2,8): {2=Two, 5=Five}
7. ConcurrentHashMap and Thread-Safe Collections
7.1 ConcurrentHashMap
ConcurrentHashMap allows concurrent access without locking the entire map. It's thread-safe and more efficient
than synchronized HashMap.

import [Link].*;
import [Link].*;
public class ConcurrentHashMapExample {
public static void main(String[] args) throws InterruptedException {
// Thread-safe without synchronization
ConcurrentHashMap<String, Integer> map =
new ConcurrentHashMap<>();
// Multiple threads updating
Runnable task1 = () -> {
for(int i = 0; i < 1000; i++) {
[Link]("counter", 1, Integer::sum);
}
};
Runnable task2 = () -> {
for(int i = 0; i < 1000; i++) {
[Link]("counter", 1, Integer::sum);
}
};
Thread t1 = new Thread(task1);
Thread t2 = new Thread(task2);
[Link]();
[Link]();
[Link]();
[Link]();
[Link]("Counter: " + [Link]("counter"));
// Output: 2000 (always correct due to thread-safety)
// Atomic operations
ConcurrentHashMap<String, Integer> scores =
new ConcurrentHashMap<>();
[Link]("Alice", 100);
// putIfAbsent - atomic
[Link]("Bob", 90);
[Link]("Alice", 95); // Won't update
// compute - atomic
[Link]("Alice", (k, v) -> v + 10);
// computeIfPresent - atomic
[Link]("Bob", (k, v) -> v + 5);
// computeIfAbsent - atomic
[Link]("Charlie", k -> 85);
[Link]("Scores: " + scores);
// Bulk operations
ConcurrentHashMap<String, Integer> data =
new ConcurrentHashMap<>();
[Link]("A", 1);
[Link]("B", 2);
[Link]("C", 3);
// forEach - parallel
[Link](1, (k, v) ->
[Link](k + "=" + v));
// reduce
int sum = [Link](1, Integer::sum);
[Link]("Sum: " + sum);
// search
String result = [Link](1, (k, v) ->
v > 2 ? k : null);
[Link]("First key with value > 2: " + result);
}
}
// ConcurrentHashMap vs Synchronized HashMap
class ComparisonExample {
public static void main(String[] args) {
// Synchronized HashMap (legacy approach)
Map<String, Integer> syncMap =
[Link](new HashMap<>());
// Locks entire map for each operation
synchronized(syncMap) {
[Link]("key", 1);
[Link]("key");
}
// ConcurrentHashMap (modern approach)
ConcurrentHashMap<String, Integer> concurrentMap =
new ConcurrentHashMap<>();
// No external synchronization needed
// Locks only segment/bucket
[Link]("key", 1);
[Link]("key");
}
}
7.2 Other Thread-Safe Collections
import [Link].*;
import [Link].*;
public class OtherConcurrentCollections {
public static void main(String[] args) throws InterruptedException {
// CopyOnWriteArrayList
// Thread-safe, but expensive for writes
// Good for read-heavy scenarios
CopyOnWriteArrayList<String> cowList =
new CopyOnWriteArrayList<>();
[Link]("A");
[Link]("B");
[Link]("C");
// Iterator doesn't throw ConcurrentModificationException
for(String s : cowList) {
[Link](s);
[Link]("D"); // Safe during iteration
}
[Link]("List: " + cowList);
// ConcurrentLinkedQueue
// Lock-free thread-safe queue
ConcurrentLinkedQueue<Integer> queue =
new ConcurrentLinkedQueue<>();
[Link](1);
[Link](2);
[Link](3);
[Link]("Poll: " + [Link]());
[Link]("Peek: " + [Link]());
// BlockingQueue - producer-consumer pattern
BlockingQueue<String> blockingQueue =
new LinkedBlockingQueue<>(10);
// Producer thread
Thread producer = new Thread(() -> {
try {
for(int i = 0; i < 5; i++) {
[Link]("Item-" + i);
[Link]("Produced: Item-" + i);
[Link](100);
}
} catch(InterruptedException e) {
[Link]().interrupt();
}
});
// Consumer thread
Thread consumer = new Thread(() -> {
try {
for(int i = 0; i < 5; i++) {
String item = [Link]();
[Link]("Consumed: " + item);
[Link](200);
}
} catch(InterruptedException e) {
[Link]().interrupt();
}
});
[Link]();
[Link]();
[Link]();
[Link]();
// ConcurrentSkipListMap
// Sorted concurrent map
ConcurrentSkipListMap<Integer, String> skipListMap =
new ConcurrentSkipListMap<>();
[Link](3, "Three");
[Link](1, "One");
[Link](2, "Two");
[Link]("\nSkipListMap: " + skipListMap);
// Output: {1=One, 2=Two, 3=Three}
}
}
8. Iterators and Iteration Techniques
import [Link].*;
public class IteratorExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>(
[Link]("A", "B", "C", "D", "E"));
// 1. Iterator
[Link]("Using Iterator:");
Iterator<String> iterator = [Link]();
while([Link]()) {
String element = [Link]();
[Link](element);
// Remove during iteration
if([Link]("C")) {
[Link](); // Safe removal
}
}
[Link]("After removal: " + list);
// 2. ListIterator (bidirectional)
[Link]("\nUsing ListIterator:");
ListIterator<String> listIter = [Link]();
while([Link]()) {
int index = [Link]();
String element = [Link]();
[Link](index + ": " + element);
// Modification during iteration
if([Link]("B")) {
[Link]("B_modified");
}
}
// Backward iteration
[Link]("\nBackward:");
while([Link]()) {
[Link]([Link]());
}
// 3. Enhanced for loop
[Link]("\nEnhanced for:");
for(String s : list) {
[Link](s);
// Cannot modify collection here!
}
// 4. forEach (Java 8+)
[Link]("\nforEach:");
[Link](s -> [Link](s));
// 5. Stream API
[Link]("\nStream:");
[Link]()
.filter(s -> [Link]() > 1)
.forEach([Link]::println);
// 6. Spliterator (for parallel processing)
[Link]("\nSpliterator:");
Spliterator<String> spliterator = [Link]();
[Link]([Link]::println);
// Fail-fast vs Fail-safe
demonstrateFailFast();
demonstrateFailSafe();
}
static void demonstrateFailFast() {
List<Integer> numbers = new ArrayList<>(
[Link](1, 2, 3, 4, 5));
try {
for(Integer num : numbers) {
[Link](num + " ");
[Link](10); // ConcurrentModificationException!
}
} catch(ConcurrentModificationException e) {
[Link]("\nFail-fast: " + [Link]().getSimpleName());
}
}
static void demonstrateFailSafe() {
// CopyOnWriteArrayList is fail-safe
CopyOnWriteArrayList<Integer> numbers =
new CopyOnWriteArrayList<>([Link](1, 2, 3, 4, 5));
[Link]("\nFail-safe iteration:");
for(Integer num : numbers) {
[Link](num + " ");
[Link](10); // No exception!
}
[Link]("\nFinal list: " + numbers);
}
}
9. Sorting and Comparators
import [Link].*;
import [Link].*;
class Student {
private String name;
private int age;
private double gpa;
public Student(String name, int age, double gpa) {
[Link] = name;
[Link] = age;
[Link] = gpa;
}
// Getters
public String getName() { return name; }
public int getAge() { return age; }
public double getGpa() { return gpa; }
@Override
public String toString() {
return [Link]("%s (Age: %d, GPA: %.2f)",
name, age, gpa);
}
}
public class SortingExample {
public static void main(String[] args) {
// 1. Sorting primitives and strings
List<Integer> numbers = [Link](5, 2, 8, 1, 9);
[Link](numbers);
[Link]("Sorted numbers: " + numbers);
// Reverse order
[Link](numbers, [Link]());
[Link]("Reverse: " + numbers);
// 2. Sorting with Comparable
List<String> names = [Link]("Charlie", "Alice", "Bob");
[Link](names); // Uses String's compareTo
[Link]("Sorted names: " + names);
// 3. Sorting with Comparator
List<Student> students = [Link](
new Student("Alice", 20, 3.8),
new Student("Bob", 22, 3.5),
new Student("Charlie", 19, 3.9),
new Student("David", 21, 3.7)
);
// Sort by name
[Link]([Link](Student::getName));
[Link]("\nSorted by name:");
[Link]([Link]::println);
// Sort by age
[Link]([Link](Student::getAge));
[Link]("\nSorted by age:");
[Link]([Link]::println);
// Sort by GPA (descending)
[Link]([Link](Student::getGpa)
.reversed());
[Link]("\nSorted by GPA (desc):");
[Link]([Link]::println);
// 4. Multiple criteria sorting
[Link](
[Link](Student::getGpa).reversed()
.thenComparingInt(Student::getAge)
.thenComparing(Student::getName)
);
[Link]("\nMultiple criteria (GPA desc, Age, Name):");
[Link]([Link]::println);
// 5. Null-safe sorting
List<String> listWithNulls =
[Link]("Alice", null, "Bob", null, "Charlie");
[Link]([Link](
[Link]()));
[Link]("\nNulls last: " + listWithNulls);
[Link]([Link](
[Link]()));
[Link]("Nulls first: " + listWithNulls);
// 6. Custom Comparator
Comparator<Student> customComparator = new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// GPA > 3.7 considered honors
boolean s1Honors = [Link]() > 3.7;
boolean s2Honors = [Link]() > 3.7;
if(s1Honors && !s2Honors) return -1;
if(!s1Honors && s2Honors) return 1;
return [Link]([Link](), [Link]());
}
};
[Link]([Link]());
[Link]("\nCustom sort (honors first):");
[Link]([Link]::println);
// 7. Stream sorting
List<Student> topStudents = [Link]()
.filter(s -> [Link]() > 3.6)
.sorted([Link](Student::getGpa).reversed())
.collect([Link]());
[Link]("\nTop students (GPA > 3.6):");
[Link]([Link]::println);
// 8. Array sorting
int[] arr = {5, 2, 8, 1, 9};
[Link](arr);
[Link]("\nSorted array: " + [Link](arr));
// Parallel sort (for large arrays)
[Link](arr);
}
}
10. Performance Comparison and Best Practices
10.1 Time Complexity Comparison
Operation ArrayList LinkedList HashSet TreeSet

Add (end) O(1)* O(1) O(1) O(log n)

Add (middle) O(n) O(1)** O(1) O(log n)

Remove O(n) O(1)** O(1) O(log n)

Get (index) O(1) O(n) N/A N/A

Contains O(n) O(n) O(1) O(log n)

Iteration O(n) O(n) O(n) O(n)

* Amortized ** If you have the node reference

10.2 Map Performance


Operation HashMap LinkedHashMap TreeMap ConcurrentHashMap

Get O(1) O(1) O(log n) O(1)

Put O(1) O(1) O(log n) O(1)

Remove O(1) O(1) O(log n) O(1)

Ordering None Insertion Sorted None

Thread-safe No No No Yes
10.3 Best Practices
Choose the right collection:
Use ArrayList for random access, LinkedList for frequent insertions/deletions, HashSet for uniqueness, TreeSet
for sorted uniqueness

Specify initial capacity:


new ArrayList<>(1000) - reduces resizing overhead

Use diamond operator:


List list = new ArrayList<>() - Java 7+

Prefer interfaces:
List list = new ArrayList<>() not ArrayList list

Use isEmpty() over size():


if([Link]()) is clearer than if([Link]() == 0)

Use enhanced for loop:


for(String s : list) - cleaner and less error-prone

Remove during iteration carefully:


Use [Link]() not [Link]()

Override hashCode and equals:


Required for proper behavior in HashMap/HashSet

Use immutable collections:


[Link](list) when needed

Consider concurrent collections:


Use ConcurrentHashMap for multi-threaded scenarios
11. Advanced Topics and Real-world Examples
11.1 Custom Collection Implementation
import [Link].*;
// Custom bounded stack
class BoundedStack<E> extends ArrayList<E> {
private final int maxSize;
public BoundedStack(int maxSize) {
super();
[Link] = maxSize;
}
public void push(E element) {
if(size() >= maxSize) {
throw new IllegalStateException("Stack is full");
}
add(element);
}
public E pop() {
if(isEmpty()) {
throw new EmptyStackException();
}
return remove(size() - 1);
}
public E peek() {
if(isEmpty()) {
throw new EmptyStackException();
}
return get(size() - 1);
}
}
public class CustomCollectionExample {
public static void main(String[] args) {
BoundedStack<String> stack = new BoundedStack<>(3);
[Link]("First");
[Link]("Second");
[Link]("Third");
[Link]("Peek: " + [Link]());
[Link]("Pop: " + [Link]());
[Link]("Size: " + [Link]());
try {
[Link]("Fourth");
[Link]("Fifth"); // Will throw exception
} catch(IllegalStateException e) {
[Link]("Error: " + [Link]());
}
}
}
11.2 Real-world Use Cases
import [Link].*;
import [Link].*;
// Use Case 1: Word Frequency Counter
class WordFrequency {
public static Map<String, Integer> countWords(String text) {
Map<String, Integer> frequency = new HashMap<>();
String[] words = [Link]()
.split("\\W+");
for(String word : words) {
if(![Link]()) {
[Link](word, 1, Integer::sum);
}
}
return frequency;
}
public static void main(String[] args) {
String text = "Java is great. Java is powerful. " +
"Learn Java programming.";
Map<String, Integer> freq = countWords(text);
// Get top 3 words
[Link]().stream()
.sorted([Link].<String, Integer>comparingByValue()
.reversed())
.limit(3)
.forEach(e -> [Link](
[Link]() + ": " + [Link]()));
}
}
// Use Case 2: Cache with LRU eviction
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true);
[Link] = capacity;
}
@Override
protected boolean removeEldestEntry([Link]<K, V> eldest) {
return size() > capacity;
}
}
// Use Case 3: Graph representation
class Graph {
private Map<String, List<String>> adjacencyList;
public Graph() {
adjacencyList = new HashMap<>();
}
public void addVertex(String vertex) {
[Link](vertex, new ArrayList<>());
}
public void addEdge(String source, String destination) {
[Link](source).add(destination);
[Link](destination).add(source); // Undirected
}
public void printGraph() {
for([Link]<String, List<String>> entry :
[Link]()) {
[Link]([Link]() + " -> " +
[Link]());
}
}
// BFS traversal
public List<String> bfs(String start) {
List<String> result = new ArrayList<>();
Set<String> visited = new HashSet<>();
Queue<String> queue = new LinkedList<>();
[Link](start);
[Link](start);
while(![Link]()) {
String vertex = [Link]();
[Link](vertex);
for(String neighbor : [Link](vertex)) {
if(![Link](neighbor)) {
[Link](neighbor);
[Link](neighbor);
}
}
}
return result;
}
}
// Use Case 4: Grouping data
class DataGrouping {
static class Employee {
String name;
String department;
double salary;
public Employee(String name, String dept, double salary) {
[Link] = name;
[Link] = dept;
[Link] = salary;
}
public String getDepartment() { return department; }
public double getSalary() { return salary; }
public String getName() { return name; }
}
public static void main(String[] args) {
List<Employee> employees = [Link](
new Employee("Alice", "IT", 80000),
new Employee("Bob", "HR", 60000),
new Employee("Charlie", "IT", 90000),
new Employee("David", "HR", 65000)
);
// Group by department
Map<String, List<Employee>> byDept = [Link]()
.collect([Link](Employee::getDepartment));
[Link]("By Department:");
[Link]((dept, emps) -> {
[Link](dept + ":");
[Link](e -> [Link](" " + [Link]()));
});
// Average salary by department
Map<String, Double> avgSalary = [Link]()
.collect([Link](
Employee::getDepartment,
[Link](Employee::getSalary)
));
[Link]("\nAverage Salary:");
[Link]((dept, avg) ->
[Link]("%s: $%.2f\n", dept, avg));
}
}
11.3 Common Pitfalls and How to Avoid Them
Pitfall: ConcurrentModificationException

Solution: Don't modify collection while iterating. Use [Link]() or concurrent collections.

Pitfall: Not overriding hashCode and equals

Solution: Objects used as HashMap keys must override both methods consistently.

Pitfall: Using == instead of equals()

Solution: Always use equals() for object comparison, not ==.

Pitfall: Assuming ordering in HashSet/HashMap

Solution: These don't guarantee order. Use LinkedHashSet/LinkedHashMap for insertion order.

Pitfall: Memory leaks with listeners

Solution: Remove listeners when done. Use WeakHashMap if appropriate.

Pitfall: Not specifying initial capacity

Solution: Can cause multiple resizings. Specify capacity if size is known.

Pitfall: Using Vector/Hashtable

Solution: These are legacy. Use ArrayList and HashMap instead.

Pitfall: Null handling

Solution: HashMap allows one null key, HashSet one null element. TreeMap/TreeSet don't allow nulls.
Summary and Quick Reference
Collection Framework Summary:
1. WHEN TO USE WHAT:
- ArrayList: Fast random access, infrequent insertions
- LinkedList: Frequent insertions/deletions, queue operations
- HashSet: Fast lookup, no duplicates, no order needed
- LinkedHashSet: No duplicates, maintain insertion order
- TreeSet: No duplicates, sorted order
- HashMap: Fast key-value lookup, no order needed
- LinkedHashMap: Key-value pairs, maintain insertion order
- TreeMap: Key-value pairs, sorted by keys
- PriorityQueue: Process elements by priority
- ArrayDeque: Stack or queue operations
2. THREAD SAFETY:
- ConcurrentHashMap: Thread-safe map
- CopyOnWriteArrayList: Thread-safe list (read-heavy)
- [Link](): Synchronized wrappers
3. KEY CONCEPTS:
- Always override hashCode() and equals() for HashMap/HashSet keys
- Use interfaces (List, Set, Map) not implementations
- Prefer enhanced for loop or forEach for iteration
- Use Streams for complex data transformations
- Specify initial capacity when size is known
- Use Comparator for flexible sorting
4. PERFORMANCE TIPS:
- HashMap/HashSet: O(1) for basic operations
- TreeMap/TreeSet: O(log n) for operations
- ArrayList: O(1) get, O(n) insert/remove
- LinkedList: O(n) get, O(1) insert/remove (with node ref)
5. MODERN JAVA (8+):
- forEach(): [Link](item -> ...)
- Stream API: [Link]().filter().map().collect()
- Collectors: [Link](), groupingBy(), etc.
- Method references: [Link]([Link]::println)

This guide covered the Java Collections Framework from basics to advanced topics. Practice these examples,
understand the trade-offs between different collections, and choose the right tool for each use case. Keep this
PDF as a quick reference for your daily development work.

You might also like