Java 8でjava.util.stream.Streamからリストを取得する


443

コレクションを簡単にフィルター処理するために、Java 8ラムダをいじっていました。しかし、同じステートメント内で新しいリストとして結果を取得する簡潔な方法は見つかりませんでした。これまでのところ、私の最も簡潔なアプローチは次のとおりです。

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = new ArrayList<>();
sourceLongList.stream().filter(l -> l > 100).forEach(targetLongList::add);

新しい結果リストを生成せずに停止したため、ネット上の例は私の質問に答えませんでした。より簡潔な方法があるはずです。私がいることを、期待したStreamクラスには、などのメソッドがありtoList()toSet()...

targetLongList3行目で変数を直接割り当てる方法はありますか?


7
sourceLongList後で必要がなくなった場合のCollection.removeIf(…)ためにあります。
Holger

2
これはどう?List<Long> targetLongList = sourceLongList.stream().collect(Collectors.toList());
leeyuiwah

回答:


609

ストリームがシーケンシャルのままである限り、あなたがやっていることは最も簡単な方法かもしれませんforEach

[後の編集:sequential()の呼び出しが必要な理由はforEach(targetLongList::add)、ストリームが並列である場合、現状のコード()が際どいためです。それでも、forEach明確に非決定的であるように、意図した効果は達成されません。シーケンシャルストリームでも、要素処理の順序は保証されません。forEachOrdered正しい順序を保証するために使用する必要があります。Stream APIデザイナーの意図は、以下のように、この状況でコレクターを使用することです。]

代替案は

targetLongList = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(Collectors.toList());

10
さらに:の静的インポートを使用すると、このコードは少し短く、明確で見栄えがよくなると思いますtoList。これは、ファイルのインポートの間に以下を配置することによって行われますstatic import java.util.stream.Collectors.toList;。その後、collect呼び出しはを読み取ります.collect(toList())
Lii

4
Eclipseでは、IDEにメソッドの静的インポートを追加さ​​せることができます。これはCollectors、「設定」 ->「Java」 ->「エディター」 ->「コンテンツ・アシスト」 ->「お気に入り」でクラスを追加することによって行われます。この後toLi、ヒットCtr + Spaceで入力するだけで、IDEにtoList入力して静的インポートを追加できます。
Lii

3
覚えておくべきことの1つは、IntStream他のほぼすべてStreamcollect(Collector)メソッドにはメソッドがなく、最初にIntStream.boxed()それらを通常のものに変換するために呼び出す必要があるということStreamです。もう一度、多分あなただけが欲しいtoArray()
ミュータントボブ

「forEachOrdered」のsequential()forEachまたは「forEachOrdered」を使用する必要がある理由
amarnath harish

1
@amarnathharish forEachは、並列ストリームの操作の実行順序を保証しないためです。JavaDocは、「この操作の動作は明確に非決定的です。並列ストリームパイプラインの場合、この操作は並列処理の利点を犠牲にするため、ストリームの遭遇順序を尊重することを保証しません。」(この引用の最初の文は、実際には順序は保持されますが、実際には順序も連続ストリームに対して保証されないことを意味します。)
Maurice Naftalin

183

更新しました:

別のアプローチは使用することCollectors.toListです:

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toList());

以前のソリューション:

別のアプローチは使用することCollectors.toCollectionです:

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toCollection(ArrayList::new));

60
ただし、これは特定のリスト実装が必要な場合に役立ちます。
orbfish 2015年

1
インターフェースに対してコーディングすることをお勧めしますが、具体的な実装に対してコーディングする必要がある明確なケース(それらの1つはGWTです)があります(すべてのList実装をコンパイルしてJavaScriptとして配信したくない場合)。
Eduard Korenschi、2015

3
このメソッドのもう1つのCollectors::toList利点は、javadocです。「返されるリストのタイプ、変更可能性、直列化可能性、またはスレッドセーフ性は保証されません。返されるリストをさらに制御する必要がある場合は、を使用してくださいtoCollection(Supplier)。」
Charles Wood

12

コレクターを返すutilメソッドを使用しArrayListたいのですが、それが必要な場合に使用します。

私が使用するソリューションCollectors.toCollection(ArrayList::new)は、そのような一般的な操作には少しうるさすぎると思います。

例:

ArrayList<Long> result = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(toArrayList());

public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

この回答では、カスタムコレクターの作成と使用がいかに簡単かを示したいと思います。


結果をList <Long>として宣言する場合、このutilメソッドを使用する必要はありません。Collectors.toListが行います。また、インターフェイスの代わりに特定のクラスを使用すると、コードのにおいがします。
Lluis Martinez

1
@LluisMartinez:"Collectors.toListが行います。" :いいえ、多くの状況ではありません。toListたとえば、後でプログラムでリストを変更する場合などに使用することはお勧めできません。toListドキュメントは、この言う:「タイプ、可変性、直列可能、またはリストのスレッド安全返さ上の保証はありません。返されたリストをより細かく制御が必要な場合は、使用toCollection。」。私の答えは、一般的なケースでそれを行うことをより便利にする方法を示しています。
Lii 2017

特にArrayListを作成したい場合は問題ありません。
Lluis Martinez

5

プリミティブの配列がある場合は、Eclipseコレクションで利用可能なプリミティブコレクションを使用できます。

LongList sourceLongList = LongLists.mutable.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
LongList targetLongList = sourceLongList.select(l -> l > 100);

sourceLongListをList次のように変更できない場合:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = 
    ListAdapter.adapt(sourceLongList).select(l -> l > 100, new ArrayList<>());

使用したい場合LongStream

long[] sourceLongs = new long[]{1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L};
LongList targetList = 
    LongStream.of(sourceLongs)
    .filter(l -> l > 100)
    .collect(LongArrayList::new, LongArrayList::add, LongArrayList::addAll);

注:私はEclipseコレクションの寄稿者です。


4

もう少し効率的な方法(ソースリストの作成とフィルターによる自動アンボックス化を避けます):

List<Long> targetLongList = LongStream.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L)
    .filter(l -> l > 100)
    .boxed()
    .collect(Collectors.toList());


1

サードパーティのライブラリを使用してもかまわない場合は、AOLのcyclops-react lib(私が寄稿者である開示)に、Listを含むすべてのJDKコレクションタイプの拡張機能があります。ListXインターフェースはjava.util.Listを拡張し、フィルターを含む多数の便利な演算子を追加します。

あなたは単に書くことができます

ListX<Long> sourceLongList = ListX.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
ListX<Long> targetLongList = sourceLongList.filter(l -> l > 100);

ListXは、既存のリストから作成することもできます(ListX.fromIterableを使用)。


1

LongStreamクラスによって提供されるcollectメソッドの別のバリアントがあり、同様にIntStreamおよびDoubleStreamクラスによっても提供されます。

<R> R collect(Supplier<R> supplier,
              ObjLongConsumer<R> accumulator,
              BiConsumer<R,R> combiner)

このストリームの要素に対して変更可能な縮小操作を実行します。変更可能な削減とは、削減された値がArrayListなどの変更可能な結果コンテナーであり、要素は、結果を置き換えるのではなく、結果の状態を更新することによって組み込まれるものです。これにより、次と同等の結果が生成されます。

R result = supplier.get();
  for (long element : this stream)
       accumulator.accept(result, element);
  return result;

reduce(long、LongBinaryOperator)と同様に、追加の同期を必要とせずに収集操作を並列化できます。これは端末操作です。

そして、この収集方法であなたの質問に答えるのは以下の通りです:

    LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, (list, value) -> list.add(value)
    , (list1, list2) -> list1.addAll(list2));

以下は、非常にスマートなメソッド参照バリアントですが、理解するのが難しいものもあります。

     LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, List::add , List::addAll);

HashSetのバリアントは次のとおりです。

     LongStream.of(1L, 2L, 3L, 3).filter(i -> i > 2)
     .collect(HashSet::new, HashSet::add, HashSet::addAll);

同様に、LinkedListバリアントは次のようになります。

     LongStream.of(1L, 2L, 3L, 3L)
     .filter(i -> i > 2)
     .collect(LinkedList::new, LinkedList::add, LinkedList::addAll);

0

(私のような)誰かがプリミティブ型ではなくオブジェクトを処理する方法を探している場合は、 mapToObj()

String ss = "An alternative way is to insert the following VM option before "
        + "the -vmargs option in the Eclipse shortcut properties(edit the "
        + "field Target inside the Shortcut tab):";

List<Character> ll = ss
                        .chars()
                        .mapToObj(c -> new Character((char) c))
                        .collect(Collectors.toList());

System.out.println("List type: " + ll.getClass());
System.out.println("Elem type: " + ll.get(0).getClass());
ll.stream().limit(50).forEach(System.out::print);

プリント:

List type: class java.util.ArrayList
Elem type: class java.lang.Character
An alternative way is to insert the following VM o

0
String joined = 
                Stream.of(isRead?"read":"", isFlagged?"flagged":"", isActionRequired?"action":"", isHide?"hide":"")
                      .filter(s -> s != null && !s.isEmpty())
                      .collect(Collectors.joining(","));

0

ここにAbacusUtilによるコードがあります

LongStream.of(1, 10, 50, 80, 100, 120, 133, 333).filter(e -> e > 100).toList();

情報開示:私はAbacusUtilの開発者です。


LongStreamクラスにtoListメソッドがありません。このコードを実行してもらえますか?
Vaneet Kataria

@VaneetKataria try com.landawn.abacus.util.stream.LongStreamまたはLongStreamExAbacusUtil
user_3380739

0

以下のようにコードを書き直すことができます:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = sourceLongList.stream().filter(l -> l > 100).collect(Collectors.toList());

ご入力いただきありがとうございます。ただし、変更点と質問との関連性について説明してください。
B--rian

ここではまず、ArrayListを変換して、フィルターを使用してそれらをスチーム処理し、必要なデータをフィルターで除外しました。最後に、Java 8ストリームのcollectメソッドを使用して、targetLongListと呼ばれる新しいリストでデータを収集しました。
Pratik Pawar

0

変更可能なリストを収集するには:

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toList());

不変リストを収集するには:

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toUnmodifiableList());

JavaDoccollectからの説明:

Collectorを使用して、このストリームの要素に対して変更可能な縮小操作を実行します。Collectorは、collect(Supplier、BiConsumer、BiConsumer)の引数として使用される関数をカプセル化し、収集戦略の再利用や、複数レベルのグループ化やパーティション化などの収集操作の構成を可能にします。ストリームが並列で、コレクターが並行であり、ストリームが順序付けされていないか、コレクターが順序付けられていない場合、並行削減が実行されます(並行削減の詳細については、コレクターを参照してください)。

これは端末操作です。

並列で実行すると、複数の中間結果がインスタンス化され、データが入力され、マージされて、可変データ構造の分離が維持されます。したがって、スレッドセーフでないデータ構造(ArrayListなど)と並行して実行された場合でも、並列縮小のために追加の同期を行う必要はありません。


-3

使用しない場合はparallel()これでうまくいきます

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);

List<Long> targetLongList =  new ArrayList<Long>();

sourceLongList.stream().peek(i->targetLongList.add(i)).collect(Collectors.toList());

各項目でpeek()フックが呼び出されるように、collect()がストリームを駆動するためだけに使用されるのが好きではありません。端末操作の結果は破棄されます。
Daniel K.

呼び出しcollectて戻り値を保存しないのは非常に奇妙です。その場合、forEach代わりに使用できます。しかし、それはまだ不十分な解決策です。
Lii

1
このようにpeek()を使用することはアンチパターンです。
Grzegorz Piwowarek 2017

Stream Java docsのとおり、peekメソッドはデバッグ目的でのみ使用する必要があります。デバッグ以外の処理には使用しないでください。
Vaneet Kataria
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.