本文通過「員工數據多維度分組」實戰,展示如何用 Lambda 表達式 + 泛型消除重複代碼,並進階到 Function 標準函數式接口與通配符邊界。
class Employee {
String name; int age; char sex; String time; int salary;
// getter/setter...
}
List data = Arrays.asList(e1, e2, e3...);
// 按姓名首字母分組 Map> groupByName = new HashMap<>(); for (Employee e : data) { Character k = e.getName().charAt(0); groupByName.computeIfAbsent(k, x -> new ArrayList<>()).add(e); } // 按薪資區間分組 Map > groupBySalary = new HashMap<>(); for (Employee e : data) { String k = salaryRange(e.getSalary()); // 5000以下/5000-10000... groupBySalary.computeIfAbsent(k, x -> new ArrayList<>()).add(e); } // ... 入職年份、部門等 N 種分組,邏輯 90% 相同
@FunctionalInterface interface KeyExtractor{ K extract(E e); } static Map > groupBy(List data, KeyExtractor extractor) { Map > result = new HashMap<>(); for (E e : data) { K key = extractor.extract(e); result.computeIfAbsent(key, k -> new ArrayList<>()).add(e); } return result; } // 調用 Map > byName = groupBy(data, e -> e.getName().charAt(0)); Map > bySalary = groupBy(data, e -> salaryRange(e.getSalary())); Map > byYear = groupBy(data, e -> entryYear(e.getTime()));
JDK 內置 Function 等價於 KeyExtractor,更通用:
staticMap > groupBy(List extends E> data, Function super E, ? extends K> mapper) { Map > result = new HashMap<>(); for (E e : data) { K key = mapper.apply(e); result.computeIfAbsent(key, k -> new ArrayList<>()).add(e); } return result; } // 調用更簡潔,類型推斷自動完成 Map > byName = groupBy(data, e -> e.getName().charAt(0)); Map > bySalary = groupBy(data, e -> salaryRange(e.getSalary()));
List extends E>:生產者 (Producer) —— 只讀取 E 及其子類,不可寫入 (PECS 原則)。Function super E, ? extends K>:消費者 (Consumer) —— 接受 E 及其父類,返回 K 及其子類。Map> byName = data.stream() .collect(Collectors.groupingBy(e -> e.getName().charAt(0)));
Stream 內部已處理泛型邊界,日常開發首選;手寫通用工具方法時理解泛型邊界更有幫助。
Function 是標準契約,優於自定義接口。