Week 4 - Collections Framework
Introduction to Java Collections Framework
The Java Collections Framework is a unified architecture for representing and manipulating collections. It includes:
- List – Ordered, allows duplicates
- Set – Unique elements, no duplicates
- Map – Key-value pairs with unique keys
Class 10.1 - Recording
▶ Watch VideoList
1. What is a List in Java?
A List is an ordered collection that:
- Maintains insertion order
- Allows duplicate elements
- Provides index-based access (like arrays)
Common List implementations:
- ArrayList – Fast reading, backed by array
- LinkedList – Fast insertion/removal, doubly linked
- Vector – Legacy, thread-safe (rarely used)
2. Basic List Example
import java.util.*;
public class ListExample {
public static void main(String[] args) {
// Create an ArrayList
List fruits = new ArrayList<>();
// Add elements
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
fruits.add("Banana"); // Duplicate allowed
// Access by index
System.out.println("First fruit: " + fruits.get(0));
// Size
System.out.println("Total fruits: " + fruits.size());
// Check existence
System.out.println("Contains Apple? " + fruits.contains("Apple"));
// Update element
fruits.set(1, "Mango");
// Remove element
fruits.remove("Orange");
// Iterate
System.out.println("\nIterating List:");
for (String fruit : fruits) {
System.out.println(fruit);
}
}
}
3. ArrayList vs LinkedList
| Operation | ArrayList | LinkedList |
|---|---|---|
| get(index) | O(1) - Very Fast | O(n) - Slow |
| add/remove at end | O(1) | O(1) |
| insert/remove in middle | O(n) - Slow | O(1) - Fast |
Use ArrayList when:
- You frequently need random access (get by index)
- You mostly add at the end
Use LinkedList when:
- You frequently insert/remove from the middle
- You don't need random access
4. Important List Methods
add(E)– Add at endadd(index, E)– Insert at positionget(index)– Access elementset(index, E)– Update elementremove(index)– Remove by indexremove(Object)– Remove first occurrencecontains(E)– Check if existssize()– Number of elementsclear()– Remove alladdAll(Collection)– Add multiplesubList(from, to)– Get portion of list
5. Best Practices for List
- Use
List<String>interface type, not implementation - Prefer ArrayList unless you need frequent insert/remove
- Use Iterator for safe removal during iteration
- Always use generics:
new ArrayList<String>()
Set
1. What is a Set in Java?
A Set is a collection that:
- Does NOT allow duplicate elements
- Does NOT maintain index
- May or may not maintain order
Common Set implementations:
- HashSet – Fastest, no ordering (O(1) average)
- LinkedHashSet – Maintains insertion order
- TreeSet – Sorted order (O(log n))
2. Basic Set Example
import java.util.*;
public class SetExample {
public static void main(String[] args) {
// Create a HashSet
Set fruits = new HashSet<>();
// Add elements
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
fruits.add("Banana"); // Duplicate ignored
// Check existence
System.out.println("Contains Apple? " + fruits.contains("Apple"));
// Remove element
fruits.remove("Orange");
// Size
System.out.println("Size: " + fruits.size());
// Iterate
System.out.println("\nSet contents:");
for (String fruit : fruits) {
System.out.println(fruit);
}
}
}
3. HashSet vs LinkedHashSet vs TreeSet
| Feature | HashSet | LinkedHashSet | TreeSet |
|---|---|---|---|
| Order | No | Insertion order | Sorted (ascending) |
| Speed | O(1) | O(1) | O(log n) |
| Null allowed? | Yes | Yes | No |
When to Use:
- HashSet: When speed is critical and order doesn't matter
- LinkedHashSet: When you need insertion order preserved
- TreeSet: When you need sorted data
4. Important Set Methods
add(E)– Add element (ignores duplicates)remove(E)– Remove elementcontains(E)– Check if existssize()– Number of elementsisEmpty()– Check if emptyclear()– Remove alladdAll(Collection)– Add multipleretainAll(Collection)– IntersectionremoveAll(Collection)– Difference
Set Methods Examples:
import java.util.*;
public class SetMethodsDemo {
public static void main(String[] args) {
// Create a Set
Set colors = new HashSet<>();
// 1. add(E) - Add element (ignores duplicates)
colors.add("Red");
colors.add("Green");
colors.add("Blue");
colors.add("Red"); // Duplicate - will be ignored
System.out.println("After add: " + colors); // [Red, Green, Blue]
// 2. remove(E) - Remove element
colors.remove("Green");
System.out.println("After remove: " + colors); // [Red, Blue]
// 3. contains(E) - Check if exists
boolean hasRed = colors.contains("Red");
System.out.println("Contains Red? " + hasRed); // true
System.out.println("Contains Yellow? " + colors.contains("Yellow")); // false
// 4. size() - Number of elements
System.out.println("Size: " + colors.size()); // 2
// 5. isEmpty() - Check if empty
System.out.println("Is empty? " + colors.isEmpty()); // false
// 6. addAll(Collection) - Add multiple elements
Set moreColors = new HashSet<>(Arrays.asList("Yellow", "Purple", "Red"));
colors.addAll(moreColors);
System.out.println("After addAll: " + colors); // [Red, Blue, Yellow, Purple]
// 7. clear() - Remove all elements
Set tempSet = new HashSet<>(colors);
tempSet.clear();
System.out.println("After clear: " + tempSet); // []
System.out.println("Is tempSet empty? " + tempSet.isEmpty()); // true
// 8. retainAll(Collection) - Intersection (keep only common elements)
Set set1 = new HashSet<>(Arrays.asList("A", "B", "C", "D"));
Set set2 = new HashSet<>(Arrays.asList("C", "D", "E", "F"));
set1.retainAll(set2);
System.out.println("Intersection (retainAll): " + set1); // [C, D]
// 9. removeAll(Collection) - Difference (remove common elements)
Set set3 = new HashSet<>(Arrays.asList("A", "B", "C", "D"));
Set set4 = new HashSet<>(Arrays.asList("C", "D", "E", "F"));
set3.removeAll(set4);
System.out.println("Difference (removeAll): " + set3); // [A, B]
// Practical Example: Remove duplicates from List
List numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
Set uniqueNumbers = new HashSet<>(numbers);
System.out.println("\nOriginal list: " + numbers);
System.out.println("Unique numbers: " + uniqueNumbers); // [1, 2, 3, 4, 5]
}
}
5. Set Operations
Set set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Set set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
// Union (combine both)
Set union = new HashSet<>(set1);
union.addAll(set2); // [1, 2, 3, 4, 5, 6, 7, 8]
// Intersection (common elements)
Set intersection = new HashSet<>(set1);
intersection.retainAll(set2); // [4, 5]
// Difference (in set1 but not in set2)
Set difference = new HashSet<>(set1);
difference.removeAll(set2); // [1, 2, 3]
6. Best Practices for Set
- Use
Set<String>interface type - Prefer HashSet for performance
- Sets automatically handle duplicates
- Use TreeSet when sorted data needed
Map
1. What is a Map in Java?
A Map stores data as key → value pairs:
- Keys are unique
- Values may be duplicated
- Access values using keys, not index
Common Map implementations:
- HashMap – Fastest, no ordering (O(1) average)
- LinkedHashMap – Maintains insertion order
- TreeMap – Sorted by key (O(log n))
2. Basic Map Example
import java.util.*;
public class MapExample {
public static void main(String[] args) {
// Create a HashMap
Map marks = new HashMap<>();
// Add key-value pairs
marks.put("Alice", 85);
marks.put("Bob", 92);
marks.put("Charlie", 78);
// Get value by key
System.out.println("Bob's marks: " + marks.get("Bob"));
// Check key
System.out.println("Has Alice? " + marks.containsKey("Alice"));
// Update value
marks.put("Bob", 95); // Updates Bob's value
// Remove entry
marks.remove("Charlie");
// Size
System.out.println("Total entries: " + marks.size());
// Iterate through all entries
System.out.println("\nAll marks:");
for (Map.Entry entry : marks.entrySet()) {
System.out.println(entry.getKey() + " → " + entry.getValue());
}
}
}
3. HashMap vs LinkedHashMap vs TreeMap
| Feature | HashMap | LinkedHashMap | TreeMap |
|---|---|---|---|
| Order | No | Insertion order | Sorted by key |
| Speed | O(1) | O(1) | O(log n) |
| Null key allowed? | Yes | Yes | No |
When to Use:
- HashMap: For speed with no ordering requirement
- LinkedHashMap: When insertion order matters
- TreeMap: When sorted keys needed
4. Important Map Methods
put(K, V)– Add/update entryget(K)– Retrieve value by keygetOrDefault(K, V)– Safe retrieval with defaultremove(K)– Remove by keycontainsKey(K)– Check key existscontainsValue(V)– Check value existskeySet()– Get all keys as Setvalues()– Get all values as CollectionentrySet()– Get key-value pairssize()– Number of entriesclear()– Remove all entriesisEmpty()– Check if emptyputAll(Map)– Add multiple entriesreplace(K, V)– Replace existing valueremove(K, V)– Remove if key-value matches
Map Methods Examples:
import java.util.*;
public class MapMethodsDemo {
public static void main(String[] args) {
// Create a Map
Map studentGrades = new HashMap<>();
// 1. put(K, V) - Add/update entry
studentGrades.put("Alice", 85);
studentGrades.put("Bob", 92);
studentGrades.put("Charlie", 78);
System.out.println("After put: " + studentGrades);
// {Alice=85, Bob=92, Charlie=78}
// Update existing value
studentGrades.put("Bob", 95); // Updates Bob's grade
System.out.println("After update: " + studentGrades);
// {Alice=85, Bob=95, Charlie=78}
// 2. get(K) - Retrieve value by key
Integer aliceGrade = studentGrades.get("Alice");
System.out.println("Alice's grade: " + aliceGrade); // 85
System.out.println("David's grade: " + studentGrades.get("David")); // null
// 3. getOrDefault(K, V) - Safe retrieval with default
Integer davidGrade = studentGrades.getOrDefault("David", 0);
System.out.println("David's grade (with default): " + davidGrade); // 0
// 4. remove(K) - Remove by key
studentGrades.remove("Charlie");
System.out.println("After remove: " + studentGrades);
// {Alice=85, Bob=95}
// 5. remove(K, V) - Remove only if key-value matches
studentGrades.remove("Alice", 90); // Won't remove (value doesn't match)
System.out.println("After failed remove: " + studentGrades);
studentGrades.remove("Alice", 85); // Will remove (exact match)
System.out.println("After successful remove: " + studentGrades);
// {Bob=95}
// Add more data for examples
studentGrades.put("Alice", 85);
studentGrades.put("Charlie", 78);
studentGrades.put("Diana", 88);
// 6. containsKey(K) - Check if key exists
boolean hasAlice = studentGrades.containsKey("Alice");
System.out.println("\nContains Alice? " + hasAlice); // true
System.out.println("Contains Eve? " + studentGrades.containsKey("Eve")); // false
// 7. containsValue(V) - Check if value exists
boolean hasGrade85 = studentGrades.containsValue(85);
System.out.println("Contains grade 85? " + hasGrade85); // true
System.out.println("Contains grade 100? " + studentGrades.containsValue(100)); // false
// 8. size() - Number of entries
System.out.println("\nTotal students: " + studentGrades.size()); // 4
// 9. isEmpty() - Check if empty
System.out.println("Is map empty? " + studentGrades.isEmpty()); // false
// 10. keySet() - Get all keys as Set
Set students = studentGrades.keySet();
System.out.println("\nAll students: " + students);
// [Alice, Bob, Charlie, Diana]
// 11. values() - Get all values as Collection
Collection grades = studentGrades.values();
System.out.println("All grades: " + grades);
// [85, 95, 78, 88]
// 12. entrySet() - Get key-value pairs
System.out.println("\nAll entries:");
for (Map.Entry entry : studentGrades.entrySet()) {
System.out.println(entry.getKey() + " → " + entry.getValue());
}
// 13. putAll(Map) - Add multiple entries
Map newStudents = new HashMap<>();
newStudents.put("Eve", 91);
newStudents.put("Frank", 82);
studentGrades.putAll(newStudents);
System.out.println("\nAfter putAll: " + studentGrades);
// {Alice=85, Bob=95, Charlie=78, Diana=88, Eve=91, Frank=82}
// 14. replace(K, V) - Replace existing value
studentGrades.replace("Bob", 98);
System.out.println("After replace: " + studentGrades.get("Bob")); // 98
// replace(K, oldV, newV) - Replace only if old value matches
boolean replaced = studentGrades.replace("Alice", 85, 90);
System.out.println("Replaced Alice? " + replaced); // true
System.out.println("Alice's new grade: " + studentGrades.get("Alice")); // 90
// 15. clear() - Remove all entries
Map tempMap = new HashMap<>(studentGrades);
tempMap.clear();
System.out.println("\nAfter clear: " + tempMap); // {}
System.out.println("Is tempMap empty? " + tempMap.isEmpty()); // true
// Practical Example: Count word frequencies
System.out.println("\n--- Word Frequency Counter ---");
String text = "java is great java is powerful java is fun";
Map wordCount = new HashMap<>();
for (String word : text.split(" ")) {
// Get current count, default to 0, then add 1
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
System.out.println("Word frequencies: " + wordCount);
// {java=3, is=3, great=1, powerful=1, fun=1}
}
}
5. Iterating Through Maps
Using entrySet() (Most Efficient)
for (Map.Entry entry : marks.entrySet()) {
String name = entry.getKey();
Integer score = entry.getValue();
System.out.println(name + " → " + score);
}
Using keySet()
for (String name : marks.keySet()) {
System.out.println(name + " → " + marks.get(name));
}
Using values()
for (Integer score : marks.values()) {
System.out.println("Score: " + score);
}
Using forEach() (Java 8+)
marks.forEach((name, score) -> {
System.out.println(name + " → " + score);
});
6. Modern Map Methods (Java 8+)
computeIfAbsent() - Compute if key missing
Map> courses = new HashMap<>();
// Add courses to students
courses.computeIfAbsent("Alice", k -> new ArrayList<>()).add("Math");
courses.computeIfAbsent("Alice", k -> new ArrayList<>()).add("Physics");
courses.computeIfAbsent("Bob", k -> new ArrayList<>()).add("Chemistry");
// Output: {Alice=[Math, Physics], Bob=[Chemistry]}
getOrDefault() - Safe value access
// Returns default value if key not found
Integer score = marks.getOrDefault("David", 0);
System.out.println("Score: " + score); // 0 if David not found
merge() - Merge values
// Count word occurrences
Map wordCount = new HashMap<>();
String text = "hello world hello java";
for (String word : text.split(" ")) {
wordCount.merge(word, 1, Integer::sum);
}
// Output: {hello=2, world=1, java=1}
7. Real-World Use Cases
1. Caching
Map cache = new HashMap<>();
String getDataFromCache(int id) {
return cache.computeIfAbsent(id, key -> expensiveOperation(key));
}
2. Grouping Data
// Group students by grade
Map> groupByGrade = new HashMap<>();
groupByGrade.computeIfAbsent("A", k -> new ArrayList<>()).add("Alice");
groupByGrade.computeIfAbsent("B", k -> new ArrayList<>()).add("Bob");
groupByGrade.computeIfAbsent("A", k -> new ArrayList<>()).add("Charlie");
3. Frequency Counting
// Count character frequencies
Map freqMap = new HashMap<>();
String text = "hello";
for (char c : text.toCharArray()) {
freqMap.merge(c, 1, Integer::sum);
}
// Output: {h=1, e=1, l=2, o=1}
8. Best Practices for Map
- Use
Map<K, V>interface type - Prefer HashMap for speed
- Use
entrySet()for iteration (most efficient) - Use
getOrDefault()instead of null checks - Use
computeIfAbsent()for complex values - Avoid mutable objects as keys