Java collectingAndThen: เก็บข้อมูลก่อน แล้วล็อกผลลัพธ์ตอนจบ
บาง stream ต้องรวบรวมด้วย collector ปกติก่อน แล้วค่อยทำขั้นสุดท้าย เช่นห่อ list ไม่ให้แก้ไขครับ Collectors.collectingAndThen ผูก collector กับ finishing function เป็น contract เดียว
WHY: ผลลัพธ์ออกจากประตูในสภาพพร้อมใช้
เหมือนใส่ของลงตะกร้าให้ครบก่อนกดฝาล็อก ถ้าปล่อย mutable list ออกไปแล้วค่อยหวังว่าผู้เรียกจะไม่แก้ boundary ยังหลวม การวาง finisher ใน collector ทำให้ทุก call site ได้รูปแบบเดียวกัน
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
static List<String> normalizedNames(List<String> names) {
return names.stream()
.map(String::strip)
.filter(name -> !name.isEmpty())
.collect(Collectors.collectingAndThen(
Collectors.toList(),
Collections::unmodifiableList
));
}
public static void main(String[] args) {
List<String> result = normalizedNames(List.of(" Guy ", "", "Blog"));
assert result.equals(List.of("Guy", "Blog"));
try {
result.add("changed");
throw new AssertionError("result should be unmodifiable");
} catch (UnsupportedOperationException expected) {
System.out.println("collectingAndThen checks passed");
}
}
}
Java 17 มี Stream.toList() ซึ่งคืน unmodifiable list อยู่แล้ว จึงใช้ทางนั้นเมื่อโจทย์มีแค่ list ปิดแก้ แต่ collectingAndThen ยังมีค่าเมื่อ downstream เป็น groupingBy, averagingInt หรือ collector อื่น แล้วต้องแปลงผลสุดท้ายเป็น domain object
Recap
- downstream collector ทำช่วงสะสม
- finisher ทำครั้งเดียวหลังสะสมเสร็จ
- ใช้
toList()เมื่อโจทย์คือ unmodifiable list ธรรมดา - เหมาะกับการสร้าง domain result จาก collector ที่ซับซ้อน