Java 8 StreamsFlatMapメソッドの例


85

私は次のJava update、すなわちをチェックしています:Java 8 or JDK 8。はい、私は焦ります。新しいものがたくさんありますが、理解できないことがあります。いくつかの簡単なコードです。

final Stream<Integer>stream = Stream.of(1,2,3,4,5,6,7,8,9,10);
stream.flatMap();

javadocsは

public <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)

このストリームの各要素を、提供されたマッピング関数を各要素に適用することによって生成されたマップされたストリームのコンテンツで置き換えた結果で構成されるストリームを返します。マップされた各ストリームは、そのコンテンツがこのストリームに配置された後に閉じられます。(マップされたストリームがnullの場合、代わりに空のストリームが使用されます。)これは中間操作です。

誰かがflatMap、以前のJavaバージョンでそれJava[6,7]をコーディングする方法、およびを使用して同じルーチンをコーディングする方法について、いくつかの簡単な実際の例を作成していただければ幸いですJava 8


2
インターネット上でflatMapを使用した例は約100万あります(少なくともScalaの場合、基本的に同じです:))、検索してみましたか?まずは、brunton-spall.co.uk
Peter Svensson

3
私はScalaを理解していません私はscalaを使ったことがありません
chiperortiz 2014年

つまり、flatMapは、ScalaだけでなくJavaにも存在する一般的な概念です。
Peter Svensson 2014年

わかりました、私はそれについてもっと読むでしょうありがとう男。
chiperortiz 2014年

10
JavaのflatMapは同じアイデアですが、ストリームではまったく異なって見えます。人々にScalaを指さないでください!
マンジュウダイ2015

回答:


158

それはしても意味がありません、ストリームのように、すでにフラットだあなたは、あなたの質問に示されました。 flatMapStream<Integer>

ただし、それがあればStream<List<Integer>>、それは理にかなっており、これを行うことができます。

Stream<List<Integer>> integerListStream = Stream.of(
    Arrays.asList(1, 2), 
    Arrays.asList(3, 4), 
    Arrays.asList(5)
);

Stream<Integer> integerStream = integerListStream .flatMap(Collection::stream);
integerStream.forEach(System.out::println);

どちらが印刷されますか:

1
2
3
4
5

このJava8より前のバージョンを実行するには、ループが必要です。

List<List<Integer>> integerLists = Arrays.asList(
    Arrays.asList(1, 2), 
    Arrays.asList(3, 4), 
    Arrays.asList(5)
)

List<Integer> flattened = new ArrayList<>();

for (List<Integer> integerList : integerLists) {
    flattened.addAll(integerList);
}

for (Integer i : flattened) {
    System.out.println(i);
}

113

作り上げた例

次のシーケンスを作成するとします:1、2、2、3、3、3、4、4、4、4など(つまり、1x1、2x2、3x3など)

次のflatMapようになります。

IntStream sequence = IntStream.rangeClosed(1, 4)
                          .flatMap(i -> IntStream.iterate(i, identity()).limit(i));
sequence.forEach(System.out::println);

どこ:

  • IntStream.rangeClosed(1, 4)int1から4までのストリームを作成します
  • IntStream.iterate(i, identity()).limit(i)iの長さiのストリームを作成しますint-したがって、それに適用するi = 4とストリームが作成されます:4, 4, 4, 4
  • flatMap ストリームを「フラット化」し、元のストリームに「連結」します

Java <8の場合、2つのネストされたループが必要になります。

List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 4; i++) {
    for (int j = 0; j < i; j++) {
        list.add(i);
    }
}

実例

List<TimeSeries>それぞれTimeSeriesが本質的にであるところがあるとしましょうMap<LocalDate, Double>。時系列の少なくとも1つに値があるすべての日付のリストを取得したいと思います。flatMap救助へ:

list.stream().parallel()
    .flatMap(ts -> ts.dates().stream()) // for each TS, stream dates and flatmap
    .distinct()                         // remove duplicates
    .sorted()                           // sort ascending
    .collect(toList());

読み取り可能であるだけでなく、突然100kの要素を処理する必要がある場合は、追加parallel()するだけで、並行コードを記述せずにパフォーマンスが向上します。


14
どちらの例も、受け入れられた回答よりもはるかに優れています。
セバスチャングラフ2014年

コンパイラがidentity()未定義
Nirmal 2015

2
@ user3320018静的インポートする必要がありますFunction.identity
assylias 2015

@assylias java.util.function.Functionをインポートしようとしましたが、機能しませんでした。Java8を初めて使用します。これは、Java 8固有である場合とそうでない場合がありますが、そのエラーを削除する方法を正確に教えてください。
Nirmal 2015

4
import static java.util.function.Function.identity;
assylias 2015

18

フレーズのリストからASCでソートされた一意の単語を抽出します。

List<String> phrases = Arrays.asList(
        "sporadic perjury",
        "confounded skimming",
        "incumbent jailer",
        "confounded jailer");

List<String> uniqueWords = phrases
        .stream()
        .flatMap(phrase -> Stream.of(phrase.split("\\s+")))
        .distinct()
        .sorted()
        .collect(Collectors.toList());
System.out.println("Unique words: " + uniqueWords);

...および出力:

Unique words: [confounded, incumbent, jailer, perjury, skimming, sporadic]

11

巻き戻しリストを退屈だと思うのは私だけですか?;-)

オブジェクトを試してみましょう。ちなみに実例。

与えられた:反復タスクを表すオブジェクト。重要なタスクフィールドについて:リマインダーが鳴り始めstart、毎回繰り返されrepeatPeriod repeatUnit(たとえば、5時間)repeatCount、合計でリマインダーがあります(開始を含む)。

目標:タスクリマインダーの呼び出しごとに1つずつ、タスクコピーのリストを作成します。

List<Task> tasks =
            Arrays.asList(
                    new Task(
                            false,//completed sign
                            "My important task",//task name (text)
                            LocalDateTime.now().plus(2, ChronoUnit.DAYS),//first reminder(start)
                            true,//is task repetitive?
                            1,//reminder interval
                            ChronoUnit.DAYS,//interval unit
                            5//total number of reminders
                    )
            );

tasks.stream().flatMap(
        x -> LongStream.iterate(
                x.getStart().toEpochSecond(ZoneOffset.UTC),
                p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds())
        ).limit(x.getRepeatCount()).boxed()
        .map( y -> new Task(x,LocalDateTime.ofEpochSecond(y,0,ZoneOffset.UTC)))
).forEach(System.out::println);

出力:

Task{completed=false, text='My important task', start=2014-10-01T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}
Task{completed=false, text='My important task', start=2014-10-02T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}
Task{completed=false, text='My important task', start=2014-10-03T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}
Task{completed=false, text='My important task', start=2014-10-04T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}
Task{completed=false, text='My important task', start=2014-10-05T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}

PS:誰かがもっと簡単な解決策を提案してくれたら、私はプロではありません。

更新: @RBzが詳細な説明を求めたので、ここにあります。基本的に、flatMapは、別のストリーム内のストリームからのすべての要素を出力ストリームに配置します。ここにはたくさんのストリームがあります:)。したがって、初期ストリームのタスクごとに、ラムダ式x -> LongStream.iterate...はタスクの開始時点を表す長い値のストリームを作成します。このストリームはx.getRepeatCount()インスタンスに限定されています。x.getStart().toEpochSecond(ZoneOffset.UTC)値はから始まり、次の各値はラムダを使用して計算されp -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds()ます。boxed()各long値を持つストリームをLongラッパーインスタンスとして返します。次に、そのストリーム内の各Longは、繰り返しではなく、正確な実行時間を含む新しいタスクインスタンスにマップされます。このサンプルには、入力リストにタスクが1つだけ含まれています。しかし、あなたが千を持っていると想像してください。これで、タスクオブジェクトの1000ストリームのストリームが作成されます。そして何flatMapここでは、すべてのストリームのすべてのタスクを同じ出力ストリームに配置しています。私が理解しているのはそれだけです。ご質問ありがとうございます!


8
Am I the only one who finds unwinding lists boring?+1
whitfin 2014

3
この例を理解するのは本当に難しいと思います。:(
RBz 2016年

@RBzストリーム操作は、特に複数の操作が関係している場合、理解しにくい場合があります。しかし、それは実践の問題です。あなたができる最善のことは、サンプルからすべての不明瞭な単語をグーグルで検索し、それを自分で使用しようとすることです。実際、通常の命令型のサンプルは、はるかに理解しやすい(場合によっては高速である)でしょう。したがって、本当にストリームを使用する必要があるかどうかを考えてみてください。
Aleksandr Kravets 2016年

返信マンをありがとう。しかし、私はストリームの概念にはまったく問題がありません。ここで問題が発生しているのは、特定の例です。私はTimeapiがあまり得意ではありませんでしたが、それを読んでも、ここで何が起こっているのかを理解するのに役立ちません。私は素朴なのかもしれませんが、あなたの答えについてもう少し説明があれば素晴らしいでしょう。それはあなたの例を理解するのに本当に役立ちます。私は好奇心でそれに閉じ込められているだけです!:)
RBz 2016年

すばらしい例...最初は少し理解しにくいですが、IDEで実行すると...とても強力な代替手段です!! どうもありがとう !
クリスティアーノ

2

このメソッドは、引数として1つの関数を取ります。この関数は、入力引数として1つのパラメーターTを受け入れ、戻り値としてパラメーターRの1つのストリームを返します。この関数をこのストリームの各要素に適用すると、新しい値のストリームが生成されます。次に、各要素によって生成されたこれらの新しいストリームのすべての要素が新しいストリームにコピーされます。これがこのメソッドの戻り値になります。

http://codedestine.com/java-8-stream-flatmap-method/


2

非常に簡単な例:最初または最後に関係なく、フルネームのリストを分割して名前のリストを取得します

 List<String> fullNames = Arrays.asList("Barry Allen", "Bruce Wayne", "Clark Kent");

 fullNames.stream()
            .flatMap(fullName -> Pattern.compile(" ").splitAsStream(fullName))
            .forEach(System.out::println);

これは印刷されます:

Barry
Allen
Bruce
Wayne
Clark
Kent

1

これを考えると:

  public class SalesTerritory
    {
        private String territoryName;
        private Set<String> geographicExtents;

        public SalesTerritory( String territoryName, Set<String> zipCodes )
        {
            this.territoryName = territoryName;
            this.geographicExtents = zipCodes;
        }

        public String getTerritoryName()
        {
            return territoryName;
        }

        public void setTerritoryName( String territoryName )
        {
            this.territoryName = territoryName;
        }

        public Set<String> getGeographicExtents()
        {
            return geographicExtents != null ? Collections.unmodifiableSet( geographicExtents ) : Collections.emptySet();
        }

        public void setGeographicExtents( Set<String> geographicExtents )
        {
            this.geographicExtents = new HashSet<>( geographicExtents );
        }

        @Override
        public int hashCode()
        {
            int hash = 7;
            hash = 53 * hash + Objects.hashCode( this.territoryName );
            return hash;
        }

        @Override
        public boolean equals( Object obj )
        {
            if ( this == obj ) {
                return true;
            }
            if ( obj == null ) {
                return false;
            }
            if ( getClass() != obj.getClass() ) {
                return false;
            }
            final SalesTerritory other = (SalesTerritory) obj;
            if ( !Objects.equals( this.territoryName, other.territoryName ) ) {
                return false;
            }
            return true;
        }

        @Override
        public String toString()
        {
            return "SalesTerritory{" + "territoryName=" + territoryName + ", geographicExtents=" + geographicExtents + '}';
        }

    }

この:

public class SalesTerritories
{
    private static final Set<SalesTerritory> territories
        = new HashSet<>(
            Arrays.asList(
                new SalesTerritory[]{
                    new SalesTerritory( "North-East, USA",
                                        new HashSet<>( Arrays.asList( new String[]{ "Maine", "New Hampshire", "Vermont",
                                                                                    "Rhode Island", "Massachusetts", "Connecticut",
                                                                                    "New York", "New Jersey", "Delaware", "Maryland",
                                                                                    "Eastern Pennsylvania", "District of Columbia" } ) ) ),
                    new SalesTerritory( "Appalachia, USA",
                                        new HashSet<>( Arrays.asList( new String[]{ "West-Virgina", "Kentucky",
                                                                                    "Western Pennsylvania" } ) ) ),
                    new SalesTerritory( "South-East, USA",
                                        new HashSet<>( Arrays.asList( new String[]{ "Virginia", "North Carolina", "South Carolina",
                                                                                    "Georgia", "Florida", "Alabama", "Tennessee",
                                                                                    "Mississippi", "Arkansas", "Louisiana" } ) ) ),
                    new SalesTerritory( "Mid-West, USA",
                                        new HashSet<>( Arrays.asList( new String[]{ "Ohio", "Michigan", "Wisconsin", "Minnesota",
                                                                                    "Iowa", "Missouri", "Illinois", "Indiana" } ) ) ),
                    new SalesTerritory( "Great Plains, USA",
                                        new HashSet<>( Arrays.asList( new String[]{ "Oklahoma", "Kansas", "Nebraska",
                                                                                    "South Dakota", "North Dakota",
                                                                                    "Eastern Montana",
                                                                                    "Wyoming", "Colorada" } ) ) ),
                    new SalesTerritory( "Rocky Mountain, USA",
                                        new HashSet<>( Arrays.asList( new String[]{ "Western Montana", "Idaho", "Utah", "Nevada" } ) ) ),
                    new SalesTerritory( "South-West, USA",
                                        new HashSet<>( Arrays.asList( new String[]{ "Arizona", "New Mexico", "Texas" } ) ) ),
                    new SalesTerritory( "Pacific North-West, USA",
                                        new HashSet<>( Arrays.asList( new String[]{ "Washington", "Oregon", "Alaska" } ) ) ),
                    new SalesTerritory( "Pacific South-West, USA",
                                        new HashSet<>( Arrays.asList( new String[]{ "California", "Hawaii" } ) ) )
                }
            )
        );

    public static Set<SalesTerritory> getAllTerritories()
    {
        return Collections.unmodifiableSet( territories );
    }

    private SalesTerritories()
    {
    }

}

次に、これを行うことができます。

System.out.println();
System.out
    .println( "We can use 'flatMap' in combination with the 'AbstractMap.SimpleEntry' class to flatten a hierarchical data-structure to a set of Key/Value pairs..." );
SalesTerritories.getAllTerritories()
    .stream()
    .flatMap( t -> t.getGeographicExtents()
        .stream()
        .map( ge -> new SimpleEntry<>( t.getTerritoryName(), ge ) )
    )
    .map( e -> String.format( "%-30s : %s",
                              e.getKey(),
                              e.getValue() ) )
    .forEach( System.out::println );
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.