MapExamples.java

package fr.univtln.bruno.samples.java101.tp3.map;

import fr.univtln.bruno.samples.java101.tp3.Person;
import fr.univtln.bruno.samples.java101.tp3.functional.GroupingDisplayExamples;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * Examples demonstrating Map implementations and modern Map APIs (compute, merge, etc.).
 * Warning: the modern Map methods heavily use functional interfaces (lambdas) so be sure to read the
 * functional programming lecture before using them.
 */
@Slf4j
public class MapExamples {
  /**
   * Private constructor to prevent instantiation.
   */
  private MapExamples() {
  }

  /**
   * Basic HashMap usage and update semantics.
   */
  public static void hashMapExample() {
    log.info("=== HashMap Example ===");
    Map<String, Person> people = new HashMap<>();
    people.put("P001", Person.of("Alice", "Smith", 30));
    people.put("P002", Person.of("Bob", "Jones", 25));
    people.put("P003", Person.of("Charlie", "Brown", 35));
    log.debug("Initial people map: {}", people);
    log.info("Size: {}", people.size());
    log.info("P001 -> {}", people.get("P001").getFullName());
    Person prev = people.put("P001", Person.of("Alice", "Smith", 31));
    log.debug("After updating P001 previous: {} current: {}", prev, people.get("P001"));
    log.info("Updated P001 age {} -> {}", prev.age(), people.get("P001").age());
    people.remove("P002");
    log.debug("After remove P002 map: {}", people);
    log.info("After remove P002 size: {}", people.size());
  }

  /**
   * LinkedHashMap to preserve insertion order.
   */
  public static void linkedHashMapExample() {
    log.info("=== LinkedHashMap Example ===");
    Map<String, String> capitals = new LinkedHashMap<>();
    capitals.put("France", "Paris");
    capitals.put("Germany", "Berlin");
    capitals.put("Italy", "Rome");
    capitals.forEach((c, a) -> log.info("{} -> {}", c, a));
  }

  /**
   * TreeMap (sorted map) demonstration.
   */
  public static void treeMapExample() {
    log.info("=== TreeMap Example ===");
    NavigableMap<String, String> capitals = new TreeMap<>();
    capitals.put("Spain", "Madrid");
    capitals.put("France", "Paris");
    capitals.put("Italy", "Rome");
    capitals.put("Germany", "Berlin");
    capitals.forEach((c, a) -> log.info("{} -> {}", c, a));
    log.info("FirstEntry {} LastEntry {}", capitals.firstEntry(), capitals.lastEntry());
  }

  /**
   * Modern Map methods: getOrDefault, putIfAbsent, computeIfAbsent, computeIfPresent, compute, merge.
   */
  public static void modernMapMethodsExample() {
    log.info("=== Modern Map Methods ===");
    Map<String, Integer> scores = new HashMap<>();
    scores.put("Alice", 100);
    scores.put("Bob", 85);
    log.debug("Scores initial: {}", scores);
    log.info("getOrDefault Charlie: {}", scores.getOrDefault("Charlie", 0));
    scores.putIfAbsent("Bob", 90);
    scores.putIfAbsent("David", 90);
    scores.computeIfAbsent("Eve", k -> k.length() * 10);
    scores.computeIfPresent("Alice", (k, v) -> v + 10);
    scores.compute("Bob", (k, v) -> v == null ? 0 : v + 5);
    scores.merge("Alice", 20, Integer::sum);
    scores.merge("Frank", 75, Integer::sum);
    scores.replace("Bob", 95);
    log.debug("Scores after modifications: {}", scores);
    log.info("Scores: {}", scores);

    // Student note: in multi-threaded contexts computeIfAbsent may race if the mapping function has side-effects.
    // Prefer using ConcurrentHashMap for concurrent scenarios or make the mapping function side-effect free.
    // Example (unsafe in multi-threaded code):
    // map.computeIfAbsent(key, k -> heavyInit(k)); // heavyInit should not have side-effects visible outside
  }

  /**
   * Different ways to iterate a map's entries, keys and values.
   */
  public static void iterationExample() {
    log.info("=== Iteration Example ===");
    Map<String, Integer> ages = Map.of("Alice", 30, "Bob", 25, "Charlie", 35);
    for (Map.Entry<String, Integer> e : ages.entrySet()) log.info("Entry {}={} ", e.getKey(), e.getValue());
    ages.keySet().forEach(k -> log.info("Key {}", k));
    ages.values().forEach(v -> log.info("Val {}", v));
  }

  /**
   * Counting and frequency aggregation examples using loops and merge.
   */
  public static void countingExample() {
    log.info("=== Counting Example ===");
    List<String> words = List.of("apple", "banana", "apple", "cherry", "banana", "apple");
    Map<String, Integer> manual = new HashMap<>();
    for (String w : words) manual.put(w, manual.getOrDefault(w, 0) + 1);
    Map<String, Integer> mergeMap = new HashMap<>();
    words.forEach(w -> mergeMap.merge(w, 1, Integer::sum));
    log.debug("Words list: {}", words);
    log.info("Manual {} Merge {}", manual, mergeMap);
  }

  /**
   * Grouping example implemented manually with computeIfAbsent.
   */
  public static void groupingExample() {
    log.info("=== Grouping Example ===");
    List<Person> people = List.of(
      Person.of("Alice", "Smith", 30),
      Person.of("Bob", "Jones", 25),
      Person.of("Charlie", "Brown", 35),
      Person.of("David", "Smith", 40)
    );
    GroupingDisplayExamples.showGroupingByLastName(people);
  }

  /**
   * Immutable map creation and views.
   */
  public static void immutableMapExample() {
    log.info("=== Immutable Map Example ===");
    Map<String, Integer> m1 = Map.of("A", 1, "B", 2, "C", 3);
    log.info("Map.of {}", m1);
    try {
      m1.put("D", 4);
    } catch (UnsupportedOperationException e) {
      log.info("Cannot modify immutable map");
    }
    Map<String, Integer> backing = new HashMap<>(Map.of("X", 1, "Y", 2));
    Map<String, Integer> view = Collections.unmodifiableMap(backing);
    backing.put("Z", 3);
    log.info("View after backing change: {}", view);
  }

  /**
   * Simple runner that executes Map examples. Useful for manual demo runs.
   *
   * @param args ignored
   */
  public static void main(String[] args) {
    hashMapExample();
    linkedHashMapExample();
    treeMapExample();
    modernMapMethodsExample();
    iterationExample();
    countingExample();
    groupingExample();
    immutableMapExample();
  }
}