0Pricing
Java Academy · レッスン

partitioningBy と counting

partitioningBy でストリームを2つのグループに分け、counting で要素数を数えます。

「partitioningBy と counting」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

partitioningByの基本

Collectors.partitioningBy(predicate)は、ストリームをtrueとfalseという2つのグループに正確に分割し、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での下流コレクター

groupingByと同じように、partitioningByと下流コレクターを組み合わせられます:

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()は、1回の走査で件数、合計、最小値、最大値、平均値を計算します:

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による頻度Map

各要素の出現回数を数えます(単語頻度Map):

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の両方のキーを持つMapを返します。これにより、map.get(true)でNullPointerExceptionが発生するのを防げます。

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]

パフォーマンス: 1回の走査

partitioningByとcountingはどちらも、1回のストリーム走査で処理されます(O(n))。ソートは必要ありません。そのため、filterを2回呼び出したり、グループ化の前にソートしたりするよりも、はるかに効率的です。

理解度チェック

Collectors.partitioningBy(predicate)の戻り値の型は何ですか?

復習: partitioningByとcounting

重要なポイント:

  • partitioningByは要素を2つのグループ(true/false)に正確に分割します
  • 常に両方のキーが存在するため、groupingByと違ってNPEのリスクがありません
  • counting()は要素数を数え、グループの下流コレクターとして使用できます
  • summarizingInt/Long/Doubleは、1回の走査で件数+合計+最小値+最大値+平均値を計算します
  • すべて1回のO(n)のストリーム走査で処理されます

よくある質問

「partitioningBy と counting」レッスンは無料ですか?

はい。「partitioningBy と counting」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「partitioningBy と counting」で何を学びますか?

partitioningBy でストリームを2つのグループに分け、counting で要素数を数えます。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン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に戻る