Java Collections Framework - alazeroone/software-concepts GitHub Wiki
This page is designed for quick interview prep on the Java Collections Framework, covering key interfaces, classes, their characteristics, and generally used methods, with detailed sections for each.
- Key Java Collections Framework Overview
-
Detailed Interface & Class Explanations
- Iterable Interface
- Collection Interface
- List Interface
- ArrayList Class
- LinkedList Class
- Vector Class
- Stack Class
- Set Interface
- HashSet Class
- LinkedHashSet Class
- TreeSet Class
- Queue Interface
- PriorityQueue Class
- ArrayDeque Class
- BlockingQueue Interface
- Map Interface
- HashMap Class
- LinkedHashMap Class
- TreeMap Class
- HashTable Class
- ConcurrentHashMap Class
- Collections (Utility Class)
- Arrays (Utility Class)
- Interview Quick Tips
Interface/Class | Implements/Extends | Key Characteristics | Common Operations | Notes & Use Cases | JavaDoc Link |
---|---|---|---|---|---|
Iterable | - | Enables for-each loop iteration. |
iterator() (for iteration). |
Root interface for all collections that can be iterated. | JavaDoc: Iterable |
Collection | Iterable |
Root of collection hierarchy (excluding Map ). Allows duplicates. Order not guaranteed. |
add() , remove() , contains() , size() , isEmpty() , clear() , toArray() . Iteration via Iterator or for-each . |
Defines common operations for groups of objects. Seldom instantiated directly; typically used for polymorphism. | JavaDoc: Collection |
List | Collection |
Ordered (insertion order). Allows duplicates. Index-based access. Allows null elements. |
add() , add(index) , get(index) , set(index) , remove() , remove(index) , indexOf() , lastIndexOf() , contains() , size() . Iteration. Sortable via Collections.sort() . |
For ordered sequences where element position matters. Commonly used. | JavaDoc: List |
ArrayList |
List , RandomAccess
|
Resizable array implementation. Ordered. Allows duplicates. Not thread-safe. Fast random access (O(1)). Slow insertions/deletions in middle (O(n)). Allows null elements. |
add() , get() , set() , remove() (by index or object), indexOf() , contains() . Iteration. Sortable via Collections.sort() . |
Default List choice. Best for frequent reads and fewer middle insertions/deletions. |
JavaDoc: ArrayList |
LinkedList |
List , Deque
|
Doubly-linked list implementation. Ordered. Allows duplicates. Not thread-safe. Fast insertions/deletions (O(1)). Slow random access (O(n)). Allows null elements. |
add() , addFirst() , addLast() , remove() , removeFirst() , removeLast() , get() , getFirst() , getLast() , indexOf() , contains() , poll() , push() , pop() . Iteration. Sortable via Collections.sort() . |
Best for frequent additions/removals from the ends. Can also act as a Queue or Stack due to Deque implementation. |
JavaDoc: LinkedList |
Vector | List |
Resizable array. Synchronized (thread-safe). Allows duplicates. Legacy. Slower than ArrayList . Allows null elements. |
add() , get() , set() , remove() , indexOf() , contains() . Iteration. addElement() , firstElement() , lastElement() . Sortable. |
Legacy class. Avoid for new code; prefer ArrayList with Collections.synchronizedList() wrapper or CopyOnWriteArrayList for concurrent use. |
JavaDoc: Vector |
Stack | Vector |
LIFO (Last-In, First-Out) implementation. Synchronized. Legacy. Allows null elements. |
push() , pop() , peek() , empty() , search() . |
Legacy class. Avoid for new code; prefer ArrayDeque or LinkedList as a stack. |
JavaDoc: Stack |
Set | Collection |
No duplicate elements. Order not guaranteed (unless specific implementations). Allows at most one null element (implementation dependent). |
add() (returns false if duplicate), remove() , contains() , size() . Iteration. |
For collections of unique elements. | JavaDoc: Set |
HashSet | Set |
Uses hash table. Unordered, unsorted. Not thread-safe. Fast (O(1)) for add/remove/contains. Allows one null element. |
add() , remove() , contains() . Iteration (order not guaranteed). |
Default Set choice. Best for performance when order is not important. |
JavaDoc: HashSet |
LinkedHashSet | Set |
Hash table + linked list. Maintains insertion order. Not thread-safe. Slower than HashSet . Allows one null element. |
add() , remove() , contains() . Iteration (insertion order guaranteed). |
Preserves the order in which elements were inserted. Useful for building caches or iterating in predictable order. | JavaDoc: LinkedHashSet |
TreeSet |
SortedSet , NavigableSet
|
Red-Black tree. Elements are sorted (natural order or Comparator ). No duplicates. Not thread-safe. Slower than HashSet (O(log n)). Does not allow null without a Comparator . |
add() , remove() , contains() . first() , last() , headSet() , tailSet() , subSet() . Iteration (sorted order). |
For unique, sorted elements. Good for range-based operations. Requires elements to be Comparable or provide a Comparator . |
JavaDoc: TreeSet |
Queue | Collection |
Elements for processing. Typically FIFO (First-In, First-Out). Allows duplicates. Allows null elements (implementation dependent). |
offer() , poll() , peek() (returns special value); add() , remove() , element() (throws exception). Iteration. |
Basic queue operations. | JavaDoc: Queue |
PriorityQueue | Queue |
Priority-based ordering (natural order or Comparator ). Allows duplicates. Not thread-safe. Does not allow null elements. |
offer() , poll() , peek() . Iteration (not guaranteed order, but poll is by priority). |
Elements retrieved by priority, not strict insertion order. Useful for task scheduling. | JavaDoc: PriorityQueue |
ArrayDeque | Deque |
Resizable array. Can be used as a Stack (LIFO) or Queue (FIFO). Allows duplicates. Not thread-safe. Does not allow null elements. |
addFirst() , addLast() , removeFirst() , removeLast() , peekFirst() , peekLast() , offerFirst() , offerLast() , pollFirst() , pollLast() , push() , pop() , peek() . Iteration. |
More efficient than LinkedList for most deque operations when elements are added/removed from both ends. No capacity restrictions. |
JavaDoc: ArrayDeque |
BlockingQueue | Queue |
Supports operations that wait for space/elements. Thread-safe. No null elements. |
put() , take() (blocking); offer(timeout) , poll(timeout) (timed blocking). Iteration. |
Used in concurrent producer-consumer patterns. Implementations include ArrayBlockingQueue , LinkedBlockingQueue . |
JavaDoc: BlockingQueue |
Map | - | Key-value pairs. Unique keys. Values can be duplicated. Order not guaranteed (unless specific implementations). |
put(K, V) , get(K) , remove(K) , containsKey(K) , containsValue(V) , keySet() , values() , entrySet() , size() , isEmpty() , clear() . Iteration via keySet() , values() , entrySet() . |
Not part of Collection hierarchy. For mapping unique keys to values. |
JavaDoc: Map |
HashMap | Map |
Uses hash table. Unordered, unsorted. Not thread-safe. Fast (O(1)) for put/get. Allows one null key, multiple null values. |
put() , get() , remove() , containsKey() . Iteration of keys/values/entries (order not guaranteed). |
Default Map choice. Best for performance when order is not important. |
JavaDoc: HashMap |
LinkedHashMap | Map |
Hash table + linked list. Maintains insertion order or access order. Not thread-safe. Slower than HashMap . Allows one null key, multiple null values. |
put() , get() , remove() . Iteration (insertion order guaranteed). Configurable for LRU cache. |
Preserves the order in which entries were inserted (or last accessed if configured for LRU cache). | JavaDoc: LinkedHashMap |
TreeMap |
SortedMap , NavigableMap
|
Red-black tree. Keys are sorted (natural order or Comparator ). Not thread-safe. Slower than HashMap (O(log n)). Does not allow null keys without a Comparator , allows null values. |
put() , get() , remove() . firstKey() , lastKey() , headMap() , tailMap() , subMap() . Iteration (sorted order). |
For sorted key-value pairs. Keys must be Comparable or provide a Comparator . Good for range queries. |
JavaDoc: TreeMap |
HashTable | Map |
Synchronized HashMap . Legacy. Slower than HashMap . Does not allow null keys or values. |
put() , get() , remove() . Iteration. |
Legacy class. Avoid for new code; prefer ConcurrentHashMap . |
JavaDoc: Hashtable |
ConcurrentHashMap | ConcurrentMap |
Thread-safe, high-performance Map . Segmented locking or fine-grained locking. Allows null values, but not null keys. |
put() , get() , remove() , putIfAbsent() , compute() , merge() . Iteration (weakly consistent). |
Best for concurrent environments; offers much better scalability than HashTable or synchronized HashMap . |
JavaDoc: ConcurrentHashMap |
Collections | Utility Class | Provides static methods for operating on or returning collections. |
sort(List list) , binarySearch(List list, Object key) , reverse() , shuffle() , frequency() , min() , max() , synchronizedList() , unmodifiableList() . |
Utility methods (e.g., sorting, creating synchronized/unmodifiable views, thread-safe wrappers for unsynchronized collections). | JavaDoc: Collections |
Arrays | Utility Class | Provides static methods for manipulating arrays. |
sort(array) , binarySearch(array, key) , equals() , fill() , asList(T... a) . |
Utility methods for array manipulation. Often used to convert arrays to List s or vice-versa. |
JavaDoc: Arrays |
Here you'll find more in-depth descriptions and practical code examples for each key component of the Java Collections Framework.
Purpose: The Iterable
interface is the root of the iteration hierarchy in Java. Any class that implements Iterable
can be the target of the "for-each" loop. It ensures that an object can be iterated over, providing access to its elements one by one.
Key Operations & Sample Use:
-
Iterator: Provides an
Iterator
for explicit control over iteration. -
For-each loop: Syntactic sugar for iterating over
Iterable
objects.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IterableExample {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
// 1. Iterate using for-each loop
System.out.println("--- Using for-each loop ---");
for (String fruit : fruits) {
System.out.println(fruit);
}
// 2. Iterate using Iterator explicitly
System.out.println("\n--- Using Iterator explicitly ---");
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
// Can remove elements safely during iteration (optional)
if (fruit.equals("Banana")) {
iterator.remove();
}
}
System.out.println("Fruits after iterator.remove(): " + fruits); // [Apple, Cherry]
}
}
<details> <summary><h3>Collection Interface</h3></summary>
Purpose: The Collection
interface is the root interface in the collection hierarchy (excluding Map
). It represents a group of objects known as its elements. It defines the common behavior that all other collection types (Lists, Sets, Queues) share.
Key Characteristics:
-
Allows Duplicates: Generally, but specific implementations (like
Set
) override this. - Order Not Guaranteed: Unless a sub-interface or implementation specifies it.
- Allows
null
elements (implementation dependent).
Key Operations & Sample Use:
- Add: Adds elements.
- Remove: Removes elements.
- Search/Check: Checks for element presence, size, emptiness.
- Clear: Removes all elements.
- Convert to Array:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionOpsExample {
public static void main(String[] args) {
Collection<String> names = new ArrayList<>(); // ArrayList implements Collection
// 1. Add elements
names.add("Alice");
names.add("Bob");
names.add("Charlie");
names.add("Alice"); // Duplicate allowed in Collection (for List, not Set)
System.out.println("Collection after adds: " + names); // [Alice, Bob, Charlie, Alice]
// 2. Iterate (via inherited Iterable)
System.out.println("--- Iterating Collection ---");
for (String name : names) {
System.out.println(name);
}
// 3. Search/Check
System.out.println("\nContains 'Bob'? " + names.contains("Bob")); // true
System.out.println("Size: " + names.size()); // 4
System.out.println("Is empty? " + names.isEmpty()); // false
// 4. Remove elements
names.remove("Bob"); // Removes first occurrence
System.out.println("Collection after removing Bob: " + names); // [Alice, Charlie, Alice]
// 5. Convert to array
Object[] nameArray = names.toArray();
System.out.println("Collection as array: " + java.util.Arrays.toString(nameArray)); // [Alice, Charlie, Alice]
// 6. Clear all elements
names.clear();
System.out.println("Collection after clear: " + names + ", empty? " + names.isEmpty()); // [], empty? true
}
}
</details>
Purpose: An ordered collection (sequence). Elements can be accessed by their integer index. List
s allow duplicate elements.
Key Characteristics:
- Ordered: Elements maintain their insertion order.
- Allows Duplicates: You can add the same element multiple times.
- Index-based Access: Elements can be accessed, inserted, or removed by their numerical position.
- Allows
null
elements. -
Not intrinsically sorted: While ordered by insertion,
List
s do not inherently maintain sorted order unlessCollections.sort()
is applied.
Key Operations & Sample Use:
- Add (by index or value):
- Get (by index):
- Set (by index):
- Remove (by index or value):
- Search (by index or value):
- Iterate:
-
Sort: (via
Collections.sort()
)
import java.util.ArrayList;
import java.util.Collections; // For sorting
import java.util.List;
public class ListOpsExample {
public static void main(String[] args) {
List<String> colors = new ArrayList<>(); // ArrayList implements List
// 1. Add elements
colors.add("Red"); // index 0
colors.add("Green"); // index 1
colors.add("Blue"); // index 2
colors.add("Red"); // index 3 (duplicate allowed)
colors.add(1, "Yellow"); // Insert Yellow at index 1, shifting others
System.out.println("List after adds: " + colors); // [Red, Yellow, Green, Blue, Red]
// 2. Iterate
System.out.println("--- Iterating List ---");
for (String color : colors) {
System.out.println(color);
}
// 3. Search/Retrieve
System.out.println("\nElement at index 2: " + colors.get(2)); // Green
System.out.println("First index of 'Red': " + colors.indexOf("Red")); // 0
System.out.println("Last index of 'Red': " + colors.lastIndexOf("Red")); // 4
System.out.println("Contains 'Blue'? " + colors.contains("Blue")); // true
// 4. Update (set)
colors.set(2, "Cyan"); // Replace Green with Cyan at index 2
System.out.println("List after set(2, 'Cyan'): " + colors); // [Red, Yellow, Cyan, Blue, Red]
// 5. Remove elements
colors.remove("Red"); // Removes the *first* occurrence of "Red"
System.out.println("List after remove('Red'): " + colors); // [Yellow, Cyan, Blue, Red]
colors.remove(0); // Removes element at index 0 (Yellow)
System.out.println("List after remove(0): " + colors); // [Cyan, Blue, Red]
// 6. Sort
Collections.sort(colors); // Sorts the list alphabetically
System.out.println("List after sort: " + colors); // [Blue, Cyan, Red]
}
}
Purpose: The most commonly used List
implementation, backed by a resizable array.
Key Characteristics:
-
Fast Random Access (O(1)):
get(index)
is very efficient. - Slow Insertions/Deletions in Middle (O(n)): Requires shifting elements.
- Not Thread-Safe:
- Allows
null
elements.
Key Operations & Sample Use:
import java.util.ArrayList;
import java.util.Collections; // For sorting
public class ArrayListOpsExample {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<>();
// 1. Add elements
numbers.add(10);
numbers.add(30);
numbers.add(1, 20); // Add at index 1
numbers.add(null); // Allows null
System.out.println("ArrayList after adds: " + numbers); // [10, 20, 30, null]
// 2. Iterate
System.out.println("--- Iterating ArrayList ---");
for (Integer num : numbers) {
System.out.println(num);
}
// 3. Search/Retrieve
System.out.println("\nElement at index 2: " + numbers.get(2)); // 30
System.out.println("Contains 20? " + numbers.contains(20)); // true
System.out.println("Index of null: " + numbers.indexOf(null)); // 3
// 4. Remove
numbers.remove(new Integer(20)); // Remove by value
System.out.println("ArrayList after removing 20: " + numbers); // [10, 30, null]
numbers.remove(0); // Remove by index
System.out.println("ArrayList after removing at index 0: " + numbers); // [30, null]
// 5. Sort (after making sure no nulls if using natural order sort)
numbers.remove(null); // Remove null for sorting Integer
numbers.add(15);
numbers.add(5);
Collections.sort(numbers);
System.out.println("ArrayList after sort: " + numbers); // [5, 15, 30]
}
}
Purpose: Implements List
and Deque
. It's a doubly-linked list.
Key Characteristics:
- Fast Insertions/Deletions at ends (O(1)): Only requires updating pointers.
- Slow Random Access (O(n)): Requires traversing the list.
- Not Thread-Safe:
- Allows
null
elements. -
Can act as a Queue or Stack: Due to its
Deque
implementation.
Key Operations & Sample Use:
import java.util.Collections;
import java.util.LinkedList;
public class LinkedListOpsExample {
public static void main(String[] args) {
LinkedList<String> tasks = new LinkedList<>();
// 1. Add elements (List and Deque methods)
tasks.add("Task A");
tasks.addLast("Task B"); // Deque: add to end
tasks.addFirst("Task Z"); // Deque: add to beginning
tasks.add(1, "Task X"); // List: add at index
System.out.println("LinkedList after adds: " + tasks); // [Task Z, Task X, Task A, Task B]
// 2. Iterate
System.out.println("--- Iterating LinkedList ---");
for (String task : tasks) {
System.out.println(task);
}
// 3. Search/Retrieve (List and Deque methods)
System.out.println("\nFirst element: " + tasks.getFirst()); // Task Z
System.out.println("Element at index 2: " + tasks.get(2)); // Task A
System.out.println("Contains 'Task B'? " + tasks.contains("Task B")); // true
System.out.println("Index of 'Task X': " + tasks.indexOf("Task X")); // 1
// 4. Remove elements (List and Deque methods)
String removedFirst = tasks.removeFirst(); // Deque: remove from beginning
System.out.println("Removed first: " + removedFirst + ", List: " + tasks); // Task Z, List: [Task X, Task A, Task B]
tasks.remove("Task A"); // List: remove by value
System.out.println("Removed Task A: " + tasks); // [Task X, Task B]
tasks.removeLast(); // Deque: remove from end
System.out.println("Removed last: " + tasks); // [Task X]
// 5. Use as Stack/Queue (demonstrated in general LinkedList example previously)
// tasks.push("StackItem"); // Add to front (LIFO)
// tasks.pop(); // Remove from front (LIFO)
// tasks.offer("QueueItem"); // Add to end (FIFO)
// tasks.poll(); // Remove from front (FIFO)
// 6. Sort
tasks.add("Beta");
tasks.add("Alpha");
Collections.sort(tasks);
System.out.println("LinkedList after sort: " + tasks); // [Alpha, Beta, Task X]
}
}
Purpose: A legacy class similar to ArrayList
but synchronized.
Key Characteristics:
-
Synchronized (Thread-safe): Slower than
ArrayList
in single-threaded scenarios. - Legacy: Generally superseded by modern concurrent collections.
- Allows
null
elements.
Key Operations & Sample Use:
import java.util.Collections;
import java.util.Vector;
public class VectorOpsExample {
public static void main(String[] args) {
Vector<String> animals = new Vector<>();
// 1. Add elements
animals.add("Dog");
animals.addElement("Cat"); // Legacy method, same as add()
animals.add(0, "Lion"); // Add at index
System.out.println("Vector after adds: " + animals); // [Lion, Dog, Cat]
// 2. Iterate
System.out.println("--- Iterating Vector ---");
for (String animal : animals) {
System.out.println(animal);
}
// 3. Search/Retrieve
System.out.println("\nElement at index 1: " + animals.get(1)); // Dog
System.out.println("First element: " + animals.firstElement()); // Lion (legacy method)
System.out.println("Contains 'Cat'? " + animals.contains("Cat")); // true
// 4. Remove
animals.remove("Dog");
System.out.println("Vector after removing Dog: " + animals); // [Lion, Cat]
animals.removeElementAt(0); // Legacy method, removes Lion
System.out.println("Vector after removing at index 0: " + animals); // [Cat]
// 5. Sort
animals.add("Aardvark");
animals.add("Zebra");
Collections.sort(animals);
System.out.println("Vector after sort: " + animals); // [Aardvark, Cat, Zebra]
}
}
Purpose: A legacy LIFO (Last-In, First-Out) stack, extending Vector
.
Key Characteristics:
- LIFO: Last element added is the first one removed.
-
Synchronized: Inherits synchronization from
Vector
. -
Legacy: Not recommended for new code;
ArrayDeque
is preferred. - Allows
null
elements.
Key Operations & Sample Use:
import java.util.Stack;
public class StackOpsExample {
public static void main(String[] args) {
Stack<Character> charStack = new Stack<>();
// 1. Push (Add) elements
charStack.push('A');
charStack.push('B');
charStack.push('C'); // C is now the top
System.out.println("Stack: " + charStack); // [A, B, C]
// 2. Iterate (Note: Iterator order is from bottom to top, not LIFO)
System.out.println("--- Iterating Stack (bottom to top) ---");
for (Character c : charStack) {
System.out.println(c);
}
// 3. Peek (Retrieve top element without removing)
System.out.println("\nPeek: " + charStack.peek()); // C
System.out.println("Stack after peek: " + charStack); // [A, B, C]
// 4. Pop (Remove from top)
System.out.println("Pop: " + charStack.pop()); // C
System.out.println("Stack after pop: " + charStack); // [A, B]
// 5. Search (Returns 1-based distance from top, -1 if not found)
System.out.println("Position of 'A' (1-based from top): " + charStack.search('A')); // 2
System.out.println("Is stack empty? " + charStack.empty()); // false
charStack.pop();
charStack.pop();
System.out.println("Is stack empty now? " + charStack.empty()); // true
}
}
Purpose: A collection that contains no duplicate elements.
Key Characteristics:
-
No Duplicates: Adding a duplicate element has no effect (returns
false
). -
Unordered/Unsorted (generally): Unless a specific implementation (like
LinkedHashSet
orTreeSet
) dictates order. - Allows at most one
null
element (implementation dependent).
Key Operations & Sample Use:
- Add: Adds element (returns boolean indicating if added).
- Remove:
- Search/Check:
- Iterate:
import java.util.HashSet;
import java.util.Set;
public class SetOpsExample {
public static void main(String[] args) {
Set<String> uniqueNames = new HashSet<>(); // HashSet implements Set
// 1. Add elements
uniqueNames.add("Alice");
uniqueNames.add("Bob");
boolean addedAliceAgain = uniqueNames.add("Alice"); // Duplicate, returns false
uniqueNames.add("Charlie");
uniqueNames.add(null); // Allows one null
System.out.println("Set after adds: " + uniqueNames); // [null, Alice, Bob, Charlie] (order may vary)
System.out.println("Was Alice added again? " + addedAliceAgain); // false
// 2. Iterate
System.out.println("--- Iterating Set ---");
for (String name : uniqueNames) {
System.out.println(name);
}
// 3. Search/Check
System.out.println("\nSize: " + uniqueNames.size()); // 4
System.out.println("Contains 'Bob'? " + uniqueNames.contains("Bob")); // true
System.out.println("Contains 'David'? " + uniqueNames.contains("David")); // false
// 4. Remove
boolean removedBob = uniqueNames.remove("Bob");
System.out.println("Removed Bob? " + removedBob + ", Set: " + uniqueNames); // true, Set: [null, Alice, Charlie]
uniqueNames.remove(null);
System.out.println("Removed null? " + uniqueNames); // [Alice, Charlie]
}
}
Purpose: The most commonly used Set
implementation, using a hash table.
Key Characteristics:
- Unordered, Unsorted: Does not maintain any specific order.
- Not Thread-Safe:
- Fast (O(1)) for basic operations on average.
- Allows one
null
element. - Relies on
hashCode()
andequals()
methods of elements for uniqueness.
Key Operations & Sample Use:
import java.util.HashSet;
public class HashSetOpsExample {
public static void main(String[] args) {
HashSet<String> fruits = new HashSet<>();
// 1. Add elements
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Apple"); // No effect, duplicate
fruits.add("Cherry");
fruits.add(null);
System.out.println("HashSet: " + fruits); // Order not guaranteed, e.g., [null, Apple, Banana, Cherry]
// 2. Iterate
System.out.println("--- Iterating HashSet ---");
for (String fruit : fruits) {
System.out.println(fruit); // Order will be arbitrary
}
// 3. Search/Retrieve
System.out.println("\nSize: " + fruits.size()); // 4
System.out.println("Contains 'Banana'? " + fruits.contains("Banana")); // true
System.out.println("Contains null? " + fruits.contains(null)); // true
// 4. Remove
fruits.remove("Apple");
System.out.println("HashSet after removing Apple: " + fruits); // [null, Banana, Cherry] (order still arbitrary)
fruits.remove(null);
System.out.println("HashSet after removing null: " + fruits); // [Banana, Cherry]
}
}
Purpose: Implements Set
using a hash table and a linked list.
Key Characteristics:
- Maintains Insertion Order: Iterates elements in the order they were inserted.
- No Duplicates.
- Not Thread-Safe:
- Slightly slower than
HashSet
due to maintaining the linked list. - Allows one
null
element.
Key Operations & Sample Use:
import java.util.LinkedHashSet;
public class LinkedHashSetOpsExample {
public static void main(String[] args) {
LinkedHashSet<String> orderedFruits = new LinkedHashSet<>();
// 1. Add elements
orderedFruits.add("Apple");
orderedFruits.add("Banana");
orderedFruits.add("Cherry");
orderedFruits.add("Apple"); // No effect, duplicate
orderedFruits.add(null);
System.out.println("LinkedHashSet: " + orderedFruits); // [Apple, Banana, Cherry, null] (Insertion Order)
// 2. Iterate (Guaranteed Insertion Order)
System.out.println("--- Iterating LinkedHashSet ---");
for (String fruit : orderedFruits) {
System.out.println(fruit); // Will print in order: Apple, Banana, Cherry, null
}
// 3. Search/Retrieve
System.out.println("\nSize: " + orderedFruits.size()); // 4
System.out.println("Contains 'Banana'? " + orderedFruits.contains("Banana")); // true
// 4. Remove
orderedFruits.remove("Banana");
System.out.println("LinkedHashSet after removing Banana: " + orderedFruits); // [Apple, Cherry, null] (Order maintained)
}
}
Purpose: Implements Set
using a Red-Black tree.
Key Characteristics:
-
Sorted: Elements are stored in their natural ordering (if
Comparable
) or according to a providedComparator
. - No Duplicates.
- Not Thread-Safe:
- Slower than
HashSet
(O(log n)) for basic operations. -
Does not allow
null
elements unless a customComparator
explicitly handlesnull
. - Elements must be
Comparable
or aComparator
must be provided.
Key Operations & Sample Use:
import java.util.TreeSet;
public class TreeSetOpsExample {
public static void main(String[] args) {
TreeSet<Integer> sortedNumbers = new TreeSet<>();
// 1. Add elements
sortedNumbers.add(5);
sortedNumbers.add(2);
sortedNumbers.add(8);
sortedNumbers.add(2); // No effect, duplicate
// sortedNumbers.add(null); // Throws NullPointerException if no custom comparator
System.out.println("TreeSet (natural order): " + sortedNumbers); // [2, 5, 8]
// 2. Iterate (Guaranteed Sorted Order)
System.out.println("--- Iterating TreeSet ---");
for (Integer num : sortedNumbers) {
System.out.println(num); // Will print in sorted order: 2, 5, 8
}
// 3. Search/Retrieve (including navigation methods)
System.out.println("\nFirst element: " + sortedNumbers.first()); // 2
System.out.println("Last element: " + sortedNumbers.last()); // 8
System.out.println("Ceiling (>=5): " + sortedNumbers.ceiling(5)); // 5
System.out.println("Floor (<=5): " + sortedNumbers.floor(5)); // 5
System.out.println("Contains 5? " + sortedNumbers.contains(5)); // true
// 4. Remove
sortedNumbers.remove(5);
System.out.println("TreeSet after removing 5: " + sortedNumbers); // [2, 8]
}
}
Purpose: A collection designed for holding elements prior to processing. Typically FIFO (First-In, First-Out).
Key Characteristics:
- FIFO (typically): Elements are removed in the order they were added.
- Allows Duplicates.
- Allows
null
elements (implementation dependent). - Provides two sets of methods for common operations: one throws exceptions on failure, the other returns special values (
null
orfalse
).
Key Operations & Sample Use:
- Offer/Add: Inserts an element into the queue.
- Poll/Remove: Retrieves and removes the head of the queue.
- Peek/Element: Retrieves, but does not remove, the head of the queue.
import java.util.LinkedList; // LinkedList also implements Queue
import java.util.Queue;
public class QueueOpsExample {
public static void main(String[] args) {
Queue<String> messages = new LinkedList<>();
// 1. Add elements (offer is preferred for queues as it doesn't throw exception on capacity issues)
messages.offer("Message A"); // Add to tail
messages.offer("Message B");
messages.add("Message C"); // Also adds to tail
System.out.println("Queue: " + messages); // [Message A, Message B, Message C]
// 2. Iterate
System.out.println("--- Iterating Queue ---");
for (String msg : messages) {
System.out.println(msg);
}
// 3. Peek (Retrieve head without removing)
System.out.println("\nNext message (peek): " + messages.peek()); // Message A
System.out.println("Queue after peek: " + messages); // [Message A, Message B, Message C]
// 4. Poll (Retrieve and remove head)
System.out.println("Processing message (poll): " + messages.poll()); // Message A
System.out.println("Queue after poll: " + messages); // [Message B, Message C]
System.out.println("Size: " + messages.size()); // 2
// When queue is empty
System.out.println("Polling empty queue: " + new LinkedList<>().poll()); // null (doesn't throw)
// new LinkedList<>().remove(); // Throws NoSuchElementException
}
}
Purpose: An unbounded priority queue based on a priority heap. Elements are ordered based on their natural ordering or a Comparator
.
Key Characteristics:
- Priority-based Ordering: Elements are retrieved based on their priority (smallest/largest first), not insertion order.
- Allows Duplicates.
- Not Thread-Safe.
-
Does not allow
null
elements. - Elements must be
Comparable
or provide aComparator
.
Key Operations & Sample Use:
import java.util.PriorityQueue;
public class PriorityQueueOpsExample {
public static void main(String[] args) {
PriorityQueue<Integer> pq = new PriorityQueue<>(); // Min-heap by default (smallest element is highest priority)
// 1. Add elements
pq.offer(10);
pq.offer(2);
pq.offer(8);
pq.offer(2); // Duplicates allowed
pq.offer(15);
System.out.println("PriorityQueue elements (internal heap structure, not sorted array): " + pq);
// Output might be like [2, 2, 8, 10, 15] or [2, 8, 2, 10, 15] due to heap structure,
// but 'poll()' will always give the smallest.
// 2. Iterate (Iteration order is NOT guaranteed to be sorted order)
System.out.println("--- Iterating PriorityQueue (order not guaranteed) ---");
for (Integer num : pq) {
System.out.print(num + " ");
}
System.out.println();
// 3. Peek (Retrieve highest priority element without removing)
System.out.println("Highest priority (smallest) element: " + pq.peek()); // 2
// 4. Poll (Retrieve and remove highest priority element)
System.out.println("Processing highest priority: " + pq.poll()); // 2
System.out.println("Next highest priority: " + pq.poll()); // 2
System.out.println("Remaining elements: " + pq); // [8, 10, 15] (order within heap)
pq.offer(1); // Add new highest priority
System.out.println("PQ after adding 1: " + pq);
System.out.println("New highest priority: " + pq.poll()); // 1
}
}
Purpose: A resizable-array implementation of the Deque
(double-ended queue) interface. Can be used as a faster alternative to LinkedList
for implementing both queues and stacks.
Key Characteristics:
- No Capacity Restrictions: Grows as needed.
- Fast O(1) operations at both ends (add/remove first/last).
- Not Thread-Safe.
-
Does not allow
null
elements. - More efficient than
LinkedList
for stack/queue operations.
Key Operations & Sample Use (as Queue and Stack):
import java.util.ArrayDeque;
import java.util.Deque;
public class ArrayDequeOpsExample {
public static void main(String[] args) {
Deque<String> deque = new ArrayDeque<>();
// --- Using ArrayDeque as a Queue (FIFO) ---
// 1. Add elements to the end (tail)
deque.offer("Task 1"); // preferred Queue add method
deque.add("Task 2"); // also adds to tail
deque.offerLast("Task 3"); // specific Deque method for tail
System.out.println("Queue (FIFO) view: " + deque); // [Task 1, Task 2, Task 3]
// 2. Iterate
System.out.println("--- Iterating Queue view ---");
for (String task : deque) {
System.out.println(task);
}
// 3. Peek at the head
System.out.println("\nPeek head: " + deque.peek()); // Task 1
System.out.println("Peek first: " + deque.peekFirst()); // Task 1
// 4. Remove from the head
System.out.println("Poll head: " + deque.poll()); // Task 1
System.out.println("Queue after poll: " + deque); // [Task 2, Task 3]
System.out.println("\n-------------------------------------");
// --- Using ArrayDeque as a Stack (LIFO) ---
deque.clear(); // Clear for stack demo
// 1. Push (Add) elements to the front (top)
deque.push("Book A"); // Stack add method
deque.push("Book B");
deque.addFirst("Book C"); // Deque method for front
System.out.println("Stack (LIFO) view: " + deque); // [Book C, Book B, Book A]
// 2. Iterate
System.out.println("--- Iterating Stack view ---");
for (String book : deque) {
System.out.println(book);
}
// 3. Peek at the top
System.out.println("\nPeek top: " + deque.peek()); // Book C
System.out.println("Peek first: " + deque.peekFirst()); // Book C
// 4. Pop (Remove from top)
System.out.println("Pop top: " + deque.pop()); // Book C
System.out.println("Stack after pop: " + deque); // [Book B, Book A]
System.out.println("Pop top: " + deque.removeFirst()); // Book B
System.out.println("Stack after removeFirst: " + deque); // [Book A]
}
}
Purpose: An extension of Queue
that supports operations that wait for the queue to become non-empty when retrieving an element, or for space to become available when storing an element. Primarily used for thread-safe producer-consumer scenarios.
Key Characteristics:
- Thread-Safe: Designed for concurrent access.
-
Blocking Operations: Methods like
put()
andtake()
block if the queue is full/empty. - Bounded or Unbounded: Can have a fixed capacity or grow indefinitely.
-
No
null
elements.
Key Operations & Sample Use:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class BlockingQueueOpsExample {
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); // Bounded queue of size 2
// --- Producer Thread ---
Thread producer = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + ": Putting Task A");
queue.put("Task A"); // Blocks if queue is full
System.out.println(Thread.currentThread().getName() + ": Putting Task B");
queue.put("Task B");
System.out.println(Thread.currentThread().getName() + ": Queue full, trying to put Task C (will block)");
queue.put("Task C"); // This will block until consumer takes an item
System.out.println(Thread.currentThread().getName() + ": Put Task C successfully");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().getName() + ": Interrupted while putting.");
}
}, "Producer");
// --- Consumer Thread ---
Thread consumer = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1); // Give producer time to put first items
System.out.println(Thread.currentThread().getName() + ": Taking " + queue.take()); // Blocks if queue is empty
TimeUnit.SECONDS.sleep(1); // Simulate work
System.out.println(Thread.currentThread().getName() + ": Taking " + queue.take());
TimeUnit.SECONDS.sleep(1); // Simulate more work, allowing producer to unblock
System.out.println(Thread.currentThread().getName() + ": Taking " + queue.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().getName() + ": Interrupted while taking.");
}
}, "Consumer");
producer.start();
consumer.start();
try {
producer.join();
consumer.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Main: All threads finished.");
}
}
Purpose: The Map
interface maps unique keys to values. It's not part of the Collection
interface hierarchy but is a core part of the Java Collections Framework.
Key Characteristics:
- Key-Value Pairs: Stores data as pairs, where each key maps to exactly one value.
-
Unique Keys: No two keys can be equal (determined by
equals()
method of keys). - Duplicate Values Allowed: Multiple keys can map to the same value.
- Order not guaranteed unless specific implementation (
LinkedHashMap
,TreeMap
).
Key Operations & Sample Use:
- Put: Associates a key with a value.
- Get: Retrieves value by key.
- Remove: Removes an entry by key.
- Check: Checks for key/value presence, size, emptiness.
-
Views: Provides
Set
view of keys,Collection
view of values,Set
view of key-value entries.
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapOpsExample {
public static void main(String[] args) {
Map<String, Integer> studentGrades = new HashMap<>(); // HashMap implements Map
// 1. Put (Add/Update) entries
studentGrades.put("Alice", 90);
studentGrades.put("Bob", 85);
studentGrades.put("Charlie", 90); // Duplicate value allowed
studentGrades.put("Alice", 92); // Updates Alice's grade
System.out.println("Map after puts: " + studentGrades); // {Alice=92, Charlie=90, Bob=85} (order may vary)
// 2. Iterate (via keySet(), values(), entrySet())
System.out.println("\n--- Iterating Map ---");
System.out.println("Keys:");
for (String name : studentGrades.keySet()) {
System.out.println(name);
}
System.out.println("Values:");
for (Integer grade : studentGrades.values()) {
System.out.println(grade);
}
System.out.println("Entries:");
for (Map.Entry<String, Integer> entry : studentGrades.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// 3. Get (Retrieve)
System.out.println("\nBob's grade: " + studentGrades.get("Bob")); // 85
System.out.println("David's grade (not found): " + studentGrades.get("David")); // null
// 4. Check presence
System.out.println("Contains key 'Alice'? " + studentGrades.containsKey("Alice")); // true
System.out.println("Contains value 90? " + studentGrades.containsValue(90)); // true
System.out.println("Size: " + studentGrades.size()); // 3
// 5. Remove entries
studentGrades.remove("Charlie");
System.out.println("Map after removing Charlie: " + studentGrades); // {Alice=92, Bob=85}
// 6. Clear
studentGrades.clear();
System.out.println("Map after clear: " + studentGrades + ", empty? " + studentGrades.isEmpty()); // {}, empty? true
}
}
Purpose: The most commonly used Map
implementation, using a hash table.
Key Characteristics:
- Unordered, Unsorted: Does not maintain any specific order.
- Not Thread-Safe:
- Fast (O(1)) for basic operations on average.
- Allows one
null
key and multiplenull
values. - Relies on
hashCode()
andequals()
methods of keys for uniqueness.
Key Operations & Sample Use:
import java.util.HashMap;
public class HashMapOpsExample {
public static void main(String[] args) {
HashMap<String, String> capitals = new HashMap<>();
// 1. Put (Add/Update) entries
capitals.put("India", "Delhi");
capitals.put("USA", "Washington D.C.");
capitals.put("France", "Paris");
capitals.put(null, "No Capital"); // Allows one null key
capitals.put("Germany", null); // Allows null values
System.out.println("HashMap: " + capitals); // Order not guaranteed
// 2. Iterate
System.out.println("--- Iterating HashMap entries ---");
for (String country : capitals.keySet()) {
System.out.println(country + " -> " + capitals.get(country));
}
// 3. Get (Retrieve)
System.out.println("\nCapital of USA: " + capitals.get("USA")); // Washington D.C.
System.out.println("Value for null key: " + capitals.get(null)); // No Capital
System.out.println("Value for Germany: " + capitals.get("Germany")); // null
// 4. Remove
capitals.remove("France");
System.out.println("HashMap after removing France: " + capitals);
capitals.remove(null);
System.out.println("HashMap after removing null key: " + capitals);
}
}
Purpose: Implements Map
using a hash table and a linked list.
Key Characteristics:
- Maintains Insertion Order (default) or Access Order (for LRU caches).
- No Duplicate Keys.
- Not Thread-Safe.
- Slightly slower than
HashMap
due to maintaining the linked list. - Allows one
null
key, multiplenull
values.
Key Operations & Sample Use:
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapOpsExample {
public static void main(String[] args) {
// LinkedHashMap preserving insertion order (default)
System.out.println("--- LinkedHashMap (Insertion Order) ---");
LinkedHashMap<String, String> countryCurrency = new LinkedHashMap<>();
countryCurrency.put("USA", "Dollar");
countryCurrency.put("India", "Rupee");
countryCurrency.put("Japan", "Yen");
countryCurrency.put("USA", "USD"); // Updates existing entry, but keeps its position
countryCurrency.put(null, "N/A");
System.out.println("Map: " + countryCurrency); // {USA=USD, India=Rupee, Japan=Yen, null=N/A}
// Notice USA's position depends on initial put, not update
// Iterate (Guaranteed Insertion Order)
System.out.println("Iterating (Insertion Order):");
for (Map.Entry<String, String> entry : countryCurrency.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// LinkedHashMap as an LRU Cache (Access Order)
System.out.println("\n--- LinkedHashMap (LRU Cache - Access Order) ---");
LinkedHashMap<String, String> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
return size() > 3; // Max 3 entries
}
};
lruCache.put("Item1", "Value1"); // Order: Item1
lruCache.put("Item2", "Value2"); // Order: Item1, Item2
lruCache.put("Item3", "Value3"); // Order: Item1, Item2, Item3
System.out.println("Cache after 3 puts: " + lruCache);
lruCache.get("Item1"); // Access Item1 - moves it to end (most recently accessed)
System.out.println("Cache after accessing Item1: " + lruCache); // Order: Item2, Item3, Item1
lruCache.put("Item4", "Value4"); // Puts Item4, eldest (Item2) is removed
System.out.println("Cache after putting Item4 (Item2 removed): " + lruCache); // Order: Item3, Item1, Item4
}
}
Purpose: Implements Map
using a Red-Black tree.
Key Characteristics:
-
Sorted by Key: Keys are stored in their natural ordering (if
Comparable
) or according to a providedComparator
. - No Duplicate Keys.
- Not Thread-Safe.
- Slower than
HashMap
(O(log n)) for basic operations. -
Does not allow
null
keys without a customComparator
that handlesnull
. Allowsnull
values. - Keys must be
Comparable
or provide aComparator
.
Key Operations & Sample Use:
import java.util.Comparator;
import java.util.TreeMap;
public class TreeMapOpsExample {
public static void main(String[] args) {
// Natural ordering (for Integer keys)
System.out.println("--- TreeMap with Natural Ordering ---");
TreeMap<Integer, String> employeeIds = new TreeMap<>();
employeeIds.put(103, "Alice");
employeeIds.put(101, "Bob");
employeeIds.put(105, "Charlie");
employeeIds.put(102, "David");
System.out.println("TreeMap: " + employeeIds); // {101=Bob, 102=David, 103=Alice, 105=Charlie} (keys sorted)
// 1. Iterate (Guaranteed Sorted Key Order)
System.out.println("Iterating (Sorted by Key):");
for (Map.Entry<Integer, String> entry : employeeIds.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// 2. Search/Retrieve (including navigation methods)
System.out.println("\nFirst key: " + employeeIds.firstKey()); // 101
System.out.println("Last key: " + employeeIds.lastKey()); // 105
System.out.println("Entry for key 102: " + employeeIds.get(102)); // David
System.out.println("Lowest key >= 102: " + employeeIds.ceilingKey(102)); // 102
System.out.println("Highest key <= 104: " + employeeIds.floorKey(104)); // 103
// 3. Remove
employeeIds.remove(103);
System.out.println("TreeMap after removing 103: " + employeeIds); // {101=Bob, 102=David, 105=Charlie}
// --- TreeMap with Custom Comparator (Reverse Order) ---
System.out.println("\n--- TreeMap with Custom Comparator (Reverse Order) ---");
TreeMap<String, Integer> reverseOrderMap = new TreeMap<>(Comparator.reverseOrder());
reverseOrderMap.put("Apple", 10);
reverseOrderMap.put("Banana", 5);
reverseOrderMap.put("Cherry", 15);
System.out.println("Reverse Ordered TreeMap: " + reverseOrderMap); // {Cherry=15, Banana=5, Apple=10}
System.out.println("First key (highest in reverse): " + reverseOrderMap.firstKey()); // Cherry
}
}
Purpose: A legacy, synchronized Map
implementation.
Key Characteristics:
-
Synchronized (Thread-safe): Slower than
HashMap
. -
Legacy: Generally superseded by
ConcurrentHashMap
. -
Does not allow
null
keys or values.
Key Operations & Sample Use:
import java.util.Hashtable;
import java.util.Map;
public class HashtableOpsExample {
public static void main(String[] args) {
Hashtable<String, String> envVars = new Hashtable<>();
// 1. Put entries
envVars.put("JAVA_HOME", "/usr/lib/jvm/java-11");
envVars.put("PATH", "/usr/bin:/bin");
// envVars.put("NULL_KEY", null); // Throws NullPointerException
// envVars.put(null, "NULL_VALUE"); // Throws NullPointerException
System.out.println("Hashtable: " + envVars); // Order not guaranteed
// 2. Iterate
System.out.println("--- Iterating Hashtable entries ---");
for (Map.Entry<String, String> entry : envVars.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
// 3. Get (Retrieve)
System.out.println("\nJAVA_HOME: " + envVars.get("JAVA_HOME")); // /usr/lib/jvm/java-11
// 4. Remove
envVars.remove("PATH");
System.out.println("Hashtable after removing PATH: " + envVars); // {JAVA_HOME=/usr/lib/jvm/java-11}
}
}
Purpose: A highly scalable, thread-safe Map
implementation designed for concurrent access.
Key Characteristics:
-
Thread-Safe: Offers much better concurrency than
HashTable
orCollections.synchronizedMap()
by using fine-grained locking or lock-free algorithms. - High Performance: Provides good performance even under heavy contention.
- Allows
null
values, but notnull
keys.
Key Operations & Sample Use:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapOpsExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> wordCounts = new ConcurrentHashMap<>();
// 1. Put (Add/Update) entries
wordCounts.put("hello", 1);
wordCounts.putIfAbsent("world", 1); // Only adds if "world" isn't present
System.out.println("Map after initial puts: " + wordCounts);
// 2. Atomic updates (common in concurrent scenarios)
// Increment count for "hello"
wordCounts.compute("hello", (key, value) -> (value == null) ? 1 : value + 1);
System.out.println("After compute 'hello': " + wordCounts); // {hello=2, world=1}
// Add if absent, or merge if present
wordCounts.merge("java", 1, (oldVal, newVal) -> oldVal + newVal); // Adds "java":1
wordCounts.merge("world", 1, (oldVal, newVal) -> oldVal + newVal); // Increments "world" by 1
System.out.println("After merge: " + wordCounts); // {hello=2, world=2, java=1}
// 3. Get (Retrieve)
System.out.println("Count for 'world': " + wordCounts.get("world")); // 2
// 4. Remove
wordCounts.remove("java");
System.out.println("After removing 'java': " + wordCounts); // {hello=2, world=2}
// 5. Iterate (Weakly consistent: reflects state at time of iterator creation,
// but changes *after* iterator creation might or might not be seen)
System.out.println("--- Iterating ConcurrentHashMap ---");
wordCounts.forEach((word, count) -> System.out.println(word + ": " + count));
}
}
Purpose: The java.util.Collections
class is a utility class providing static methods that operate on or return collections. It contains various polymorphic algorithms and wrappers.
Key Operations & Sample Use:
-
Sort: Sorts a
List
. -
Search: Binary search on a sorted
List
. - Shuffling/Reversing:
- Synchronized Views: Returns thread-safe wrappers for unsynchronized collections.
- Unmodifiable Views: Returns read-only views of collections.
- Min/Max: Finds minimum or maximum element.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
public class CollectionsUtilityOpsExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Bob");
names.add("Alice");
names.add("Charlie");
System.out.println("Original List: " + names); // [Bob, Alice, Charlie]
// 1. Sort a List
Collections.sort(names);
System.out.println("Sorted List: " + names); // [Alice, Bob, Charlie]
// 2. Binary Search (List must be sorted)
int index = Collections.binarySearch(names, "Bob");
System.out.println("Index of Bob: " + index); // 1
// 3. Reverse Order
Collections.reverse(names);
System.out.println("Reversed List: " + names); // [Charlie, Bob, Alice]
// 4. Shuffle (Randomize Order)
Collections.shuffle(names);
System.out.println("Shuffled List: " + names); // (Random order)
// 5. Create Synchronized Views
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
Set<Integer> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
// These are now thread-safe wrappers around the underlying collections.
synchronizedList.add("Secure Item");
System.out.println("Synchronized List: " + synchronizedList);
// 6. Create Unmodifiable Views
List<String> unmodifiableNames = Collections.unmodifiableList(names);
// unmodifiableNames.add("Eve"); // Throws UnsupportedOperationException
System.out.println("Unmodifiable List: " + unmodifiableNames);
// 7. Min/Max
System.out.println("Min element in names: " + Collections.min(names));
System.out.println("Max element in names: " + Collections.max(names));
}
}
Purpose: The java.util.Arrays
class provides static methods for manipulating arrays. It's distinct from Collections
but often used in conjunction, especially for converting between arrays and collections.
Key Operations & Sample Use:
- Sort: Sorts an array.
- Search: Binary search on a sorted array.
- Equals: Checks if two arrays are equal.
- Fill: Fills an array with a specified value.
-
AsList: Converts an array to a fixed-size
List
. - ToString: Converts array to a string representation.
import java.util.Arrays;
import java.util.List;
public class ArraysUtilityOpsExample {
public static void main(String[] args) {
// 1. Sort an array
int[] numbers = {5, 2, 8, 1};
Arrays.sort(numbers);
System.out.println("Sorted Array: " + Arrays.toString(numbers)); // [1, 2, 5, 8]
// 2. Binary Search (array must be sorted)
int index = Arrays.binarySearch(numbers, 5);
System.out.println("Index of 5: " + index); // 2
// 3. Check equality
int[] numbers2 = {1, 2, 5, 8};
System.out.println("Arrays equal? " + Arrays.equals(numbers, numbers2)); // true
// 4. Fill array
int[] filledArray = new int[3];
Arrays.fill(filledArray, 7);
System.out.println("Filled Array: " + Arrays.toString(filledArray)); // [7, 7, 7]
// 5. Convert array to List (fixed-size)
String[] fruitsArray = {"apple", "banana", "cherry"};
List<String> fixedList = Arrays.asList(fruitsArray);
System.out.println("List from Array: " + fixedList);
// fixedList.add("grape"); // Throws UnsupportedOperationException (fixed-size)
fruitsArray[0] = "apricot"; // Changes in array reflect in list
System.out.println("List after array modification: " + fixedList); // [apricot, banana, cherry]
// To get a modifiable list from an array:
List<String> modifiableList = new ArrayList<>(Arrays.asList(fruitsArray));
modifiableList.add("grape");
System.out.println("Modifiable List: " + modifiableList);
}
}
-
Interfaces vs. Implementations: Always program to interfaces (
List list = new ArrayList<>()
) for flexibility, extensibility, and better design. -
Performance Trade-offs: Be able to explain the Big O notation for common operations (add, remove, get, contains) for various implementations (e.g.,
ArrayList
vs.LinkedList
,HashSet
vs.TreeSet
vs.HashMap
). - Thread-Safety: Differentiate between unsynchronized (faster, but not safe for concurrency), legacy synchronized (slower, avoid in new code), and concurrently safe (scalable for high-concurrency).
- Ordering: Understand which collections guarantee order (insertion, sorted) and which do not.
- Duplicates: Know which collections allow duplicate elements/values and which enforce uniqueness.
-
Null Handling: Be aware of how different collections handle
null
elements, keys, or values (this is a common source of bugs if not understood). -
Utility Classes (
Collections
,Arrays
): Remember their static methods for common operations like sorting, searching, and creating synchronized/unmodifiable views.
📌 Pro Tip: For interviews, be ready to discuss typical use cases for each collection type and justify your choice based on performance, ordering, thread-safety, and null-handling requirements.