2013-02-12 9 views
341

Ich spielte mit Java 8 Lambdas, um Sammlungen einfach zu filtern. Aber ich habe keine übersichtliche Methode gefunden, das Ergebnis als neue Liste innerhalb derselben Anweisung abzurufen. Hier ist meine knappste Ansatz so weit:Abrufen einer Liste aus einem java.util.stream.Stream in 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); 

Beispiele im Netz antwortete nicht auf meine Frage, weil sie ohne Erzeugen eines neuen Ergebnisliste stoppen. Es muss einen prägnanteren Weg geben. Ich habe erwartet, dass, dass die Stream Klasse verfügt über Methoden wie toList(), toSet(), ...

Gibt es eine Möglichkeit, dass die Variablen targetLongList können direkt von der dritten Zeile zugeordnet werden werden?

+6

Falls Sie die 'sourceLongList' danach nicht benötigen, gibt es ['Collection.removeIf (...)'] (http://docs.oracle.com/javase/8/docs/api/java/util/Collection .html # removeIf-java.util.function.Predicate-). – Holger

Antwort

479

Was Sie tun, ist möglicherweise der einfachste Weg, sofern Ihr Stream sequentiell bleibt — sonst müssen Sie einen Aufruf von sequential() vor forEach.

[später bearbeiten: Der Grund für den Aufruf von sequential() ist notwendig, dass der Code, wie er steht (forEach(targetLongList::add)) wäre rassig, wenn der Strom parallel war. Selbst dann wird es den beabsichtigten Effekt nicht erreichen, da forEach explizit nicht deterministisch — ist, selbst in einem sequentiellen Strom ist die Reihenfolge der Elementverarbeitung nicht garantiert. Sie müssen forEachOrdered verwenden, um die korrekte Bestellung sicherzustellen. Die Absicht des Stream-API-Designers ist, dass man Sammler in dieser Situation, wie weiter unten]

Eine Alternative ist

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

Zusatz: Ich denke, dass diese Codes ein wenig kürzer, klarer und hübscher werden, wenn Sie einen statischen Import von 'toList' verwenden. Dies geschieht, indem Sie Folgendes zu den Importen der Datei hinzufügen: 'static import java.util.stream.Collectors.toList;'. Dann liest der Collect-Aufruf nur '.collect (toList())'. – Lii

+2

In Eclipse ist es möglich, dass die IDE einen statischen Import für Methoden hinzufügt. Dies geschieht durch Hinzufügen der 'Collectors'-Klasse in * Preferences * -> * Java * -> * Editor * -> * Content Assist * -> * Favorites *. Danach müssen Sie nur noch 'toLi' eingeben, indem Sie * Ctr + Space * drücken, damit die IDE' toList' ausfüllt und den statischen Import hinzufügt. – Lii

+2

Eine Sache zu beachten ist, dass 'IntStream' und einige andere fast-aber-nicht-ganz-'Stream's nicht die' collect (Collector) 'Methode haben und Sie' IntStream.boxed() aufrufen müssen. 'um sie zuerst in einen regulären' Stream' zu konvertieren. Vielleicht willst du auch 'toArray()'. –

131

Ein weiterer Ansatz Collectors.toCollection verwenden zu verwenden ist.

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

Älterer Weg. Der akzeptierte ist der Hauptweg. – odiszapc

+44

Dies ist jedoch nützlich, wenn Sie eine bestimmte List-Implementierung wünschen. – orbfish

+0

Obwohl empfohlen wird, Code für Schnittstellen zu verwenden, gibt es klare Fälle (einer davon ist GWT), wenn Sie gegen konkrete Implementierungen codieren müssen (es sei denn, Sie möchten, dass alle List-Implementierungen als Javascript kompiliert und ausgeliefert werden). –

0

In Fall jemand (wie ich) da draußen nach Wegen sucht, mit Objekten statt primitiver Typen umzugehen, dann benutze 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); 

druckt:

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

Ich mag eine util-Methode verwenden, die einen Kollektor für ArrayList zurückgibt, wenn das ist, was ich will.

Ich denke, die Lösung mit Collectors.toCollection(ArrayList::new) ist ein wenig zu laut für eine solche gemeinsame Operation.

Beispiel:

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

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

Mit dieser Antwort, die ich mag auch zeigen, wie einfach es zu schaffen ist und benutzerdefinierte Sammler zu verwenden, die in der Regel sehr nützlich ist.

+0

Wenn Sie das Ergebnis als Liste deklarieren, müssen Sie diese util-Methode nicht verwenden. Collectors.toList wird tun. Auch die Verwendung bestimmter Klassen anstelle von Schnittstellen ist ein Code-Geruch. –

+1

@LluisMartinez: * "Collectors.toList wird tun." *: Nein, nicht in vielen Situationen. Weil es keine gute Idee ist, 'toList' zu verwenden, wenn Sie die Liste später im Programm ändern möchten.Die ['toList'-Dokumentation] (https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toList--) sagt dies: *" Es gibt keine Garantien auf Typ, Änderbarkeit, Serialisierbarkeit oder Thread-Sicherheit der zurückgegebenen Liste; wenn mehr Kontrolle über die zurückgegebene Liste erforderlich ist, verwenden Sie 'toCollection'." *. Meine Antwort zeigt eine Möglichkeit, dies in einem häufigen Fall bequemer zu machen. – Lii

+0

Wenn Sie eine ArrayList erstellen möchten, ist es in Ordnung. –

1

Wenn es Ihnen nichts ausmacht, 3rd-Party-Bibliotheken zu verwenden, hat AOL's cyclops-react lib (Offenlegung ich einen Beitrag) Erweiterungen für alle JDK Collection Typen, einschließlich List. Die ListX-Schnittstelle erweitert java.util.List und fügt eine große Anzahl nützlicher Operatoren hinzu, einschließlich Filter.

können Sie einfach

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

Listex auch aus einer bestehenden Liste erstellt werden kann (via Listex schreib.fromIterable)

2

Wenn Sie über ein Array von Primitiven verfügen, können Sie die in Eclipse Collections verfügbaren Primitivsammlungen verwenden.

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

Wenn Sie nicht die sourceLongList von List ändern können:

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

Wenn Sie verwenden möchten 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); 

Hinweis: Ich bin ein Beitrag Kollektionen Eclipse.

-2

Wenn Sie nicht parallel() verwenden dies funktionieren wird

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

Ich mag es nicht, dass die collect() nur verwendet wird, um den Stream zu steuern, so dass der peek() - Haken für jedes Element aufgerufen wird. Das Ergebnis der Terminaloperation wird verworfen. –

+0

Es ist sehr seltsam, 'collect' aufzurufen und den Rückgabewert nicht zu speichern. In diesem Fall können Sie stattdessen "forEach" verwenden. Aber das ist immer noch eine schlechte Lösung. – Lii

+0

Mit peek() auf diese Weise ist ein Antipattern. –

3

Eine wenig effiziente Art und Weise (vermeiden die die Quellenliste und die Auto-Unboxing durch die Filter erstellen):

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

hier ist der Code von AbacusUtil

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

Disclosure: ich bin der Entwickler AbacusU bis.

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

Das ist der Ruf, die alle Stream konvertieren verwenden können, um Liste.