Java 8'de java.util.stream.Stream öğesinden bir Liste alma


443

Koleksiyonları kolayca filtrelemek için Java 8 lambdas ile oynuyordum. Ancak sonucu aynı ifadede yeni bir liste olarak almanın kısa bir yolunu bulamadım. İşte şimdiye kadarki en özlü yaklaşımım:

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);

İnternetteki örnekler soruma cevap vermedi çünkü yeni bir sonuç listesi oluşturmadan duruyorlar. Daha özlü bir yol olmalı. Bunu, umuyordum Streamsınıf olarak yöntemleri vardır toList(), toSet()...

Değişkenlerin targetLongListdoğrudan üçüncü satır tarafından atanabilmesinin bir yolu var mı ?


7
Daha sourceLongListsonra ihtiyacınız yoksa Collection.removeIf(…)kolaylık sağlayın.
Holger

2
Buna ne dersin? List<Long> targetLongList = sourceLongList.stream().collect(Collectors.toList());
leeyuiwah

Yanıtlar:


609

Yaptığınız şey, akışınızın sıralı kalması şartıyla en basit yol olabilir; aksi takdirde daha önce sequential () öğesine bir çağrı yapmanız gerekir forEach.

[daha sonraki düzenleme: ardışık () çağrısının gerekli olmasının nedeni forEach(targetLongList::add), akış paralel olsaydı, durduğu kodun ( ) açık olması. O zaman bile, açıkça belirleyici olmadığı gibi amaçlanan etkiyi elde etmeyecektir forEach- sıralı bir akışta bile eleman işleme sırası garanti edilmez. forEachOrderedDoğru siparişi sağlamak için kullanmanız gerekir. Stream API tasarımcılarının amacı bu durumda toplayıcıyı aşağıdaki gibi kullanmanızdır.]

Bir alternatif

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

10
Ek: Ben statik bir ithalat kullanırsanız bu kodlar biraz daha kısa, daha net ve güzel olur düşünüyorum toList. Bu dosyanın ithalat arasında aşağıdaki yerleştirerek yapılır: static import java.util.stream.Collectors.toList;. Sonra toplama çağrısı sadece okur .collect(toList()).
Lii

4
Eclipse'de IDE'nin yöntemler için statik bir içe aktarma eklemesi mümkündür. Bu, Collectorssınıfı Tercihler -> Java -> Editör -> İçerik Yardımı -> Sık Kullanılanlar'a ekleyerek yapılır . Bundan sonra , IDE'yi doldurmak ve statik içe aktarmayı eklemek için sadece toLihit Ctr + Space'e yazmanız gerekir toList.
Lii

3
Akılda tutulması gereken bir şey, IntStreamve diğer neredeyse-ama-oldukça-olmayanlar yönteme Streamsahip değildir collect(Collector)ve IntStream.boxed()bunları düzenli bir Streamilk haline dönüştürmek için çağırmanız gerekecektir . Sonra tekrar, belki sadece istersiniz toArray().
Mutant Bob

neden sequential()önce kullanmalıyız forEachya da 'forEachOrdered` kullanmalıyız
amarnath harish

1
@ amarnathharish Çünkü forEach paralel bir akış için işlem yürütme sırasını garanti etmez. JavaDoc, "Bu işlemin davranışı açıkça belirsizdir. Paralel akışlı boru hatları için, bu işlem, akımın karşılaşma sırasına saygı gösterilmesini garanti etmez, çünkü bu, paralelliğin yararını feda eder." (Bu alıntıdaki ilk cümle aslında uygulamada korunmasına rağmen sıralı akışlar için düzenin garanti edilmediği anlamına gelir.)
Maurice Naftalin

183

Güncellenmiş:

Başka bir yaklaşım kullanmak Collectors.toList:

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

Önceki Çözüm:

Başka bir yaklaşım kullanmak Collectors.toCollection:

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

60
Ancak, belirli bir Liste uygulaması istiyorsanız bu yararlıdır.
orbfish

1
Arayüzlere kodlama yapılması tavsiye edilen arıya rağmen, somut uygulamalara karşı kodlamak zorunda olduğunuzda (tüm Liste uygulamalarının javascript olarak derlenmesini ve teslim edilmesini istemiyorsanız) açık durumlar vardır (bunlardan biri GWT'dir).
Eduard Korenschi

3
Bu yöntem için başka bir profesyonel, Collectors::toListjavadoc'tan: "Döndürülen Listenin türü, değiştirilebilirliği, serileştirilebilirliği veya iş parçacığı güvenliği konusunda hiçbir garanti yoktur; Döndürülen Liste üzerinde daha fazla denetim gerekiyorsa, kullanın toCollection(Supplier)."
Charles Wood

12

İstediğim bu ArrayListolduğunda bir toplayıcı döndüren bir util yöntemi kullanmayı seviyorum .

Bence çözüm Collectors.toCollection(ArrayList::new)böyle yaygın bir işlem için biraz fazla gürültülü.

Misal:

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

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

Bu cevapla birlikte, genellikle çok yararlı olan özel koleksiyonerler oluşturmanın ve kullanmanın ne kadar basit olduğunu göstermek istiyorum.


Sonucu Liste <Uzun> olarak bildirirseniz, bu util yöntemini kullanmanız gerekmez. Collectors.toList yapacak. Ayrıca, arayüzler yerine belirli sınıfların kullanılması bir kod kokusudur.
Lluis Martinez

1
@LluisMartinez: "Collectors.toList yapacak." : Hayır, pek çok durumda değil. toListÖrneğin, listeyi daha sonra programda değiştirmek isterseniz kullanmak iyi bir fikir değildir . toListDokümantasyon bu diyor ki: "tipi, mutability, serializability veya Listesinin parçacığı güvenliği döndü üzerinde hiçbir garantisi yoktur; iade Listesi'nde üzerinde daha fazla kontrol gerekiyorsa, kullanım toCollection." . Cevabım, bunu yaygın bir durumda yapmayı daha kolay hale getirmenin bir yolunu gösteriyor.
Lii

Özellikle bir ArrayList oluşturmak istiyorsanız sorun değil.
Lluis Martinez

5

Bir dizi temel öğe varsa, Eclipse Koleksiyonlarında bulunan ilkel koleksiyonları kullanabilirsiniz .

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

SourceLongList öğesini değiştiremiyorsanız 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<>());

Kullanmak istiyorsanız 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);

Not: Eclipse Koleksiyonlarına katkıda bulunuyorum.


4

Biraz daha etkili bir yol (kaynak listesini oluşturmaktan ve filtrenin otomatik olarak açılmasını önlemek):

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


1

Üçüncü taraf kitaplıklarını kullanmanın bir sakıncası yoksa, AOL'nin cyclops-tepki tepkisi (açıklama ben katkıda bulunuyorum) Liste de dahil olmak üzere tüm JDK Koleksiyonu türleri için uzantılara sahiptir . ListX arabirimi java.util.List öğesini genişletir ve filtre de dahil olmak üzere çok sayıda yararlı işleç ekler.

Yazabilirsiniz.

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

ListX ayrıca mevcut bir Listeden de oluşturulabilir (ListX.fromIterable aracılığıyla)


1

LongStream sınıfı ve benzer şekilde IntStream ve DoubleStream sınıfları tarafından sağlanan başka bir toplama yöntemi varyantı vardır.

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

Bu akışın elemanları üzerinde değiştirilebilir bir azaltma işlemi gerçekleştirir. Değişken bir indirgeme, indirgenmiş değerin bir ArrayList gibi değişebilir bir sonuç kabı olduğu ve öğelerin, sonucu değiştirmek yerine sonucun durumunu güncelleyerek dahil edildiği bir indirgeme değeridir. Bu, aşağıdakine eşdeğer bir sonuç üretir:

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

Azaltma (uzun, LongBinaryOperator) gibi, toplama işlemleri ek senkronizasyon gerektirmeden paralelleştirilebilir. Bu bir terminal işlemidir.

Ve bu toplama yöntemi ile sorunuza cevap aşağıdaki gibidir:

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

Aşağıda oldukça akıllı ama anlaşılması zor olan yöntem referans varyantı:

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

HashSet varyantı aşağıdadır:

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

Benzer şekilde LinkedList varyantı şöyle:

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

0

Birinin (benim gibi) ilkel tipler yerine Nesnelerle başa çıkma yolları araması durumunda, 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);

baskılar:

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

İşte AbacusUtil kodu

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

Açıklama : AbacusUtil'in geliştiricisiyim.


LongStream sınıfında herhangi bir toList yöntemi bulamıyorum. Bu kodu çalıştırabilir misiniz?
Vaneet Kataria

@VaneetKataria denemek com.landawn.abacus.util.stream.LongStreamya LongStreamExda AbacusUtil içinde
user_3380739

0

Kodu aşağıdaki gibi yeniden yazabilirsiniz:

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());

Girdiniz için teşekkürler. Bununla birlikte, lütfen neyi değiştirdiğinizi ve bunun soru ile ne kadar ilişkili olduğunu açıklayın.
B - rian

Burada öncelikle ArrayList'imi filtre I kullanarak gerekli verileri filtrelemek için dönüştürdüm. Son olarak targetLongList olarak adlandırılan yeni listede veri toplamak için java 8 akışının toplama yöntemini kullandım.
Pratik Pawar

0

Değişken bir listede toplamak için:

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

Değişmez bir listede toplamak için:

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

Açıklanması collectdan JavaDoc'u :

Bir Toplayıcı kullanarak bu akışın elemanları üzerinde değiştirilebilir bir azaltma işlemi gerçekleştirir. Bir Toplayıcı, toplama stratejilerinin yeniden kullanılmasına ve çok düzeyli gruplama veya bölümleme gibi toplama işlemlerinin kompozisyonuna izin vererek, toplama argümanları olarak kullanılan işlevleri (Tedarikçi, BiConsumer, BiConsumer) içerir. Akış paralelse ve Toplayıcı eşzamanlıysa ve akış sıralanmamışsa veya toplayıcı sıralanmamışsa, eşzamanlı bir azalma gerçekleştirilir (eşzamanlı azaltmayla ilgili ayrıntılar için Toplayıcı'ya bakın).

Bu bir terminal işlemidir.

Paralel olarak yürütüldüğünde, değiştirilebilir ara yapıların izolasyonunu sürdürmek için çoklu ara sonuçlar başlatılabilir, doldurulabilir ve birleştirilebilir. Bu nedenle, iş parçacığı için güvenli olmayan veri yapılarına (ArrayList gibi) paralel olarak yürütüldüğünde bile, paralel azaltma için ek senkronizasyon gerekmez.


-3

Eğer kullanmazsanız parallel()bu işe yarayacak

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());

Collect () sadece her öğe üzerinde peek () kanca çağrılır böylece akışı sürücü için kullanılmasını sevmiyorum. Terminal işleminin sonucu atılır.
Daniel K.

Aramak collectve sonra dönüş değerini kaydetmemek çok garip . Bu durumda kullanabilirsiniz forEach. Ama bu hala kötü bir çözüm.
Lii

1
Peek () 'i bu şekilde kullanmak bir antipatterndir.
Grzegorz Piwowarek

Akışa göre Java dokümanları peek yöntemi sadece hata ayıklama amacıyla kullanılmalıdır. Hata ayıklama dışında herhangi bir işlem için kullanılmamalıdır.
Vaneet Kataria
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.