0Pricing
Java Academy · บทเรียน

partitioningBy และ counting

แบ่งสตรีมออกเป็นสองกลุ่มด้วย partitioningBy และนับองค์ประกอบด้วย counting

partitioningBy และ counting เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

พื้นฐานของ partitioningBy

Collectors.partitioningBy(predicate) แบ่งสตรีมออกเป็นสองกลุ่มพอดี ได้แก่ true และ false แล้วคืนค่าเป็น Map<Boolean, List<T>>

import java.util.*;
import java.util.stream.*;

List<Integer> nums = List.of(1,2,3,4,5,6,7,8,9,10);

Map<Boolean, List<Integer>> evenOdd =
    nums.stream().collect(Collectors.partitioningBy(n -> n % 2 == 0));

System.out.println(evenOdd.get(true));  // [2, 4, 6, 8, 10]
System.out.println(evenOdd.get(false)); // [1, 3, 5, 7, 9]

partitioningBy เทียบกับ filter

ต่างจาก filter ตรงที่ partitioningBy จะเก็บทั้งสองกลุ่มไว้พร้อมกัน จึงเหมาะเมื่อคุณต้องการข้อมูลทั้งสองส่วนของการแบ่งกลุ่ม

record Student(String name, int score) {}
List<Student> students = List.of(
    new Student("Alice",90), new Student("Bob",55),
    new Student("Carol",72), new Student("Dave",48)
);

Map<Boolean, List<Student>> result =
    students.stream().collect(
        Collectors.partitioningBy(s -> s.score() >= 60)
    );
System.out.println("Passed: " + result.get(true).size());
System.out.println("Failed: " + result.get(false).size());

การใช้ตัวรวบรวมปลายทางกับ partitioningBy

รวม partitioningBy กับตัวรวบรวมปลายทางได้เช่นเดียวกับ groupingBy:

Map<Boolean, Long> passFailCount =
    students.stream().collect(
        Collectors.partitioningBy(
            s -> s.score() >= 60,
            Collectors.counting()
        )
    );
System.out.println("Passed: " + passFailCount.get(true));
System.out.println("Failed: " + passFailCount.get(false));

ตัวรวบรวม counting()

Collectors.counting() นับจำนวนองค์ประกอบในสตรีมหรือกลุ่มปลายทาง ซึ่งเทียบเท่าอย่างสะดวกกับ reducing(0L, e -> 1L, Long::sum)

long count = Stream.of("a","bb","ccc","dddd")
    .collect(Collectors.counting());
System.out.println(count); // 4

// Same as:
long count2 = Stream.of("a","bb","ccc","dddd").count();
// Use counting() as a downstream; use .count() as a terminal op

การใช้ counting() เป็นตัวรวบรวมปลายทาง

counting() มีประโยชน์อย่างยิ่งเมื่อใช้เป็นตัวรวบรวมปลายทางสำหรับ groupingBy/partitioningBy:

List<String> words = List.of("cat","dog","car","door","cup","dune");

Map<Character, Long> byFirstLetter =
    words.stream().collect(
        Collectors.groupingBy(w -> w.charAt(0), Collectors.counting())
    );
byFirstLetter.forEach((c, n) -> System.out.println(c + ": " + n));
// c: 3, d: 3

แบ่งกลุ่มเป็นชื่อ

ดึงชื่อจากแต่ละกลุ่มโดยใช้ mapping เป็นตัวรวบรวมปลายทาง:

Map<Boolean, List<String>> namesByPass =
    students.stream().collect(
        Collectors.partitioningBy(
            s -> s.score() >= 60,
            Collectors.mapping(Student::name, Collectors.toList())
        )
    );
System.out.println("Passed: " + namesByPass.get(true));
System.out.println("Failed: " + namesByPass.get(false));

การใช้ counting ร่วมกับ summarizingInt

Collectors.summarizingInt() คำนวณจำนวน ผลรวม ค่าต่ำสุด ค่าสูงสุด และค่าเฉลี่ยได้ในการส่งผ่านครั้งเดียว:

IntSummaryStatistics stats =
    students.stream().collect(
        Collectors.summarizingInt(Student::score)
    );
System.out.println("Count: " + stats.getCount());
System.out.println("Average: " + stats.getAverage());
System.out.println("Max: " + stats.getMax());
System.out.println("Min: " + stats.getMin());

กรณีใช้งานจริง: การแบ่งกลุ่มการทดลอง A/B

แบ่งผู้ใช้เป็นกลุ่มควบคุมและกลุ่มที่ได้รับการทดลองสำหรับการทดสอบ A/B:

record User(String id, boolean isInTreatment) {}
List<User> users = List.of(
    new User("u1",true), new User("u2",false),
    new User("u3",true), new User("u4",false)
);

Map<Boolean, Long> split =
    users.stream().collect(
        Collectors.partitioningBy(User::isInTreatment, Collectors.counting())
    );
System.out.println("Treatment: " + split.get(true)); // 2
System.out.println("Control: " + split.get(false));  // 2

แมปความถี่ด้วย groupingBy + counting

นับจำนวนครั้งที่แต่ละองค์ประกอบปรากฏ เช่น แมปความถี่ของคำ:

List<String> words2 = List.of("apple","banana","apple","cherry","banana","apple");

Map<String, Long> freq =
    words2.stream().collect(
        Collectors.groupingBy(w -> w, Collectors.counting())
    );
freq.entrySet().stream()
    .sorted(Map.Entry.<String,Long>comparingByValue().reversed())
    .forEach(e -> System.out.println(e.getKey()+": "+e.getValue()));

partitioningBy คืนค่าคีย์ทั้งสองเสมอ

ต่างจาก groupingBy ตรงที่ partitioningBy จะคืนแมปที่มีทั้งคีย์ true และ false เสมอ แม้กลุ่มใดกลุ่มหนึ่งจะว่างเปล่าก็ตาม จึงหลีกเลี่ยง NullPointerExceptions เมื่อใช้ map.get(true)

List<Integer> allEven = List.of(2,4,6);
Map<Boolean, List<Integer>> m =
    allEven.stream().collect(Collectors.partitioningBy(n -> n % 2 != 0));
System.out.println(m.get(true));  // [] (empty, not null)
System.out.println(m.get(false)); // [2, 4, 6]

ประสิทธิภาพ: การส่งผ่านครั้งเดียว

ทั้ง partitioningBy และ counting ทำงานโดยส่งผ่านสตรีมเพียงครั้งเดียว หรือ O(n) และไม่จำเป็นต้องจัดเรียงข้อมูล จึงมีประสิทธิภาพมากกว่าการเรียก filter สองครั้งหรือจัดเรียงข้อมูลก่อนจัดกลุ่มอย่างมาก

ตรวจสอบความเข้าใจ

ชนิดข้อมูลที่คืนค่าจาก Collectors.partitioningBy(predicate) คืออะไร

สรุป: partitioningBy และ counting

ประเด็นสำคัญ:

  • partitioningBy แบ่งข้อมูลออกเป็นสองกลุ่มพอดี ได้แก่ จริงและเท็จ
  • มีคีย์ทั้งสองเสมอ จึงไม่มีความเสี่ยง NPE เหมือน groupingBy
  • counting() ใช้นับองค์ประกอบ และใช้เป็นตัวรวบรวมปลายทางสำหรับกลุ่มได้
  • summarizingInt/ชนิดจำนวนเต็มยาว/ชนิดทศนิยมคู่ ให้จำนวน ผลรวม ค่าต่ำสุด ค่าสูงสุด และค่าเฉลี่ยในการส่งผ่านครั้งเดียว
  • ทั้งหมดทำงานในการส่งผ่านสตรีมครั้งเดียวแบบ O(n)

คำถามที่พบบ่อย

บทเรียน “partitioningBy และ counting” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “partitioningBy และ counting” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “partitioningBy และ counting”

แบ่งสตรีมออกเป็นสองกลุ่มด้วย partitioningBy และนับองค์ประกอบด้วย counting คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “partitioningBy และ counting” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. groupingBy: การจัดหมวดหมู่องค์ประกอบ
  2. partitioningBy และ counting
  3. toMap, joining และ summarizing
  4. การสร้าง Collector แบบกำหนดเอง
← กลับไปที่ Java Academy