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 Video

List

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 end
  • add(index, E) – Insert at position
  • get(index) – Access element
  • set(index, E) – Update element
  • remove(index) – Remove by index
  • remove(Object) – Remove first occurrence
  • contains(E) – Check if exists
  • size() – Number of elements
  • clear() – Remove all
  • addAll(Collection) – Add multiple
  • subList(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 element
  • contains(E) – Check if exists
  • size() – Number of elements
  • isEmpty() – Check if empty
  • clear() – Remove all
  • addAll(Collection) – Add multiple
  • retainAll(Collection) – Intersection
  • removeAll(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 entry
  • get(K) – Retrieve value by key
  • getOrDefault(K, V) – Safe retrieval with default
  • remove(K) – Remove by key
  • containsKey(K) – Check key exists
  • containsValue(V) – Check value exists
  • keySet() – Get all keys as Set
  • values() – Get all values as Collection
  • entrySet() – Get key-value pairs
  • size() – Number of entries
  • clear() – Remove all entries
  • isEmpty() – Check if empty
  • putAll(Map) – Add multiple entries
  • replace(K, V) – Replace existing value
  • remove(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