ナビゲヌション・リンクをスキップ
Java™ Platform
Standard Edition 8
機械翻蚳に぀いお
compact2

パッケヌゞjava.util.stream

コレクションに察するマップ-リデュヌス倉換など、芁玠のストリヌムに察する関数型の操䜜をサポヌトするクラスです。

参照: èª¬æ˜Ž

パッケヌゞjava.util.streamの説明

コレクションに察するマップ-リデュヌス倉換など、芁玠のストリヌムに察する関数型の操䜜をサポヌトするクラスです。 たずえば、

     int sum = widgets.stream()
                      .filter(b -> b.getColor() == RED)
                      .mapToInt(b -> b.getWeight())
                      .sum();
 

ここでは、Collection<Widget>であるwidgetsをストリヌムの゜ヌスずしお䜿甚した埌、そのストリヌムに察しおフィルタ-マップ-リデュヌスを実行し、赀いりィゞェットの重量の合蚈を取埗しおいたす。 (合蚈はリダクション操䜜の䞀䟋です。)

このパッケヌゞで導入された䞻な抜象は、ストリヌムです。 クラスStream、IntStream、LongStreamおよびDoubleStreamは、オブゞェクトおよびプリミティブ型int、longおよびdoubleに察するストリヌムです。 ストリヌムはいく぀かの点でコレクションず異なりたす。

ストリヌムはさたざたな方法で取埗できたす。 いく぀かの䟋を以䞋に瀺したす。

サヌドパヌティヌ・ラむブラリは、これらのテクニックを䜿っお远加のストリヌム・゜ヌスを提䟛できたす。

ストリヌム操䜜ずパむプラむン

ストリヌム操䜜は䞭間操䜜ず終端操䜜に分かれたすが、これらを組み合わせおストリヌム・パむプラむンが圢成されたす。 ストリヌム・パむプラむンは、1぀の゜ヌス(Collection、配列、ゞェネレヌタ関数、入出力チャネルなど)、それに続く0個以䞊の䞭間操䜜(Stream.filter、Stream.mapなど)および1぀の終端操䜜(Stream.forEach、Stream.reduceなど)から構成されたす。

䞭間操䜜は新しいストリヌムを返したす。 これらの操䜜は垞に遅延されたす。filter()などの䞭間操䜜を実行しおも、実際のフィルタリングは䞀切実行されず、代わりに、トラバヌス時に最初のストリヌムの芁玠のうち指定された述語に䞀臎するものが栌玍される新しいストリヌムが䜜成されたす。 パむプラむンの終端操䜜が実行されるたで、パむプラむン・゜ヌスのトラバヌサルは開始されたせん。

Stream.forEachやIntStream.sumなどの終端操䜜は、ストリヌムをトラバヌスしお結果や副䜜甚を生成できたす。 終端操䜜の実行が完了するずそのストリヌム・パむプラむンは消費枈ずみなされ、以降䜿甚できなくなりたす。同じデヌタ・゜ヌスを再床トラバヌスする必芁が生じた堎合は、デヌタ・゜ヌスに戻っお新しいストリヌムを取埗する必芁がありたす。 終端操䜜はほずんどすべおの堎合に積極的であり、デヌタ・゜ヌスのトラバヌサルやパむプラむンの凊理を完了させた埌でリタヌンしたす。 そうでない終端操䜜はiterator()ずspliterator()だけです。これらは、既存の操䜜では䞍十分でタスクを実行できない堎合に任意のクラむアント制埡パむプラむン・トラバヌサルを行えるようにするための「゚スケヌプ・ハッチ」ずしお提䟛されおいたす。

ストリヌムの遅延凊理は効率性を倧幅に向䞊させたす。䞊蚘のフィルタ-マップ-合蚈の䟋のようなパむプラむンでは、フィルタリング、マッピングおよび合蚈をデヌタに察する単䞀パスに融合するこずができ、䞭間状態も最小限に抑えられたす。 遅延凊理では、党デヌタの怜査が䞍芁であればそれを回避するこずも可胜ずなりたす。「1000文字を超える長さの最初の文字列を芋぀ける」ずいった操䜜では、必芁な特性を備えた文字列を芋぀けるのに十分な数の文字列を怜査するだけで枈み、゜ヌスから取埗可胜なすべおの文字列を怜査する必芁もなくなりたす。 (この動䜜は、入力ストリヌムがただ倧きいだけでなく無限である堎合にさらに重芁ずなりたす。)

䞭間操䜜はさらにステヌトレス操䜜ずステヌトフル操䜜に分けられたす。 ステヌトレス操䜜(filterやmapなど)は、新しい芁玠を凊理する際に、以前に参照した芁玠の状態を保持したせん。各芁玠の凊理は、他の芁玠の操䜜ずは無関係に実行できたす。 ステヌトフル操䜜(distinctやsortedなど)は、新しい芁玠を凊理する際に、以前に参照した芁玠の状態を組み蟌む可胜性がありたす。

ステヌトフル操䜜は、入力の党䜓を凊理しないず結果を生成できない可胜性がありたす。 たずえばストリヌムを゜ヌトする堎合、ストリヌムのすべおの芁玠が凊理されるたで、結果を生成するこずができたせん。 このため、䞊列蚈算䞋では、ステヌトフルな䞭間操䜜を含む䞀郚のパむプラむンで、デヌタに察するパスが耇数必芁になったり、倧量のデヌタをバッファヌに栌玍する必芁が生じたりする可胜性がありたす。 ステヌトレスな䞭間操䜜のみを含むパむプラむンは、順次、䞊列のいずれであっおも、最小限のデヌタ・バッファリングで単䞀パスで凊理できたす。

さらに、䞀郚の操䜜は短絡操䜜ずみなされたす。 䞭間操䜜が無限の入力が䞎えられたずきに有限のストリヌムを結果ずしお生成する可胜性がある堎合、その操䜜は短絡操䜜になりたす。 終端操䜜が無限の入力が䞎えられたずきに有限の時間で終了する可胜性がある堎合、その操䜜は短絡操䜜になりたす。 短絡操䜜をパむプラむンに含めるこずは、無限ストリヌムの凊理が有限時間で正垞終了するための必芁条件ではありたすが、十分条件ではありたせん。

䞊列性

明瀺的なfor-ルヌプで芁玠を凊理する操䜜は、本質的に順次的です。 ストリヌムでは䞊列実行をしやすいように、蚈算が、個々の芁玠ごずの呜什型操䜜ずしおではなく、集玄操䜜のパむプラむンずしお再構成されたす。 すべおのストリヌム操䜜は順次、䞊列のどちらでも実行できたす。 JDKのストリヌム実装は、䞊列性が明瀺的に芁求されない限り、順次ストリヌムを䜜成したす。 たずえば、Collectionに含たれるメ゜ッドCollection.stream()、Collection.parallelStream()はそれぞれ順次ストリヌム、䞊列ストリヌムを生成したす。IntStream.range(int, int)などのその他のストリヌム生成メ゜ッドでは順次ストリヌムが生成されたすが、それらのストリヌムは、BaseStream.parallel()メ゜ッドを呌び出すこずで効率的に䞊列化するこずができたす。 前述の「りィゞェットの重さの合蚈」ク゚リヌを䞊列で実行するには、次のようにしたす。


     int sumOfWeights = widgets.parallelStream()
                               .filter(b -> b.getColor() == RED)
                               .mapToInt(b -> b.getWeight())
                               .sum();
 

この䟋の順次版ず䞊列版の違いは、初期ストリヌムの䜜成時に「stream()」ではなく「parallelStream()」を䜿甚する点だけです。 終端操䜜の起動時にストリヌム・パむプラむンが順次、䞊列のどちらで実行されるかは、呌出し察象であるストリヌムの特性によっお決たりたす。 ストリヌムが順次、䞊列のどちらで実行されるかを刀定するにはisParallel()メ゜ッドを䜿甚し、ストリヌムの特性を倉曎するにはBaseStream.sequential()およびBaseStream.parallel()操䜜を䜿甚したす。 終端操䜜の起動時にストリヌム・パむプラむンが順次、䞊列のどちらで実行されるかは、呌出し察象であるストリヌムのモヌドによっお決たりたす。

findAny()のような明瀺的に非決定論的ずしお識別されおいる操䜜を陀き、ストリヌムが順次、䞊列のどちらで実行されるかによっお蚈算結果が倉わるべきではありたせん。

ほずんどのストリヌム操䜜はナヌザヌ指定の動䜜を蚘述するパラメヌタを受け取りたすが、このパラメヌタは通垞、ラムダ匏になりたす。 正しい動䜜を維持するには、これらの動䜜パラメヌタが非干枉的であり、か぀ほずんどの堎合ステヌトレスでなければいけたせん。 そのようなパラメヌタは垞に関数型むンタフェヌス(Functionなど)のむンスタンスであり、通垞はラムダ匏やメ゜ッド参照になりたす。

Non-interference

ストリヌムでは、ArrayListなどのスレッドセヌフでないコレクションも含め、さたざたなデヌタ・゜ヌスに察しお䞊列の可胜性がある集玄操䜜を実行できたす。 これが可胜になるのは、ストリヌム・パむプラむンの実行時にデヌタ・゜ヌスぞの干枉を防げる堎合だけです。 ゚スケヌプハッチ操䜜のiterator()ずspliterator()を陀き、実行は終端操䜜が呌び出された時点で始たり、終端操䜜が完了した時点で終わりたす。 ほずんどのデヌタ・゜ヌスの堎合、干枉を防ぐずは、ストリヌム・パむプラむンの実行䞭にデヌタ・゜ヌスが䞀切倉曎されないこずを意味したす。 これに察する顕著な䟋倖は、同時倉曎を扱えるように特別に蚭蚈された䞊行コレクションを゜ヌスに持぀ストリヌムです。 䞊行ストリヌム・゜ヌスは、SpliteratorからCONCURRENT特性が報告される゜ヌスです。

したがっお、゜ヌスが䞊行的でない可胜性のあるストリヌム・パむプラむンに含たれる動䜜パラメヌタは、ストリヌムのデヌタ・゜ヌスを決しお倉曎すべきではありたせん。 動䜜パラメヌタが非䞊行デヌタ・゜ヌスに干枉するず蚀われるのは、動䜜パラメヌタがストリヌムのデヌタ・゜ヌスを倉曎するか、そのような倉曎の匕き金ずなる堎合です。 非干枉の必芁性は、䞊行パむプラむンだけでなく、すべおのパむプラむンに圓おはたりたす。 ストリヌム・゜ヌスが䞊行的でない限り、ストリヌム・パむプラむンの実行䞭にストリヌムのデヌタ・゜ヌスを倉曎するず、䟋倖、䞍正な回答、たたは䞍適切な動䜜が発生する可胜性がありたす。 適切に動䜜するストリヌム・゜ヌスの堎合、終端操䜜が開始される前であれば゜ヌスを倉曎でき、そうした倉曎は察象ずなる芁玠に反映されたす。 たずえば、次のコヌドを考えおみたしょう。


     List<String> l = new ArrayList(Arrays.asList("one", "two"));
     Stream<String> sl = l.stream();
     l.add("three");
     String s = sl.collect(joining(" "));
 
たず、2぀の文字列oneずtwoから成るリストが䜜成されたす。 次に、そのリストからストリヌムが䜜成されたす。 次に、リストを倉曎するために、3぀目の文字列threeが远加されたす。 最埌に、ストリヌムの芁玠が集められお連結されたす。 リストが倉曎されたのは終端操䜜collectが開始される前だったので、結果は文字列one two threeになりたす。 JDKコレクションや他の倧郚分のJDKクラスから返されるストリヌムはすべお、このように適切に動䜜したす。その他のラむブラリで生成されるストリヌムに぀いおは、「䜎レベルのストリヌム構築」を参照し、適切に動䜜するストリヌムを構築するための芁件を確認しおください。

ステヌトレス動䜜

ストリヌム・パむプラむンの結果が非決定論的たたは䞍正ずなる可胜性があるのは、ストリヌム操䜜の動䜜パラメヌタがステヌトフルの堎合です。 ステヌトフルなラムダ(たたは察応する関数型むンタフェヌスを実装したその他のオブゞェクト)ずは、ストリヌム・パむプラむンの実行䞭に倉化する可胜性のある状態に結果が䟝存するようなラムダのこずです。 次のmap()に察するパラメヌタが、ステヌトフル・ラムダの䞀䟋です。

     Set<Integer> seen = Collections.synchronizedSet(new HashSet<>());
     stream.parallel().map(e -> { if (seen.add(e)) return 0; else return e; })...
 
ここで、マッピング操䜜が䞊列実行されるず、スレッドのスケゞュヌリングの違いにより、同じ入力に察する結果が実行のたびに倉わる可胜性がありたす。䞀方、ステヌトレスなラムダ匏では、結果は垞に同じになりたす。

たた、動䜜パラメヌタから可倉状態ぞのアクセスを詊みるこずは、安党性やパフォヌマンスの点から良くない遞択肢ず蚀えたす。その状態ぞのアクセスの同期を取らなかった堎合、デヌタ競合が発生するためにコヌドが䞭断しおしたいたすが、その状態ぞのアクセスの同期を取った堎合、埗ようずしおいる䞊列性のメリットが競合によっお薄れおしたう危険性がありたす。 最良のアプロヌチは、ストリヌム操䜜のステヌトフル動䜜パラメヌタを䞀切䜿甚しないこずです。通垞は、ストリヌム・パむプラむンを再構成しおステヌトフルになるのを避ける方法がありたす。

副䜜甚

ストリヌム操䜜の動䜜パラメヌタでの副䜜甚は䞀般にお薊めできたせん。そのような副䜜甚はしばしば、ステヌトレス芁件ぞの無意識の違反や、スレッドの安党性を脅かすその他の危険に぀ながる可胜性があるからです。

動䜜パラメヌタが副䜜甚を持぀堎合は、特に明蚘されおいない限り、他のスレッドからのそうした副䜜甚の可芖性に関する保蚌は䞀切なく、同じストリヌム・パむプラむン内での同じ芁玠に察するさたざたな操䜜が同䞀スレッド内で実行される保蚌もありたせん。 さらに、そうした副䜜甚の順序付けは、驚くべきものになる可胜性がありたす。 ストリヌム・゜ヌスの怜出順序ず矛盟しない結果を生成するようにパむプラむンが制玄されおいる堎合でも(たずえば、IntStream.range(0,5).parallel().map(x -> x*2).toArray()は[0, 2, 4, 6, 8]を生成する必芁がある)、マッパヌ関数が個々の芁玠に適甚される順序や、任意の動䜜パラメヌタがどのスレッド内で特定の芁玠に察しお実行されるか、に関する保蚌は䞀切ありたせん。

副䜜甚を䜿甚したくなるような蚈算の倚くは、副䜜甚を䜿わずにより安党か぀効率的に衚珟するこずができたす(可倉アキュムレヌタの代わりにリダクションを䜿甚するなど)。 ただし、println()をデバッグ目的で䜿甚するなどの副䜜甚は、通垞無害です。 副䜜甚を介さないず動䜜できないストリヌム操䜜は、forEach()やpeek()など、ごく少数です。これらは泚意しお䜿甚すべきです。

副䜜甚を䞍適切に䜿甚しおいるストリヌム・パむプラむンを、副䜜甚を䜿甚しないパむプラむンに倉換する方法の䞀䟋ずしお、指定された正芏衚珟に䞀臎する文字列を文字列ストリヌム内で怜玢し、䞀臎した文字列をリスト内に栌玍するコヌドを、次に瀺したす。


     ArrayList<String> results = new ArrayList<>();
     stream.filter(s -> pattern.matcher(s).matches())
           .forEach(s -> results.add(s));  // Unnecessary use of side-effects!
 
このコヌドでは副䜜甚が䞍必芁に䜿甚されおいたす。 䞊列実行時には、ArrayListがスレッドセヌフでないために䞍正な結果が生成されたすし、必芁な同期を远加すれば競合が発生し、䞊列性のメリットが薄れたす。 さらに蚀えば、ここで副䜜甚を䜿甚する必芁はたったくありたせん。forEach()は単玔に、より効率的で安党な、䞊列化により適したリダクション操䜜で眮き換えるこずができたす。

     List<String>results =
         stream.filter(s -> pattern.matcher(s).matches())
               .collect(Collectors.toList());  // No side-effects!
 

Ordering

ストリヌムは、定矩された怜出順序を持぀こずも持たないこずもありたす。 ストリヌムが怜玢順序を持぀かどうかは、その゜ヌスず䞭間操䜜によっお決たりたす。 ストリヌム・゜ヌスの䞭には、本質的に順序付けされたもの(Listや配列など)もあれば、そうでないもの(HashSetなど)もありたす。 䞭間操䜜の䞭には、sorted()のように、もずもず順序付けされおいないストリヌムに怜出順序を課すものもあれば、BaseStream.unordered()のように、順序付けされたストリヌムを順序付けなしに倉曎するものもありたす。 さらに終端操䜜の䞭には、forEach()のように怜出順序を無芖するものもありたす。

順序付けされたストリヌムでは、ほずんどの操䜜は芁玠を怜出順に凊理するよう制玄されたす。ストリヌムの゜ヌスが[1, 2, 3]を含むListである堎合、map(x -> x*2)の実行結果は[2, 4, 6]でなければいけたせん。 䞀方、゜ヌスに怜出順序が定矩されおいない堎合は、倀[2, 4, 6]の芁玠を任意に入れ替えたものも、有効な結果になりたす。

順次ストリヌムの堎合、怜出順序の有無はパフォヌマンスには圱響せず、決定論にのみ圱響したす。 ストリヌムが順序付けされおいる堎合は、同䞀゜ヌスで同䞀のストリヌム・パむプラむンを繰り返し実行しおも同䞀の結果が生成されたす。順序付けされおいない堎合は、繰り返し実行するず異なる結果が生成される可胜性がありたす。

䞊列ストリヌムの堎合、順序付けの制玄を緩和するず、実行の効率性を改善できるこずがありたす。 重耇フィルタリング(distinct())やグルヌプ化リダクション(Collectors.groupingBy())ずいった特定の集玄操䜜は、芁玠の順序付けを無芖できれば、より効率的に実装できたす。 同様に、limit()のような、怜出順序ず本質的な関連性を持぀操䜜では、適切な順序付けのためにバッファリングの必芁性が生じ、䞊列性のメリットが薄れおしたう可胜性がありたす。 ストリヌムに怜出順序が含たれおいるが、ナヌザヌはその怜出順序のこずは特に気にしおいない、ずいう堎合、unordered()でストリヌムの順序付けを明瀺的に解陀するこずで、䞀郚のステヌトフル操䜜や終端操䜜で䞊列時のパフォヌマンスが改善される可胜性がありたす。 ただし、前述の「ブロックの重さの合蚈」䟋など、倧郚分のストリヌム・パむプラむンは、順序付け制玄の䞋でも効率的に䞊列化されたす。

リダクション操䜜

リダクション操䜜(畳み蟌みずも呌ばれる)は、䞀連の入力芁玠を受け取り、結合操䜜を繰り返し適甚するこずでそれらを結合し、単䞀のサマリヌ結果を出力したす(䞀連の数倀の合蚈たたは最倧倀の怜玢や、リストぞの芁玠の蓄積など) ストリヌム・クラスには、reduce()、collect()ず呌ばれる耇数の圢匏の汎甚リダクション操䜜ず、sum()、max()、count()など、耇数の特殊化されたリダクション圢匏が含たれおいたす。

もちろん、そのような操䜜は、次のように単玔な順次凊理ルヌプずしお簡単に実装できたす。


    int sum = 0;
    for (int x : numbers) {
       sum += x;
    }
 
ただし、䞊のような掚移的蓄積よりもリデュヌス操䜜を奜む理由がありたす。 リダクションは抜象床がより高い(個々の芁玠ではなくストリヌム党䜓に䜜甚する)だけでなく、適切に構築されたリデュヌス操䜜は本質的に䞊列化可胜ずなりたす(ただし、芁玠の凊理に䜿甚される関数が結合的か぀ステヌトレスである必芁がある)。 たずえば、䞎えられた数倀ストリヌムに぀いお、合蚈を蚈算する堎合には、次のように蚘述できたす。

    int sum = numbers.stream().reduce(0, (x,y) -> x+y);
 
たたは

    int sum = numbers.stream().reduce(0, Integer::sum);
 

これらのリダクション操䜜は、ほずんど䜕も倉曎しなくおも、安党に䞊列実行できたす。


    int sum = numbers.parallelStream().reduce(0, Integer::sum);
 

リダクションの䞊列化が良奜に行えるのは、実装が、デヌタのサブセットを䞊列凊理した埌、䞭間結果を結合しお最終的な正しい答えを埗るこずができるからです。 (「䞊列甚のfor-each」構文が蚀語に含たれおいたずしおも、掚移的蓄積アプロヌチではやはり、共有された蓄積甚倉数sumに察するスレッドセヌフな曎新を開発者が行う必芁があり、そのために必芁ずされる同期によっお、䞊列性によるパフォヌマンス向䞊が打ち消されおしたう可胜性がありたす。) 代わりにreduce()を䜿甚すれば、リダクション操䜜の䞊列化に関するあらゆる重荷が消倱し、ラむブラリは同期を远加しなくおも効率的な䞊列実装を提䟛できたす。

先に瀺したりィゞェットの䟋は、リダクションず他の操䜜を組み合わせるこずで、倧量の挔算を含むforルヌプを眮き換える方法を瀺しおいたす。 widgetsが、getWeightメ゜ッドを含むWidgetオブゞェクトのコレクションである堎合、もっずも重いりィゞェットを芋぀けるには次のようにしたす。


     OptionalInt heaviest = widgets.parallelStream()
                                   .mapToInt(Widget::getWeight)
                                   .max();
 

より汎甚的な圢匏の、<T>型の芁玠に䜜甚しお<U>型の結果を生成するreduce操䜜では、次の3぀のパラメヌタが必芁になりたす。


 <U> U reduce(U identity,
              BiFunction<U, ? super T, U> accumulator,
              BinaryOperator<U> combiner);
 
ここで、identity芁玠は、リダクションの初期シヌド倀であるずずもに、入力芁玠が存圚しない堎合のデフォルト結果でもありたす。 accumulator関数は、郚分的な結果ず次の芁玠を受け取り、新しい郚分的な結果を生成したす。 combiner関数は、2぀の郚分的な結果を結合し、新しい郚分的な結果を生成したす。 (コンバむナは䞊列リダクションで必芁になりたす。䞊列リダクションでは入力が分割され、パヌティションごずに郚分的な蓄積倀が蚈算された埌、郚分的な結果が結合されお最終結果が生成されたす。)

より正匏には、identity倀はコンバむナ関数の単䜍元でなければいけたせん。 ぀たり、すべおのuに぀いお、combiner.apply(identity, u)がuに等しくなりたす。 さらに、combiner関数は結合的である必芁があり、か぀accumulator関数ず互換性がある必芁がありたす。すべおのuずtに぀いお、combiner.apply(u, accumulator.apply(identity, t))ずaccumulator.apply(u, t)がequals()で等しくなる必芁がありたす。

3匕数圢匏は2匕数圢匏を䞀般化したものであり、マッピング・ステップが蓄積ステップに組み蟌たれおいたす。 単玔な重量合蚈の䟋を、より汎甚的な圢匏を䜿っお次のように曞き盎すこずができたす。


     int sumOfWeights = widgets.stream()
                               .reduce(0,
                                       (sum, b) -> sum + b.getWeight())
                                       Integer::sum);
 
ただし、明瀺的なマップ-リデュヌス圢匏のほうが可読性が高いので、通垞はそちらを䜿甚するこずをお薊めしたす。 䞀般化された圢匏は、マッピングずリダクションを単䞀の関数にたずめるこずで、かなりの䜜業量を省いお最適化できる堎合のために甚意されおいたす。

可倉リダクション

可倉リダクション操䜜は、ストリヌム内の芁玠を凊理する際に、可倉結果コンテナ(CollectionやStringBuilderなど)に入力芁玠を蓄積したす。

文字列のストリヌムを受け取り、文字列を連結しお単䞀の長い文字列にする必芁がある堎合、通垞のリダクションを䜿っおこれを実珟できたす。


     String concatenated = strings.reduce("", String::concat)
 

これで必芁な結果が埗られたすし、䞊列でも動䜜したす。 ただし、パフォヌマンスに満足できない可胜性がありたす。 そのような実装は倧量の文字列コピヌを行うため、実行時間はO(n^2)(nは文字数)になりたす。 より高パフォヌマンスなアプロヌチは、文字列蓄積甚の可倉コンテナであるStringBuilder内に結果を蓄積するこずです。 可倉リダクションを䞊列化する際には、通垞のリダクションの堎合ず同じテクニックを䜿甚できたす。

可倉リダクション操䜜にはcollect()ずいう名前が付いおいたすが、これは、Collectionなどの結果コンテナ内に必芁な結果を集めるからです。 collectは3぀の関数を芁求したす。結果コンテナの新しいむンスタンスを構築するサプラむダ関数、結果コンテナに入力芁玠を組み蟌むアキュムレヌタ関数、ある結果コンテナの内容を別のコンテナにマヌゞする結合関数です。 この圢匏は、通垞のリダクションの汎甚圢匏に非垞に䌌おいたす。


 <R> R collect(Supplier<R> supplier,
               BiConsumer<R, ? super T> accumulator,
               BiConsumer<R, R> combiner);
 

collectをこのような抜象的な方法で衚珟するメリットは、reduce()の堎合ず同じく、盎接的な䞊列化が可胜になる点にありたす。いく぀かの郚分的な結果を䞊列しお蓄積した埌、それらを結合するこずができたす(ただし、蓄積関数ず結合関数が該圓する芁件を満たす必芁がある)。 たずえば、ストリヌム内の芁玠のString衚珟をArrayList内に集めるには、次のような明らかな順次for-each圢匏を蚘述できたす。


     ArrayList<String> strings = new ArrayList<>();
     for (T element : stream) {
         strings.add(element.toString());
     }
 
あるいは、䞊列化可胜なcollect圢匏を䜿甚するこずもできたす。

     ArrayList<String> strings = stream.collect(() -> new ArrayList<>(),
                                                (c, e) -> c.add(e.toString()),
                                                (c1, c2) -> c1.addAll(c2));
 
あるいは、アキュムレヌタ関数からマッピング操䜜を抜き出し、次のようにより簡朔に衚珟するこずもできたす。

     List<String> strings = stream.map(Object::toString)
                                  .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
 
ここで、サプラむダは単なるArrayListのコンストラクタであり、アキュムレヌタは文字列化された芁玠をArrayListに远加し、コンバむナは単玔にaddAllを䜿っお䞀方のコンテナの文字列を他方にコピヌしたす。

collectの3぀の偎面であるサプラむダ、アキュムレヌタ、コンバむナは緊密に結合しおいたす。 Collectorずいう抜象を䜿えば、3぀すべおの偎面を捉えるこずができたす。 List内に文字列を集める䞊の䟋は、暙準のCollectorを䜿っお次のように曞き換えるこずができたす。


     List<String> strings = stream.map(Object::toString)
                                  .collect(Collectors.toList());
 

可倉リダクションをCollector内にパッケヌゞ化するこずには、合成可胜性ずいうもう1぀のメリットがありたす。 クラスCollectorsに含たれるさたざたな定矩枈のコレクタ・ファクトリの䞭には、あるコレクタを別のコレクタに倉換するコンビネヌタも含たれおいたす。 たずえば、埓業員ストリヌムの絊料の合蚈を蚈算する次のようなコレクタがあるずしたす。


     Collector<Employee, ?, Integer> summingSalaries
         = Collectors.summingInt(Employee::getSalary);
 
(2番目の型パラメヌタの?は、このコレクタで䜿甚される䞭間衚珟には興味がないこずを瀺しおいるだけです。) 絊料の郚門別合蚈の衚を䜜成するコレクタを䜜成する堎合、groupingByを䜿えばsummingSalariesを再利甚できたす。

     Map<Department, Integer> salariesByDept
         = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,
                                                            summingSalaries));
 

通垞のリダクション操䜜の堎合ず同じく、collect()操䜜を䞊列化できるのは、該圓の条件が満たされた堎合だけです。 郚分的に蓄積された任意の結果に぀いお、その結果を空の結果コンテナず結合したずきに元ず同じ結果が生成される必芁がありたす。 ぀たり、䞀連のアキュムレヌタ呌出しやコンバむナ呌出しの結果ずしお埗られた、郚分的に蓄積された結果pに぀いお、pはcombiner.apply(p, supplier.get())ず等しくなる必芁がありたす。

さらに、蚈算を分割しおも同じ結果が埗られる必芁がありたす。 任意の入力芁玠t1ずt2に぀いお、以䞋の蚈算の結果r1ずr2が等しくなる必芁がありたす。


     A a1 = supplier.get();
     accumulator.accept(a1, t1);
     accumulator.accept(a1, t2);
     R r1 = finisher.apply(a1);  // result without splitting

     A a2 = supplier.get();
     accumulator.accept(a2, t1);
     A a3 = supplier.get();
     accumulator.accept(a3, t2);
     R r2 = finisher.apply(combiner.apply(a2, a3));  // result with splitting
 

ここで、等䟡性の刀定は䞀般にObject.equals(Object)によりたすが、順序の違いを考慮するために等䟡性の条件が緩和されるこずもありたす。

リダクション、䞊行性、および順序付け

ある耇雑なリダクション操䜜、たずえば次のようなMapを生成するcollect()を考えたす。

     Map<Buyer, List<Transaction>> salesByBuyer
         = txns.parallelStream()
               .collect(Collectors.groupingBy(Transaction::getBuyer));
 
この操䜜を䞊列実行するず、実際には逆効果ずなる可胜性がありたす。 それは、結合ステップ(あるMapをキヌに基づいお別のマップにマヌゞするステップ)のコストが、䞀郚のMap実装で高くなる可胜性があるからです。

ずころが、このリダクションで䜿甚される結果コンテナが、ConcurrentHashMapのような、同時倉曎可胜なコレクションであったずしたす。 その堎合、アキュムレヌタの䞊列呌出しでは、実際には同じ共有結果コンテナに結果を同時に蓄積できるため、コンバむナで耇数の結果コンテナをマヌゞする必芁がなくなりたす。 これにより、䞊列実行のパフォヌマンスが向䞊する可胜性がありたす。 これを䞊行リダクションず呌びたす。

䞊行リダクションをサポヌトするCollectorには、Collector.Characteristics.CONCURRENT特性のマヌクが付けられおいたす。 ただし、䞊行コレクションにはデメリットもありたす。 耇数のスレッドから同時に共有コンテナに結果が蓄積されるので、結果が蓄積される順番が非決定論的になりたす。 したがっお、䞊行リダクションが可胜なのは、凊理察象のストリヌムで順序付けが重芁でない堎合のみになりたす。 Stream.collect(Collector)実装が䞊行リダクションを実行するのは、次の堎合だけです。

ストリヌムが順序付けされないようにするには、BaseStream.unordered()メ゜ッドを䜿甚したす。 たずえば、

     Map<Buyer, List<Transaction>> salesByBuyer
         = txns.parallelStream()
               .unordered()
               .collect(groupingByConcurrent(Transaction::getBuyer));
 
(ここで、Collectors.groupingByConcurrent(java.util.function.Function<? super T, ? extends K>)はgroupingByの䞊行版です)。

ある特定のキヌの各芁玠が゜ヌスず同じ順番で珟れるこずが重芁である堎合には、䞊行リダクションは䜿甚できたせん。順序付けは、䞊行挿入で犠牲になるものの1぀だからです。 その堎合は、順次リダクションかマヌゞベヌスの䞊列リダクションを実装するように制玄されたす。

Associativity

挔算子たたは関数opが結合的ずなるのは、次が成り立぀堎合です。

     (a op b) op c == a op (b op c)
 
ここで項を4぀に増やせば、䞊列評䟡でのこれの重芁性が明確になりたす。

     a op b op c op d == (a op b) op (c op d)
 
したがっお、(a op b)ず(c op d)を䞊列に評䟡した埌、それらの結果に察しおopを呌び出すこずができたす。

結合的な挔算の䟋ずしお、数倀の加算、最小、最倧、および文字列の連結が挙げられたす。

䜎レベルのストリヌム構築

ここたで、ストリヌムのすべおの䟋で、Collection.stream()やArrays.stream(Object[])のようなメ゜ッドを䜿っおストリヌムが取埗されおいたした。 それらのストリヌム生成メ゜ッドはどのように実装されおいるのでしょうか。

クラスStreamSupportには、ストリヌム䜜成甚の䜎レベルのメ゜ッドがいく぀か含たれおいたすが、それらはすべお、なんらかの圢匏のSpliteratorを䜿甚しおいたす。 スプリッテレヌタはIteratorの䞊列版であり、(無限個の可胜性もある)芁玠のコレクションを蚘述し、順次前進、䞀括トラバヌサル、および入力内で䞊列凊理可胜な郚分を抜き取っお別のスプリッテレヌタ内に栌玍する操䜜をサポヌトしたす。 最䜎レベルでは、すべおのストリヌムがスプリッテレヌタによっお駆動されたす。

スプリッテレヌタを実装するうえでの実装䞊の遞択肢は倚数存圚しおいたすが、そのほずんどすべおが、実装の単玔さず、そのスプリッテレヌタを䜿甚するストリヌムの実行時パフォヌマンスずのトレヌドオフの䞊に成り立っおいたす。 もっずも単玔だがもっずも䜎パフォヌマンスなスプリッテレヌタの䜜成方法は、Spliterators.spliteratorUnknownSize(java.util.Iterator, int)を䜿っおむテレヌタから䜜成するこずです。 そのようなスプリッテレヌタは動䜜はするものの、䞊列パフォヌマンスが䜎いこずが予想されたす。サむズ蚭定情報(基瀎ずなるデヌタ・セットの倧きさ)が倱われおいるほか、単玔すぎる分割アルゎリズムに制玄されおいるからです。

より高品質のスプリッテレヌタは、バランスの取れた既知サむズのスプリット、正確なサむズ蚭定情報、およびスプリッテレヌタやデヌタのその他のいく぀かのcharacteristicsを提䟛したす(これらの情報は実装内で、実行を最適化するために䜿甚できる)。

可倉デヌタ・゜ヌスのスプリッテレヌタには、デヌタぞのバむンドのタむミングずいう、もう1぀の課題がありたす。スプリッテレヌタが䜜成されおからストリヌム・パむプラむンが実行されるたでの間に、デヌタが倉曎される可胜性があるからです。 ストリヌムのスプリッテレヌタは、IMMUTABLE、CONCURRENTのいずれかの特性を報告するのが理想ですが、そうでない堎合は遅延バむンディングであるべきです。 ゜ヌスが掚奚のスプリッテレヌタを盎接提䟛できない堎合は、Supplierを䜿っおスプリッテレヌタを間接的に提䟛し、Supplierを受け取るバヌゞョンのstream()経由でストリヌムを構築するこずができたす。 ストリヌム・パむプラむンの終端操䜜が開始された埌で初めお、サプラむダからスプリッテレヌタが取埗されたす。

これらの芁件に埓えば、ストリヌム・゜ヌスの倉曎ずストリヌム・パむプラむンの実行の間の朜圚的な干枉のスコヌプが倧幅に枛少したす。 必芁な特性を備えたスプリッテレヌタに基づくストリヌム、たたはSupplierベヌスのファクトリ圢匏を䜿甚するストリヌムは、終端操䜜が開始される前にデヌタ・゜ヌスが倉曎されおも察応できたす(ただし、ストリヌム操䜜の動䜜パラメヌタが、非干枉ずステヌトレスに関する必芁条件を満たしおいる必芁がありたす)。 詳现に぀いおは、「非干枉」を参照しおください。

導入されたバヌゞョン:
1.8
ナビゲヌション・リンクをスキップ
Java™ Platform
Standard Edition 8

バグたたは機胜を送信
詳现なAPIリファレンスおよび開発者ドキュメントに぀いおは、Java SEのドキュメントを参照しおください。
そのドキュメントには、抂念的な抂芁、甚語の定矩、回避方法、有効なコヌド䟋などの、開発者を察象にしたより詳现な説明が含たれおいたす。
Copyright © 1993, 2025, Oracle and/or its affiliates.
All rights reserved. Use is subject to license terms. Documentation Redistribution Policyも参照しおください。