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