Java Collections Framework - alazeroone/software-concepts GitHub Wiki

📚 Java Collections Framework (JCF) Internals

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.


🚀 Table of Contents


📅 Key Java Collections Framework Overview

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 Lists or vice-versa. JavaDoc: Arrays

📚 Detailed Interface & Class Explanations

Here you'll find more in-depth descriptions and practical code examples for each key component of the Java Collections Framework.

Iterable Interface

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>

List Interface

Purpose: An ordered collection (sequence). Elements can be accessed by their integer index. Lists 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, Lists do not inherently maintain sorted order unless Collections.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]
    }
}

ArrayList Class

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]
    }
}

LinkedList Class

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]
    }
}

Vector Class

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]
    }
}

Stack Class

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
    }
}

Set Interface

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 or TreeSet) 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]
    }
}

HashSet Class

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() and equals() 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]
    }
}

LinkedHashSet Class

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)
    }
}

TreeSet Class

Purpose: Implements Set using a Red-Black tree.

Key Characteristics:

  • Sorted: Elements are stored in their natural ordering (if Comparable) or according to a provided Comparator.
  • No Duplicates.
  • Not Thread-Safe:
  • Slower than HashSet (O(log n)) for basic operations.
  • Does not allow null elements unless a custom Comparator explicitly handles null.
  • Elements must be Comparable or a Comparator 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]
    }
}

Queue Interface

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 or false).

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
    }
}

PriorityQueue Class

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 a Comparator.

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
    }
}

ArrayDeque Class

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]
    }
}

BlockingQueue Interface

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() and take() 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.");
    }
}

Map Interface

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
    }
}

HashMap Class

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 multiple null values.
  • Relies on hashCode() and equals() 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);
    }
}

LinkedHashMap Class

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, multiple null 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
    }
}

TreeMap Class

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 provided Comparator.
  • No Duplicate Keys.
  • Not Thread-Safe.
  • Slower than HashMap (O(log n)) for basic operations.
  • Does not allow null keys without a custom Comparator that handles null. Allows null values.
  • Keys must be Comparable or provide a Comparator.

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
    }
}

Hashtable Class

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}
    }
}

ConcurrentHashMap Class

Purpose: A highly scalable, thread-safe Map implementation designed for concurrent access.

Key Characteristics:

  • Thread-Safe: Offers much better concurrency than HashTable or Collections.synchronizedMap() by using fine-grained locking or lock-free algorithms.
  • High Performance: Provides good performance even under heavy contention.
  • Allows null values, but not null 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));
    }
}

Collections (Utility Class)

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));
    }
}

Arrays (Utility Class)

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);
    }
}

✅ Interview Quick Tips

  • 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.

⚠️ **GitHub.com Fallback** ⚠️