2016-10-13 1 views
5

Ich habe einen Java-Lambda-Stream, der eine Datei analysiert und die Ergebnisse basierend auf einer einfachen Filterung in einer Sammlung speichert.Java Lambda-Stream in verschiedene Sammlungen

Ich lerne nur Lambdas, also ertragen Sie mit mir hier, wenn das lächerlich schlecht ist. Aber zögern Sie nicht, auf meine Fehler hinzuweisen.

Für eine bestimmte Datei:

#ignored 
this 
is 
#ignored 
working 
fine 

Der Code:

List<String> matches; 

Stream<String> g = Files.lines(Paths.get(givenFile)); 

matches = g.filter(line -> !line.startsWith("#")) 
      .collect(Collectors.toList()); 

["this", "is", "working", "fine"] 

Nun, wie würde ich mich über die ignorierten Zeilen in einer zweiten Liste in diesem gleichen Strom zu sammeln? Etwas wie:

List<String> matches; 
List<String> ignored; // to store lines that start with # 

Stream<String> g = Files.lines(Paths.get(exclusionFile.toURI())); 

matches = g.filter(line -> !line.startsWith("#")) 
      // how can I add a condition to throw these 
      // non-matching lines into the ignored collection? 
      .collect(Collectors.toList()); 

ich es erkennen würde ziemlich trivial sein, einen neuen Stream zu öffnen, verändern die Logik ein wenig, und .collect() die ignorierten Linien leicht genug. Aber ich möchte diese Datei nicht zweimal durchlaufen müssen, wenn ich alles in einem Stream machen kann.

+0

Es muss etwas wie 'g.filter (..). Map (t-> t :: toString)) sein. Collect (.. toList())' –

+0

Siehe das letzte Beispiel in der Oracle-Dokumentation: https: //docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html – Gene

Antwort

13

Statt zwei Ströme können Sie partitioningBy in Collector verwenden

List<String> strings = Arrays.asList("#ignored", "this", "is", "#ignored", "working", "fine"); 
Map<Boolean, List<String>> map = strings.stream().collect(Collectors.partitioningBy(s -> s.startsWith("#"))); 
System.out.println(map); 

Ausgang

{false=[this, is, working, fine], true=[#ignored, #ignored]} 

hier verwendete ich Schlüssel als Boolean aber Sie können es zu einem sinnvollen String oder Enum ändern

BEARBEITEN

Wenn können die Strings mit einigen anderen Sonderzeichen beginnt könnten Sie groupingBy

List<String> strings = Arrays.asList("#ignored", "this", "is", "#ignored", "working", "fine", "!Someother", "*star"); 
    Function<String, String> classifier = s -> { 
     if (s.matches("^[[email protected]#$%^&*]{1}.*")) { 
      return Character.toString(s.charAt(0)); 
     } else { 
      return "others"; 
     } 
    }; 
    Map<String, List<String>> maps = strings.stream().collect(Collectors.groupingBy(classifier)); 
    System.out.println(maps); 

Ausgabe

{!=[!Someother], #=[#ignored, #ignored], *=[*star], others=[this, is, working, fine]} 

auch können Sie nisten groupingBy und partitioningBy

+1

Das hat super funktioniert, danke. – AWT

1

Ich denke, die nächstgelegene Sie kommen könnte ein generischer Ansatz dafür wäre etwas wie peek:

g.peek(line -> if (line.startsWith("#")) { 
    ignored.add(line); 
}) 
.filter(line -> !line.startsWith("#")) 
// how can I add a condition to throw these 
// non-matching lines into the ignored collection? 
.collect(Collectors.toList()); 

Ich erwähne es, denn im Gegensatz zu der Partitionierung Collector könnten Sie, zumindest in der Theorie, gemeinsam aber viele peek s Sie wollen - aber, wie Sie sehen können, müssen Sie Logik duplizieren, so ist es nicht ideal.

Verwandte Themen