นากมิ้นต์ต้นฉบับรวบรวมชิ้นงานลงตะกร้าแล้วกดฝาสีอำพันให้ล็อกหลังเก็บครบ

Java collectingAndThen: เก็บข้อมูลก่อน แล้วล็อกผลลัพธ์ตอนจบ

พี่โชว์Engineering

บาง 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 ที่ซับซ้อน