นากกายเทเม็ดสีผ่านกรวยไม้แยกสองทางลงชามบนโต๊ะมิ้นต์

Java Collectors.teeing: สรุปสตรีมสองมุมในครั้งเดียว

น้องกายEngineering

เคยอยากได้ทั้งค่าเฉลี่ยและจำนวนจากลิสต์เดิม แต่ไม่อยากวนสตรีมสองรอบไหม ผมเคย จนได้ลอง Collectors.teeing

ตามAPI Java 21 teeing คือ collector ที่ส่งองค์ประกอบแต่ละตัวเข้า สอง collector ย่อย แล้วรวมผลด้วยฟังก์ชันผสาน คำว่า tee มาจากการแยกท่อสองทาง

ใช้เมื่อไหร่

เหมาะเมื่อต้องการสถิติคู่จากชุดข้อมูลเดียว เช่น ผลรวมกับจำนวน ค่าเฉลี่ยกับค่าสูงสุด หรือสรุปที่ต้องดูสองมุมพร้อมกัน ถ้าสองงานไม่เกี่ยวกับกันเลย การแยกเมธอดอาจอ่านง่ายกว่า

import java.util.List;
import java.util.stream.Collectors;

public class Main {
    record AvgAndCount(double average, long count) {}

    static AvgAndCount summarize(List<Integer> values) {
        return values.stream().collect(
            Collectors.teeing(
                Collectors.averagingInt(Integer::intValue),
                Collectors.counting(),
                AvgAndCount::new
            )
        );
    }

    public static void main(String[] args) {
        AvgAndCount result = summarize(List.of(2, 4, 6, 8));
        assert Math.abs(result.average() - 5.0) < 1e-9;
        assert result.count() == 4L;

        AvgAndCount empty = summarize(List.of());
        assert empty.count() == 0L;
    }
}

ตัวอย่างใช้ averagingInt คู่กับ counting แล้วห่อเป็น record อ่านง่ายในชั้นโดเมน

ข้อควรจำ

teeing ไม่ได้ทำให้ side effect ใน collector ย่อยหายไป และสตรีมขนานยังต้องคิดเรื่องความถูกต้องของ collector ที่ใช้ ถ้าต้องการสถิติหลายตัวในอ็อบเจ็กต์เดียว บางที summarizingInt / DoubleSummaryStatistics ก็พอโดยไม่ต้อง teeing

Recap

  • ใช้ teeing เมื่อต้องสรุปสองมุมจากสตรีมเดียว
  • รวมผลด้วยฟังก์ชัน/ record ที่ตั้งชื่อธุรกิจชัด
  • ทดสอบเคสว่างและเคสค่าปกติ
  • อ่านCollectors API ก่อนเลือกสถิติสำเร็จรูป