Collections Framework Overview in Java

Introduction

The Java Collections Framework is a unified architecture for representing and manipulating collections. It provides:

  • Interfaces
  • Implementations
  • Algorithms

Collection Framework Hierarchy

 Iterable | Collection | ------------------------------------- | | | List Queue Set | | | - ArrayList - PriorityQueue - HashSet - LinkedList - LinkedList - LinkedHashSet - Vector - ArrayDeque - TreeSet - Stack | - Vector | Stack Map | ------------------------------------- | | | SortedMap HashMap Hashtable | | | TreeMap - HashMap - Hashtable - LinkedHashMap

Core Interfaces

1. Collection Interface

Root interface for all collections (except Map)

public interface Collection<E> extends Iterable<E> { // Basic operations boolean add(E element); boolean remove(Object element); boolean contains(Object element); int size(); boolean isEmpty(); void clear(); // Bulk operations boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); boolean removeAll(Collection<?> c); boolean retainAll(Collection<?> c); // Array operations Object[] toArray(); <T> T[] toArray(T[] a); // Iteration Iterator<E> iterator(); }

List Interface

Characteristics

  • Ordered collection (sequence)
  • Allows duplicates
  • Positional access
  • ListIterator for bidirectional traversal

Implementations

ArrayList

import java.util.*; public class ArrayListExample { public static void main(String[] args) { // Creating ArrayList List<String> arrayList = new ArrayList<>(); // Adding elements arrayList.add("Apple"); arrayList.add("Banana"); arrayList.add("Orange"); arrayList.add(1, "Mango"); // Insert at specific position System.out.println("ArrayList: " + arrayList); // Accessing elements System.out.println("First element: " + arrayList.get(0)); System.out.println("Index of Banana: " + arrayList.indexOf("Banana")); // Iterating for (String fruit : arrayList) { System.out.println(fruit); } // Using ListIterator ListIterator<String> iterator = arrayList.listIterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } // Bulk operations List<String> moreFruits = Arrays.asList("Grapes", "Pineapple"); arrayList.addAll(moreFruits); // Sorting Collections.sort(arrayList); System.out.println("Sorted: " + arrayList); } }

LinkedList

import java.util.*; public class LinkedListExample { public static void main(String[] args) { // Creating LinkedList List<String> linkedList = new LinkedList<>(); // Adding elements linkedList.add("First"); linkedList.add("Second"); linkedList.add("Third"); System.out.println("LinkedList: " + linkedList); // LinkedList specific operations (when cast to LinkedList) LinkedList<String> ll = (LinkedList<String>) linkedList; ll.addFirst("New First"); ll.addLast("New Last"); ll.removeFirst(); ll.removeLast(); System.out.println("After operations: " + ll); } }

Vector vs ArrayList

import java.util.*; public class VectorExample { public static void main(String[] args) { // Vector is synchronized (thread-safe) Vector<String> vector = new Vector<>(); vector.add("Element1"); vector.add("Element2"); // Stack (extends Vector) Stack<String> stack = new Stack<>(); stack.push("First"); stack.push("Second"); stack.push("Third"); System.out.println("Stack: " + stack); System.out.println("Popped: " + stack.pop()); System.out.println("Peek: " + stack.peek()); } }

Set Interface

Characteristics

  • No duplicate elements
  • At most one null element
  • Mathematical set operations

Implementations

HashSet

import java.util.*; public class HashSetExample { public static void main(String[] args) { // Creating HashSet Set<String> hashSet = new HashSet<>(); // Adding elements hashSet.add("Apple"); hashSet.add("Banana"); hashSet.add("Orange"); hashSet.add("Apple"); // Duplicate - won't be added System.out.println("HashSet: " + hashSet); System.out.println("Size: " + hashSet.size()); // Checking existence System.out.println("Contains Apple: " + hashSet.contains("Apple")); // Removing hashSet.remove("Banana"); System.out.println("After removal: " + hashSet); // Set operations Set<String> set1 = new HashSet<>(Arrays.asList("A", "B", "C")); Set<String> set2 = new HashSet<>(Arrays.asList("B", "C", "D")); // Union Set<String> union = new HashSet<>(set1); union.addAll(set2); System.out.println("Union: " + union); // Intersection Set<String> intersection = new HashSet<>(set1); intersection.retainAll(set2); System.out.println("Intersection: " + intersection); // Difference Set<String> difference = new HashSet<>(set1); difference.removeAll(set2); System.out.println("Difference: " + difference); } }

LinkedHashSet

import java.util.*; public class LinkedHashSetExample { public static void main(String[] args) { // Maintains insertion order Set<String> linkedHashSet = new LinkedHashSet<>(); linkedHashSet.add("Zebra"); linkedHashSet.add("Apple"); linkedHashSet.add("Monkey"); linkedHashSet.add("Banana"); System.out.println("LinkedHashSet: " + linkedHashSet); // Insertion order preserved } }

TreeSet

import java.util.*; public class TreeSetExample { public static void main(String[] args) { // Sorted set Set<String> treeSet = new TreeSet<>(); treeSet.add("Zebra"); treeSet.add("Apple"); treeSet.add("Monkey"); treeSet.add("Banana"); System.out.println("TreeSet: " + treeSet); // Natural ordering // Custom comparator Set<String> reverseSet = new TreeSet<>(Collections.reverseOrder()); reverseSet.addAll(treeSet); System.out.println("Reverse order: " + reverseSet); // NavigableSet operations TreeSet<Integer> numbers = new TreeSet<>(Arrays.asList(1, 3, 5, 7, 9)); System.out.println("Lower than 5: " + numbers.lower(5)); // 3 System.out.println("Higher than 5: " + numbers.higher(5)); // 7 System.out.println("Floor of 4: " + numbers.floor(4)); // 3 System.out.println("Ceiling of 4: " + numbers.ceiling(4)); // 5 } }

Queue Interface

Characteristics

  • Designed for holding elements prior to processing
  • FIFO (usually), but can be LIFO or priority-based

Implementations

PriorityQueue

import java.util.*; public class PriorityQueueExample { public static void main(String[] args) { // Natural ordering Queue<Integer> priorityQueue = new PriorityQueue<>(); priorityQueue.offer(5); priorityQueue.offer(1); priorityQueue.offer(3); priorityQueue.offer(2); System.out.println("PriorityQueue: " + priorityQueue); // Processing in priority order while (!priorityQueue.isEmpty()) { System.out.println("Processing: " + priorityQueue.poll()); } // Custom comparator Queue<String> lengthQueue = new PriorityQueue<>( (s1, s2) -> s1.length() - s2.length() ); lengthQueue.offer("AAAA"); lengthQueue.offer("BB"); lengthQueue.offer("CCC"); while (!lengthQueue.isEmpty()) { System.out.println("By length: " + lengthQueue.poll()); } } }

ArrayDeque

import java.util.*; public class ArrayDequeExample { public static void main(String[] args) { // Double-ended queue Deque<String> deque = new ArrayDeque<>(); // Add at both ends deque.addFirst("First"); deque.addLast("Last"); deque.offerFirst("New First"); deque.offerLast("New Last"); System.out.println("Deque: " + deque); // Remove from both ends System.out.println("Remove first: " + deque.removeFirst()); System.out.println("Remove last: " + deque.removeLast()); // Peek elements System.out.println("First element: " + deque.getFirst()); System.out.println("Last element: " + deque.getLast()); } }

Map Interface

Characteristics

  • Key-value pairs
  • No duplicate keys
  • Each key maps to exactly one value

Implementations

HashMap

import java.util.*; public class HashMapExample { public static void main(String[] args) { // Creating HashMap Map<String, Integer> hashMap = new HashMap<>(); // Adding key-value pairs hashMap.put("Apple", 10); hashMap.put("Banana", 20); hashMap.put("Orange", 15); hashMap.put("Apple", 25); // Overwrites previous value System.out.println("HashMap: " + hashMap); // Accessing values System.out.println("Apple quantity: " + hashMap.get("Apple")); System.out.println("Contains key 'Banana': " + hashMap.containsKey("Banana")); System.out.println("Contains value 15: " + hashMap.containsValue(15)); // Iterating for (Map.Entry<String, Integer> entry : hashMap.entrySet()) { System.out.println(entry.getKey() + " => " + entry.getValue()); } // Key set and values Set<String> keys = hashMap.keySet(); Collection<Integer> values = hashMap.values(); System.out.println("Keys: " + keys); System.out.println("Values: " + values); // Compute if absent hashMap.computeIfAbsent("Mango", k -> 30); System.out.println("After computeIfAbsent: " + hashMap); } }

LinkedHashMap

import java.util.*; public class LinkedHashMapExample { public static void main(String[] args) { // Maintains insertion order Map<String, Integer> linkedHashMap = new LinkedHashMap<>(); linkedHashMap.put("Zebra", 1); linkedHashMap.put("Apple", 2); linkedHashMap.put("Monkey", 3); System.out.println("LinkedHashMap: " + linkedHashMap); // Insertion order // Access order (for LRU cache) Map<String, Integer> accessOrderMap = new LinkedHashMap<>(16, 0.75f, true); accessOrderMap.put("A", 1); accessOrderMap.put("B", 2); accessOrderMap.put("C", 3); accessOrderMap.get("A"); // Access A accessOrderMap.get("B"); // Access B System.out.println("Access order: " + accessOrderMap); // C, A, B } }

TreeMap

import java.util.*; public class TreeMapExample { public static void main(String[] args) { // Sorted map Map<String, Integer> treeMap = new TreeMap<>(); treeMap.put("Zebra", 1); treeMap.put("Apple", 2); treeMap.put("Monkey", 3); System.out.println("TreeMap: " + treeMap); // Natural ordering // NavigableMap operations TreeMap<Integer, String> navigableMap = new TreeMap<>(); navigableMap.put(1, "One"); navigableMap.put(3, "Three"); navigableMap.put(5, "Five"); navigableMap.put(7, "Seven"); System.out.println("First key: " + navigableMap.firstKey()); System.out.println("Last key: " + navigableMap.lastKey()); System.out.println("HeadMap (<5): " + navigableMap.headMap(5)); System.out.println("TailMap (>=5): " + navigableMap.tailMap(5)); System.out.println("SubMap (3-7): " + navigableMap.subMap(3, 7)); } }

Hashtable

import java.util.*; public class HashtableExample { public static void main(String[] args) { // Legacy, synchronized Hashtable<String, Integer> hashtable = new Hashtable<>(); hashtable.put("One", 1); hashtable.put("Two", 2); hashtable.put("Three", 3); System.out.println("Hashtable: " + hashtable); // Enumeration (legacy) Enumeration<String> keys = hashtable.keys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); System.out.println(key + " = " + hashtable.get(key)); } } }

Utility Classes

Collections Class

import java.util.*; public class CollectionsUtility { public static void main(String[] args) { List<String> list = new ArrayList<>(Arrays.asList("B", "A", "D", "C")); // Sorting Collections.sort(list); System.out.println("Sorted: " + list); // Reversing Collections.reverse(list); System.out.println("Reversed: " + list); // Shuffling Collections.shuffle(list); System.out.println("Shuffled: " + list); // Binary search Collections.sort(list); int index = Collections.binarySearch(list, "C"); System.out.println("Index of C: " + index); // Synchronized collections List<String> syncList = Collections.synchronizedList(new ArrayList<>()); Set<String> syncSet = Collections.synchronizedSet(new HashSet<>()); Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>()); // Unmodifiable collections List<String> unmodifiableList = Collections.unmodifiableList(list); // unmodifiableList.add("X"); // Throws UnsupportedOperationException } }

Arrays Class

import java.util.*; public class ArraysUtility { public static void main(String[] args) { String[] array = {"B", "A", "D", "C"}; // Sorting Arrays.sort(array); System.out.println("Sorted array: " + Arrays.toString(array)); // Binary search int index = Arrays.binarySearch(array, "C"); System.out.println("Index of C: " + index); // Fill int[] numbers = new int[5]; Arrays.fill(numbers, 42); System.out.println("Filled array: " + Arrays.toString(numbers)); // Copy String[] copy = Arrays.copyOf(array, array.length); System.out.println("Copy: " + Arrays.toString(copy)); // Convert array to list List<String> list = Arrays.asList(array); System.out.println("As list: " + list); } }

Java 8+ Features

Streams with Collections

import java.util.*; import java.util.stream.*; public class StreamsWithCollections { public static void main(String[] args) { List<String> fruits = Arrays.asList("Apple", "Banana", "Orange", "Mango", "Grapes"); // Filter and collect List<String> filtered = fruits.stream() .filter(f -> f.startsWith("A")) .collect(Collectors.toList()); System.out.println("Fruits starting with A: " + filtered); // Map and collect List<Integer> lengths = fruits.stream() .map(String::length) .collect(Collectors.toList()); System.out.println("Lengths: " + lengths); // Sort List<String> sorted = fruits.stream() .sorted() .collect(Collectors.toList()); System.out.println("Sorted: " + sorted); // Group by Map<Integer, List<String>> groupedByLength = fruits.stream() .collect(Collectors.groupingBy(String::length)); System.out.println("Grouped by length: " + groupedByLength); // Statistics IntSummaryStatistics stats = fruits.stream() .mapToInt(String::length) .summaryStatistics(); System.out.println("Stats: " + stats); } }

forEach with Lambda

import java.util.*; public class ForEachExample { public static void main(String[] args) { List<String> list = Arrays.asList("A", "B", "C", "D"); // Traditional for-each for (String s : list) { System.out.println(s); } // Lambda for-each list.forEach(s -> System.out.println(s)); // Method reference list.forEach(System.out::println); // Map for-each Map<String, Integer> map = new HashMap<>(); map.put("A", 1); map.put("B", 2); map.put("C", 3); map.forEach((k, v) -> System.out.println(k + " => " + v)); } }

Performance Characteristics

CollectionGetAddRemoveContainsOrder
ArrayListO(1)O(1)*O(n)O(n)Insertion
LinkedListO(n)O(1)O(1)O(n)Insertion
HashSetO(1)O(1)O(1)O(1)No
LinkedHashSetO(1)O(1)O(1)O(1)Insertion
TreeSetO(log n)O(log n)O(log n)O(log n)Sorted
HashMapO(1)O(1)O(1)O(1)No
LinkedHashMapO(1)O(1)O(1)O(1)Insertion/Access
TreeMapO(log n)O(log n)O(log n)O(log n)Sorted

*Amortized constant time

Best Practices

  1. Use interface types for declarations
  2. Choose the right collection based on requirements
  3. Use diamond operator for type inference
  4. Consider thread safety requirements
  5. Use immutable collections when possible
  6. Prefer enhanced for-loop over iterators for readability
  7. Use streams for complex data processing
// Good practice examples List<String> list = new ArrayList<>(); // Interface type, diamond operator Set<Integer> set = new HashSet<>(); Map<String, Object> map = new HashMap<>(); // Thread-safe alternatives List<String> syncList = Collections.synchronizedList(new ArrayList<>()); Map<String, String> concurrentMap = new ConcurrentHashMap<>();

The Java Collections Framework provides a comprehensive set of interfaces and implementations for storing and processing groups of objects, making it one of the most powerful features of the Java language.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper