Java Collections Framework
The Java Collections Framework provides a comprehensive set of data structures and algorithms for storing, manipulating, and accessing groups of objects. Understanding collections is essential for writing efficient Java applications and handling data effectively.
Collections Hierarchy Overview
The Collections Framework is built around several key interfaces and their implementations:
1// Core collection interfaces demonstration
2import java.util.*;
3
4public class CollectionsOverview {
5 public static void demonstrateHierarchy() {
6 System.out.println("=== Collections Framework Hierarchy ===");
7
8 // Collection interface - root of most collections
9 // ├── List interface - ordered collections that allow duplicates
10 // │ ├── ArrayList - resizable array implementation
11 // │ ├── LinkedList - doubly-linked list implementation
12 // │ └── Vector - synchronized resizable array (legacy)
13 //
14 // ├── Set interface - collections that do not allow duplicates
15 // │ ├── HashSet - hash table implementation
16 // │ ├── LinkedHashSet - hash table with insertion order
17 // │ └── TreeSet - sorted set implementation
18 //
19 // └── Queue interface - collections for holding elements prior to processing
20 // ├── PriorityQueue - priority heap implementation
21 // ├── ArrayDeque - resizable array deque
22 // └── LinkedList - also implements Queue
23 //
24 // Map interface - separate hierarchy for key-value pairs
25 // ├── HashMap - hash table implementation
26 // ├── LinkedHashMap - hash table with insertion/access order
27 // ├── TreeMap - sorted map implementation
28 // └── Hashtable - synchronized hash table (legacy)
29
30 System.out.println("Framework provides consistent APIs across different data structures");
31 }
32}
List Interface and Implementations
Lists maintain insertion order and allow duplicate elements with indexed access.
ArrayList - Dynamic Array
1import java.util.*;
2
3public class ArrayListExamples {
4
5 public static void demonstrateArrayList() {
6 System.out.println("=== ArrayList Examples ===");
7
8 // Creating and initializing ArrayList
9 List<String> fruits = new ArrayList<>();
10
11 // Adding elements
12 fruits.add("Apple");
13 fruits.add("Banana");
14 fruits.add("Orange");
15 fruits.add("Apple"); // Duplicates allowed
16
17 System.out.println("Initial list: " + fruits);
18 System.out.println("Size: " + fruits.size());
19
20 // Indexed access
21 String firstFruit = fruits.get(0);
22 System.out.println("First fruit: " + firstFruit);
23
24 // Modifying elements
25 fruits.set(1, "Blueberry"); // Replace at index 1
26 System.out.println("After replacement: " + fruits);
27
28 // Inserting at specific position
29 fruits.add(2, "Cherry");
30 System.out.println("After insertion: " + fruits);
31
32 // Searching
33 int appleIndex = fruits.indexOf("Apple");
34 int lastAppleIndex = fruits.lastIndexOf("Apple");
35 boolean hasOrange = fruits.contains("Orange");
36
37 System.out.println("First Apple at index: " + appleIndex);
38 System.out.println("Last Apple at index: " + lastAppleIndex);
39 System.out.println("Contains Orange: " + hasOrange);
40
41 // Removing elements
42 fruits.remove("Orange"); // Remove by value
43 fruits.remove(0); // Remove by index
44 System.out.println("After removals: " + fruits);
45
46 // Bulk operations
47 List<String> moreFruits = Arrays.asList("Mango", "Pineapple");
48 fruits.addAll(moreFruits);
49 System.out.println("After adding more fruits: " + fruits);
50
51 // Iterating
52 System.out.println("\nIterating through list:");
53 for (int i = 0; i < fruits.size(); i++) {
54 System.out.println(i + ": " + fruits.get(i));
55 }
56
57 // Enhanced for loop
58 System.out.println("\nUsing enhanced for loop:");
59 for (String fruit : fruits) {
60 System.out.println("- " + fruit);
61 }
62
63 // Using streams (Java 8+)
64 System.out.println("\nFruits starting with 'A':");
65 fruits.stream()
66 .filter(fruit -> fruit.startsWith("A"))
67 .forEach(System.out::println);
68 }
69
70 public static void performanceCharacteristics() {
71 System.out.println("\n=== ArrayList Performance ===");
72
73 List<Integer> numbers = new ArrayList<>();
74
75 // Adding elements - O(1) amortized
76 long startTime = System.nanoTime();
77 for (int i = 0; i < 100000; i++) {
78 numbers.add(i);
79 }
80 long endTime = System.nanoTime();
81 System.out.println("Adding 100,000 elements: " + (endTime - startTime) / 1_000_000 + " ms");
82
83 // Random access - O(1)
84 startTime = System.nanoTime();
85 for (int i = 0; i < 10000; i++) {
86 int randomIndex = (int) (Math.random() * numbers.size());
87 numbers.get(randomIndex);
88 }
89 endTime = System.nanoTime();
90 System.out.println("10,000 random accesses: " + (endTime - startTime) / 1_000_000 + " ms");
91
92 // Inserting at beginning - O(n)
93 startTime = System.nanoTime();
94 for (int i = 0; i < 1000; i++) {
95 numbers.add(0, -i); // Insert at beginning
96 }
97 endTime = System.nanoTime();
98 System.out.println("1,000 insertions at beginning: " + (endTime - startTime) / 1_000_000 + " ms");
99 }
100}
LinkedList - Doubly Linked List
1import java.util.*;
2
3public class LinkedListExamples {
4
5 public static void demonstrateLinkedList() {
6 System.out.println("=== LinkedList Examples ===");
7
8 LinkedList<String> tasks = new LinkedList<>();
9
10 // Adding elements
11 tasks.add("Complete project");
12 tasks.add("Review code");
13 tasks.add("Write documentation");
14
15 // LinkedList specific methods
16 tasks.addFirst("Plan meeting"); // Add to beginning
17 tasks.addLast("Deploy application"); // Add to end
18
19 System.out.println("Task list: " + tasks);
20
21 // Accessing first and last elements
22 String firstTask = tasks.getFirst();
23 String lastTask = tasks.getLast();
24 System.out.println("First task: " + firstTask);
25 System.out.println("Last task: " + lastTask);
26
27 // Removing from ends
28 String removedFirst = tasks.removeFirst();
29 String removedLast = tasks.removeLast();
30 System.out.println("Removed first: " + removedFirst);
31 System.out.println("Removed last: " + removedLast);
32 System.out.println("Remaining tasks: " + tasks);
33
34 // Using as Queue (FIFO)
35 Queue<String> taskQueue = new LinkedList<>();
36 taskQueue.offer("Task 1"); // Add to rear
37 taskQueue.offer("Task 2");
38 taskQueue.offer("Task 3");
39
40 System.out.println("\nProcessing tasks from queue:");
41 while (!taskQueue.isEmpty()) {
42 String currentTask = taskQueue.poll(); // Remove from front
43 System.out.println("Processing: " + currentTask);
44 }
45
46 // Using as Stack (LIFO)
47 Deque<String> taskStack = new LinkedList<>();
48 taskStack.push("Task A"); // Add to top
49 taskStack.push("Task B");
50 taskStack.push("Task C");
51
52 System.out.println("\nProcessing tasks from stack:");
53 while (!taskStack.isEmpty()) {
54 String currentTask = taskStack.pop(); // Remove from top
55 System.out.println("Processing: " + currentTask);
56 }
57 }
58
59 public static void compareListPerformance() {
60 System.out.println("\n=== ArrayList vs LinkedList Performance ===");
61
62 int size = 100000;
63
64 // ArrayList performance
65 List<Integer> arrayList = new ArrayList<>();
66 long startTime = System.nanoTime();
67 for (int i = 0; i < size; i++) {
68 arrayList.add(0, i); // Insert at beginning
69 }
70 long arrayListTime = System.nanoTime() - startTime;
71
72 // LinkedList performance
73 List<Integer> linkedList = new LinkedList<>();
74 startTime = System.nanoTime();
75 for (int i = 0; i < size; i++) {
76 linkedList.add(0, i); // Insert at beginning
77 }
78 long linkedListTime = System.nanoTime() - startTime;
79
80 System.out.println("ArrayList insertions at beginning: " + arrayListTime / 1_000_000 + " ms");
81 System.out.println("LinkedList insertions at beginning: " + linkedListTime / 1_000_000 + " ms");
82
83 // Random access comparison
84 startTime = System.nanoTime();
85 for (int i = 0; i < 10000; i++) {
86 arrayList.get(i);
87 }
88 long arrayListAccessTime = System.nanoTime() - startTime;
89
90 startTime = System.nanoTime();
91 for (int i = 0; i < 10000; i++) {
92 linkedList.get(i);
93 }
94 long linkedListAccessTime = System.nanoTime() - startTime;
95
96 System.out.println("ArrayList random access: " + arrayListAccessTime / 1_000_000 + " ms");
97 System.out.println("LinkedList random access: " + linkedListAccessTime / 1_000_000 + " ms");
98 }
99}
Set Interface and Implementations
Sets ensure uniqueness of elements and provide efficient membership testing.
HashSet, LinkedHashSet, and TreeSet
1import java.util.*;
2
3public class SetExamples {
4
5 public static void demonstrateSets() {
6 System.out.println("=== Set Implementations Comparison ===");
7
8 // HashSet - no ordering guarantee, fastest for basic operations
9 Set<String> hashSet = new HashSet<>();
10 hashSet.addAll(Arrays.asList("Banana", "Apple", "Cherry", "Apple", "Date"));
11 System.out.println("HashSet: " + hashSet); // Order may vary
12
13 // LinkedHashSet - maintains insertion order
14 Set<String> linkedHashSet = new LinkedHashSet<>();
15 linkedHashSet.addAll(Arrays.asList("Banana", "Apple", "Cherry", "Apple", "Date"));
16 System.out.println("LinkedHashSet: " + linkedHashSet); // Insertion order preserved
17
18 // TreeSet - maintains sorted order
19 Set<String> treeSet = new TreeSet<>();
20 treeSet.addAll(Arrays.asList("Banana", "Apple", "Cherry", "Apple", "Date"));
21 System.out.println("TreeSet: " + treeSet); // Natural order (alphabetical)
22
23 System.out.println("\n=== Set Operations ===");
24
25 Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
26 Set<Integer> set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
27
28 // Union
29 Set<Integer> union = new HashSet<>(set1);
30 union.addAll(set2);
31 System.out.println("Set1: " + set1);
32 System.out.println("Set2: " + set2);
33 System.out.println("Union: " + union);
34
35 // Intersection
36 Set<Integer> intersection = new HashSet<>(set1);
37 intersection.retainAll(set2);
38 System.out.println("Intersection: " + intersection);
39
40 // Difference
41 Set<Integer> difference = new HashSet<>(set1);
42 difference.removeAll(set2);
43 System.out.println("Difference (set1 - set2): " + difference);
44
45 // Subset testing
46 Set<Integer> subset = new HashSet<>(Arrays.asList(1, 2, 3));
47 boolean isSubset = set1.containsAll(subset);
48 System.out.println("Is {1,2,3} subset of set1? " + isSubset);
49 }
50
51 public static void demonstrateCustomObjects() {
52 System.out.println("\n=== Sets with Custom Objects ===");
53
54 // Student class for demonstration
55 class Student {
56 private String name;
57 private int id;
58
59 public Student(String name, int id) {
60 this.name = name;
61 this.id = id;
62 }
63
64 @Override
65 public boolean equals(Object obj) {
66 if (this == obj) return true;
67 if (obj == null || getClass() != obj.getClass()) return false;
68 Student student = (Student) obj;
69 return id == student.id && Objects.equals(name, student.name);
70 }
71
72 @Override
73 public int hashCode() {
74 return Objects.hash(name, id);
75 }
76
77 @Override
78 public String toString() {
79 return String.format("Student{name='%s', id=%d}", name, id);
80 }
81 }
82
83 Set<Student> students = new HashSet<>();
84 students.add(new Student("Alice", 123));
85 students.add(new Student("Bob", 456));
86 students.add(new Student("Alice", 123)); // Duplicate - won't be added
87
88 System.out.println("Students set: " + students);
89 System.out.println("Set size: " + students.size());
90
91 // TreeSet with custom comparator
92 Set<Student> sortedStudents = new TreeSet<>((s1, s2) -> s1.name.compareTo(s2.name));
93 sortedStudents.addAll(Arrays.asList(
94 new Student("Charlie", 789),
95 new Student("Alice", 123),
96 new Student("Bob", 456)
97 ));
98 System.out.println("Sorted students: " + sortedStudents);
99 }
100}
Map Interface and Implementations
Maps store key-value pairs and provide efficient lookup, insertion, and deletion operations.
HashMap, LinkedHashMap, and TreeMap
1import java.util.*;
2
3public class MapExamples {
4
5 public static void demonstrateMaps() {
6 System.out.println("=== Map Implementations ===");
7
8 // HashMap - fastest for basic operations, no ordering
9 Map<String, Integer> hashMap = new HashMap<>();
10 hashMap.put("Alice", 85);
11 hashMap.put("Bob", 92);
12 hashMap.put("Charlie", 78);
13 hashMap.put("Diana", 95);
14
15 System.out.println("HashMap: " + hashMap);
16
17 // LinkedHashMap - maintains insertion order
18 Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
19 linkedHashMap.put("Alice", 85);
20 linkedHashMap.put("Bob", 92);
21 linkedHashMap.put("Charlie", 78);
22 linkedHashMap.put("Diana", 95);
23
24 System.out.println("LinkedHashMap: " + linkedHashMap);
25
26 // TreeMap - maintains sorted order by keys
27 Map<String, Integer> treeMap = new TreeMap<>();
28 treeMap.put("Alice", 85);
29 treeMap.put("Bob", 92);
30 treeMap.put("Charlie", 78);
31 treeMap.put("Diana", 95);
32
33 System.out.println("TreeMap: " + treeMap);
34
35 System.out.println("\n=== Map Operations ===");
36
37 Map<String, Integer> scores = new HashMap<>();
38 scores.put("Math", 95);
39 scores.put("Science", 88);
40 scores.put("English", 92);
41 scores.put("History", 85);
42
43 // Basic operations
44 Integer mathScore = scores.get("Math");
45 Integer artScore = scores.get("Art"); // Returns null if key doesn't exist
46 Integer defaultScore = scores.getOrDefault("Art", 0); // Returns default if key doesn't exist
47
48 System.out.println("Math score: " + mathScore);
49 System.out.println("Art score: " + artScore);
50 System.out.println("Art score (with default): " + defaultScore);
51
52 // Checking for keys and values
53 boolean hasMath = scores.containsKey("Math");
54 boolean hasScore95 = scores.containsValue(95);
55 System.out.println("Has Math: " + hasMath);
56 System.out.println("Has score 95: " + hasScore95);
57
58 // Iterating over maps
59 System.out.println("\nIterating over key-value pairs:");
60 for (Map.Entry<String, Integer> entry : scores.entrySet()) {
61 System.out.println(entry.getKey() + ": " + entry.getValue());
62 }
63
64 System.out.println("\nIterating over keys:");
65 for (String subject : scores.keySet()) {
66 System.out.println(subject + " -> " + scores.get(subject));
67 }
68
69 System.out.println("\nIterating over values:");
70 for (Integer score : scores.values()) {
71 System.out.println("Score: " + score);
72 }
73
74 // Java 8 forEach
75 System.out.println("\nUsing Java 8 forEach:");
76 scores.forEach((subject, score) ->
77 System.out.println(subject + " grade: " + getLetterGrade(score)));
78 }
79
80 private static String getLetterGrade(int score) {
81 if (score >= 90) return "A";
82 if (score >= 80) return "B";
83 if (score >= 70) return "C";
84 if (score >= 60) return "D";
85 return "F";
86 }
87
88 public static void advancedMapOperations() {
89 System.out.println("\n=== Advanced Map Operations ===");
90
91 Map<String, List<String>> studentCourses = new HashMap<>();
92
93 // Adding courses for students
94 addCourse(studentCourses, "Alice", "Math");
95 addCourse(studentCourses, "Alice", "Science");
96 addCourse(studentCourses, "Bob", "Math");
97 addCourse(studentCourses, "Bob", "History");
98 addCourse(studentCourses, "Alice", "English");
99
100 System.out.println("Student courses: " + studentCourses);
101
102 // Using computeIfAbsent for cleaner code
103 Map<String, Set<String>> uniqueCourses = new HashMap<>();
104 uniqueCourses.computeIfAbsent("Alice", k -> new HashSet<>()).add("Math");
105 uniqueCourses.computeIfAbsent("Alice", k -> new HashSet<>()).add("Math"); // Duplicate
106 uniqueCourses.computeIfAbsent("Alice", k -> new HashSet<>()).add("Science");
107
108 System.out.println("Unique courses: " + uniqueCourses);
109
110 // Merge operation
111 Map<String, Integer> inventory1 = new HashMap<>();
112 inventory1.put("Apples", 10);
113 inventory1.put("Bananas", 15);
114
115 Map<String, Integer> inventory2 = new HashMap<>();
116 inventory2.put("Apples", 5);
117 inventory2.put("Oranges", 20);
118
119 // Merge inventories
120 inventory2.forEach((fruit, count) ->
121 inventory1.merge(fruit, count, Integer::sum));
122
123 System.out.println("Merged inventory: " + inventory1);
124
125 // Replace operations
126 Map<String, String> cache = new HashMap<>();
127 cache.put("key1", "value1");
128 cache.put("key2", "value2");
129
130 cache.replace("key1", "new_value1");
131 cache.replace("key3", "value3"); // Won't add since key doesn't exist
132 cache.replaceAll((key, value) -> value.toUpperCase());
133
134 System.out.println("Cache after replace operations: " + cache);
135 }
136
137 private static void addCourse(Map<String, List<String>> studentCourses,
138 String student, String course) {
139 studentCourses.computeIfAbsent(student, k -> new ArrayList<>()).add(course);
140 }
141}
Queue and Deque Interfaces
Queues provide FIFO (First-In-First-Out) access, while deques support both FIFO and LIFO operations.
PriorityQueue and ArrayDeque
1import java.util.*;
2
3public class QueueExamples {
4
5 public static void demonstratePriorityQueue() {
6 System.out.println("=== PriorityQueue Examples ===");
7
8 // Natural ordering (min-heap)
9 PriorityQueue<Integer> minHeap = new PriorityQueue<>();
10 minHeap.addAll(Arrays.asList(5, 2, 8, 1, 9, 3));
11
12 System.out.println("Priority queue (natural order): " + minHeap);
13 System.out.println("Polling elements in priority order:");
14 while (!minHeap.isEmpty()) {
15 System.out.print(minHeap.poll() + " ");
16 }
17 System.out.println();
18
19 // Custom ordering (max-heap)
20 PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
21 maxHeap.addAll(Arrays.asList(5, 2, 8, 1, 9, 3));
22
23 System.out.println("\nPriority queue (reverse order): " + maxHeap);
24 System.out.println("Polling elements in reverse priority order:");
25 while (!maxHeap.isEmpty()) {
26 System.out.print(maxHeap.poll() + " ");
27 }
28 System.out.println();
29
30 // Priority queue with custom objects
31 demonstrateTaskPriorityQueue();
32 }
33
34 private static void demonstrateTaskPriorityQueue() {
35 System.out.println("\n=== Task Priority Queue ===");
36
37 class Task implements Comparable<Task> {
38 private String name;
39 private int priority;
40
41 public Task(String name, int priority) {
42 this.name = name;
43 this.priority = priority;
44 }
45
46 @Override
47 public int compareTo(Task other) {
48 return Integer.compare(this.priority, other.priority); // Lower number = higher priority
49 }
50
51 @Override
52 public String toString() {
53 return String.format("%s(priority:%d)", name, priority);
54 }
55 }
56
57 PriorityQueue<Task> taskQueue = new PriorityQueue<>();
58 taskQueue.add(new Task("Fix bug", 1));
59 taskQueue.add(new Task("Write docs", 3));
60 taskQueue.add(new Task("Code review", 2));
61 taskQueue.add(new Task("Deploy app", 1));
62
63 System.out.println("Processing tasks by priority:");
64 while (!taskQueue.isEmpty()) {
65 Task task = taskQueue.poll();
66 System.out.println("Processing: " + task);
67 }
68 }
69
70 public static void demonstrateArrayDeque() {
71 System.out.println("\n=== ArrayDeque Examples ===");
72
73 Deque<String> deque = new ArrayDeque<>();
74
75 // Adding elements
76 deque.addFirst("First");
77 deque.addLast("Last");
78 deque.addFirst("New First");
79 deque.addLast("New Last");
80
81 System.out.println("Deque after additions: " + deque);
82
83 // Accessing elements without removing
84 String first = deque.peekFirst();
85 String last = deque.peekLast();
86 System.out.println("First element: " + first);
87 System.out.println("Last element: " + last);
88 System.out.println("Deque unchanged: " + deque);
89
90 // Removing elements
91 String removedFirst = deque.removeFirst();
92 String removedLast = deque.removeLast();
93 System.out.println("Removed first: " + removedFirst);
94 System.out.println("Removed last: " + removedLast);
95 System.out.println("Deque after removals: " + deque);
96
97 // Using as stack (LIFO)
98 System.out.println("\nUsing ArrayDeque as Stack:");
99 Deque<Integer> stack = new ArrayDeque<>();
100 stack.push(1);
101 stack.push(2);
102 stack.push(3);
103
104 System.out.println("Stack: " + stack);
105 while (!stack.isEmpty()) {
106 System.out.println("Popped: " + stack.pop());
107 }
108
109 // Using as queue (FIFO)
110 System.out.println("\nUsing ArrayDeque as Queue:");
111 Queue<String> queue = new ArrayDeque<>();
112 queue.offer("Task 1");
113 queue.offer("Task 2");
114 queue.offer("Task 3");
115
116 System.out.println("Queue: " + queue);
117 while (!queue.isEmpty()) {
118 System.out.println("Processing: " + queue.poll());
119 }
120 }
121}
Practical Collection Usage Patterns
Data Processing Examples
1import java.util.*;
2import java.util.stream.Collectors;
3
4public class CollectionPatterns {
5
6 // Student class for examples
7 static class Student {
8 private String name;
9 private String major;
10 private double gpa;
11 private List<String> courses;
12
13 public Student(String name, String major, double gpa, List<String> courses) {
14 this.name = name;
15 this.major = major;
16 this.gpa = gpa;
17 this.courses = new ArrayList<>(courses);
18 }
19
20 // Getters
21 public String getName() { return name; }
22 public String getMajor() { return major; }
23 public double getGpa() { return gpa; }
24 public List<String> getCourses() { return courses; }
25
26 @Override
27 public String toString() {
28 return String.format("Student{name='%s', major='%s', gpa=%.2f}", name, major, gpa);
29 }
30 }
31
32 public static void demonstrateDataProcessing() {
33 System.out.println("=== Data Processing with Collections ===");
34
35 // Create sample data
36 List<Student> students = Arrays.asList(
37 new Student("Alice", "Computer Science", 3.8, Arrays.asList("Math", "Programming", "Algorithms")),
38 new Student("Bob", "Mathematics", 3.5, Arrays.asList("Calculus", "Linear Algebra", "Statistics")),
39 new Student("Charlie", "Computer Science", 3.9, Arrays.asList("Programming", "Database", "Networks")),
40 new Student("Diana", "Physics", 3.7, Arrays.asList("Physics", "Math", "Lab")),
41 new Student("Eve", "Computer Science", 3.6, Arrays.asList("Programming", "AI", "Machine Learning"))
42 );
43
44 // Group students by major
45 Map<String, List<Student>> studentsByMajor = new HashMap<>();
46 for (Student student : students) {
47 studentsByMajor.computeIfAbsent(student.getMajor(), k -> new ArrayList<>()).add(student);
48 }
49
50 System.out.println("Students grouped by major:");
51 studentsByMajor.forEach((major, studentList) -> {
52 System.out.println(major + ": " + studentList.size() + " students");
53 studentList.forEach(s -> System.out.println(" - " + s.getName()));
54 });
55
56 // Find students with high GPA
57 List<Student> highAchievers = new ArrayList<>();
58 for (Student student : students) {
59 if (student.getGpa() >= 3.7) {
60 highAchievers.add(student);
61 }
62 }
63
64 System.out.println("\nHigh achievers (GPA >= 3.7):");
65 highAchievers.forEach(System.out::println);
66
67 // Count course enrollments
68 Map<String, Integer> courseEnrollments = new HashMap<>();
69 for (Student student : students) {
70 for (String course : student.getCourses()) {
71 courseEnrollments.merge(course, 1, Integer::sum);
72 }
73 }
74
75 System.out.println("\nCourse enrollments:");
76 courseEnrollments.entrySet().stream()
77 .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
78 .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
79
80 // Find students taking common courses
81 Set<String> commonCourses = new HashSet<>(students.get(0).getCourses());
82 for (int i = 1; i < students.size(); i++) {
83 commonCourses.retainAll(students.get(i).getCourses());
84 }
85
86 System.out.println("\nCourses taken by all students: " + commonCourses);
87 }
88
89 public static void demonstrateCollectionAlgorithms() {
90 System.out.println("\n=== Collection Algorithms ===");
91
92 List<Integer> numbers = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3, 7, 4, 6));
93 System.out.println("Original list: " + numbers);
94
95 // Sorting
96 List<Integer> sortedNumbers = new ArrayList<>(numbers);
97 Collections.sort(sortedNumbers);
98 System.out.println("Sorted: " + sortedNumbers);
99
100 // Reverse sorting
101 List<Integer> reverseSorted = new ArrayList<>(numbers);
102 Collections.sort(reverseSorted, Collections.reverseOrder());
103 System.out.println("Reverse sorted: " + reverseSorted);
104
105 // Shuffle
106 List<Integer> shuffled = new ArrayList<>(numbers);
107 Collections.shuffle(shuffled);
108 System.out.println("Shuffled: " + shuffled);
109
110 // Binary search (requires sorted list)
111 int searchValue = 5;
112 int index = Collections.binarySearch(sortedNumbers, searchValue);
113 System.out.println("Index of " + searchValue + " in sorted list: " + index);
114
115 // Min and max
116 Integer min = Collections.min(numbers);
117 Integer max = Collections.max(numbers);
118 System.out.println("Min: " + min + ", Max: " + max);
119
120 // Frequency
121 List<String> words = Arrays.asList("apple", "banana", "apple", "cherry", "banana", "apple");
122 int appleCount = Collections.frequency(words, "apple");
123 System.out.println("Frequency of 'apple': " + appleCount);
124
125 // Rotate
126 List<Integer> toRotate = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
127 Collections.rotate(toRotate, 2);
128 System.out.println("After rotating by 2: " + toRotate);
129
130 // Fill and replace
131 List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d", "e"));
132 Collections.fill(list, "x");
133 System.out.println("After fill: " + list);
134
135 List<String> replaceList = new ArrayList<>(Arrays.asList("a", "b", "a", "c", "a"));
136 Collections.replaceAll(replaceList, "a", "z");
137 System.out.println("After replace all 'a' with 'z': " + replaceList);
138 }
139}
Performance Characteristics and Best Practices
Choosing the Right Collection
ArrayList:
- ✅ Fast random access O(1)
- ✅ Memory efficient
- ❌ Slow insertion/deletion at beginning O(n)
- 💡 Use when: frequent random access, append operations
LinkedList:
- ✅ Fast insertion/deletion at ends O(1)
- ✅ Implements Queue and Deque
- ❌ Slow random access O(n)
- ❌ Higher memory overhead
- 💡 Use when: frequent insertion/deletion, queue operations
- ✅ Fast add/remove/contains O(1) average
- ❌ No ordering guarantee
- 💡 Use when: uniqueness and fast lookups matter most
LinkedHashSet:
- ✅ Maintains insertion order
- ✅ Fast operations O(1) average
- ❌ Higher memory overhead
- 💡 Use when: need uniqueness and predictable iteration order
TreeSet:
- ✅ Sorted order maintained
- ✅ Implements NavigableSet
- ❌ Slower operations O(log n)
- 💡 Use when: need sorted unique elements
- ✅ Fast operations O(1) average
- ❌ No ordering guarantee
- 💡 Use when: general-purpose key-value storage
LinkedHashMap:
- ✅ Maintains insertion/access order
- ✅ Fast operations O(1) average
- 💡 Use when: need predictable iteration order
TreeMap:
- ✅ Sorted by keys
- ✅ Implements NavigableMap
- ❌ Slower operations O(log n)
- 💡 Use when: need sorted key-value pairs
1import java.util.*;
2
3public class PerformanceComparison {
4
5 public static void compareListPerformance() {
6 System.out.println("=== List Performance Comparison ===");
7
8 int size = 100000;
9
10 // ArrayList vs LinkedList for different operations
11 testAddAtEnd(size);
12 testAddAtBeginning(size);
13 testRandomAccess(size);
14 testIteration(size);
15 }
16
17 private static void testAddAtEnd(int size) {
18 System.out.println("\nAdding " + size + " elements at end:");
19
20 // ArrayList
21 long start = System.nanoTime();
22 List<Integer> arrayList = new ArrayList<>();
23 for (int i = 0; i < size; i++) {
24 arrayList.add(i);
25 }
26 long arrayListTime = System.nanoTime() - start;
27
28 // LinkedList
29 start = System.nanoTime();
30 List<Integer> linkedList = new LinkedList<>();
31 for (int i = 0; i < size; i++) {
32 linkedList.add(i);
33 }
34 long linkedListTime = System.nanoTime() - start;
35
36 System.out.println("ArrayList: " + arrayListTime / 1_000_000 + " ms");
37 System.out.println("LinkedList: " + linkedListTime / 1_000_000 + " ms");
38 }
39
40 private static void testAddAtBeginning(int size) {
41 System.out.println("\nAdding " + size + " elements at beginning:");
42
43 // ArrayList (expensive)
44 long start = System.nanoTime();
45 List<Integer> arrayList = new ArrayList<>();
46 for (int i = 0; i < size; i++) {
47 arrayList.add(0, i);
48 }
49 long arrayListTime = System.nanoTime() - start;
50
51 // LinkedList (efficient)
52 start = System.nanoTime();
53 LinkedList<Integer> linkedList = new LinkedList<>();
54 for (int i = 0; i < size; i++) {
55 linkedList.addFirst(i);
56 }
57 long linkedListTime = System.nanoTime() - start;
58
59 System.out.println("ArrayList: " + arrayListTime / 1_000_000 + " ms");
60 System.out.println("LinkedList: " + linkedListTime / 1_000_000 + " ms");
61 }
62
63 private static void testRandomAccess(int size) {
64 System.out.println("\nRandom access (" + size + " operations):");
65
66 List<Integer> arrayList = new ArrayList<>();
67 List<Integer> linkedList = new LinkedList<>();
68
69 // Populate lists
70 for (int i = 0; i < size; i++) {
71 arrayList.add(i);
72 linkedList.add(i);
73 }
74
75 Random random = new Random();
76
77 // ArrayList random access
78 long start = System.nanoTime();
79 for (int i = 0; i < size; i++) {
80 int index = random.nextInt(arrayList.size());
81 arrayList.get(index);
82 }
83 long arrayListTime = System.nanoTime() - start;
84
85 // LinkedList random access
86 start = System.nanoTime();
87 for (int i = 0; i < size; i++) {
88 int index = random.nextInt(linkedList.size());
89 linkedList.get(index);
90 }
91 long linkedListTime = System.nanoTime() - start;
92
93 System.out.println("ArrayList: " + arrayListTime / 1_000_000 + " ms");
94 System.out.println("LinkedList: " + linkedListTime / 1_000_000 + " ms");
95 }
96
97 private static void testIteration(int size) {
98 System.out.println("\nIteration over " + size + " elements:");
99
100 List<Integer> arrayList = new ArrayList<>();
101 List<Integer> linkedList = new LinkedList<>();
102
103 // Populate lists
104 for (int i = 0; i < size; i++) {
105 arrayList.add(i);
106 linkedList.add(i);
107 }
108
109 // ArrayList iteration
110 long start = System.nanoTime();
111 for (Integer value : arrayList) {
112 // Simulate some work
113 }
114 long arrayListTime = System.nanoTime() - start;
115
116 // LinkedList iteration
117 start = System.nanoTime();
118 for (Integer value : linkedList) {
119 // Simulate some work
120 }
121 long linkedListTime = System.nanoTime() - start;
122
123 System.out.println("ArrayList: " + arrayListTime / 1_000_000 + " ms");
124 System.out.println("LinkedList: " + linkedListTime / 1_000_000 + " ms");
125 }
126}
Best Practices Summary
Collection Best Practices:
- Choose the right collection based on usage patterns
- Specify initial capacity for ArrayList/HashMap when size is known
- Use generics to ensure type safety
- Implement equals() and hashCode() properly for custom objects in Sets/Maps
- Use enhanced for-loops for better readability
- Consider using streams for data processing (Java 8+)
- Prefer Collections.unmodifiableX() for read-only views
- Use try-with-resources for collections that implement AutoCloseable
Common Pitfalls:
- Modifying collections during iteration (use Iterator.remove())
- Not overriding equals()/hashCode() for custom objects
- Using raw types instead of generics
- Choosing wrong collection type for the use case
- Not considering thread safety in concurrent environments
The Java Collections Framework provides powerful, flexible tools for data management. Choose the right collection for your specific needs and leverage their built-in methods for efficient data processing.