2014-03-26 13 views
6

Ich versuche, Lambda-Ausdrücke in Java zu verstehen, 8.Der Versuch, in Java Lambda und Strom zu verstehen 8

sagen, dass ich eine Person Klasse, die wie folgt aussieht:

public class Person implements { 
    String name; 
    GenderEnum gender; 
    int age; 
    List<Person> children; 
} 

Nun, was ich tun möchte, findet alle Personen, die weiblich sind, die Kinder haben, die jünger als 10 Jahre alt sind.

Pre Java 8 Ich würde es tun, wie folgt:

List<Person> allPersons = somePeople(); 
List<Person> allFemaleWithChildren = new ArrayList<>(); 
for(Person p : allPersons) { 
    for(Person child : p.getChildren()) { 
     if(child.getAge() < 10 && p.getGender() == GenderEnum.Female) { 
      allFemaleWithChildren.add(p); 
     } 
    } 
} 

Jetzt allFemaleWithChildren sollte, was ich will. Ich habe versucht, die gleichen mit Hilfe von Streams zu tun Ich glaube, ich brauche eine Art Karte, Filter zu verwenden und

allPersons.stream() 
//filter females 
.filter(p -> p.getGender == GenderEnum.Female) 
//get the children 
.map(c -> c.getChildren()) 
//filter the ones that are less than 10 years 
.filter(c -> c.getAge() < 10) 
//return a list with the result 
.collect(Collectors.toList()) 

reduzieren Aber dieser Code nicht kompiliert. Was vermisse ich.

Auch ich verstehe nicht, wofür die Reduce-Methode verwendet werden kann.

Der Compiler sagt cannot resolve method getAge(). Dies liegt daran, c ist anscheinend eine Sammlung und nicht die Elemente in der Sammlung, die wirklich was ich will.

+1

Was sagt der Compiler? Und was implementiert die Klasse "Person"? –

+0

Das Problem scheint mit der letzten Filtermethode zu sein. 'c.getYear()' weil 'c' eine Sammlung und kein Iterator der Items –

+0

@Duncan ist. Aber es ist meiner Meinung nach nicht relevant. Jeder, der dies klar versteht, sollte meinen Fehler erkennen können, da ich nichts richtig mache. –

Antwort

9

Im Moment (sobald Sie den Kompilierungsfehler behoben haben) würden Sie eine Liste von Kindern zurückgeben. Unter der Annahme, dass Sie in Ihrem ursprünglichen Code gemeint, so bald zu brechen, wie Sie ein Kinder unter 10 Jahren finden, könnte das Äquivalent wie folgt aussehen:

allPersons.stream() 
    //filter females 
    .filter(p -> p.getGender() == GenderEnum.Female) 
    //only keep females with at least one child < 10 
    .filter(f -> f.getChildren().stream() 
        .anyMatch(c -> c.getAge() < 10)) 
    //return a list with the result 
    .collect(Collectors.toList()) 

Und in der Tat, wie unten kommentiert, Sie ein paar statische Importe verwenden könnte, fügen Sie Hilfsmethoden und Refactoring den ursprünglichen Code, um es besser lesbar:

allPersons.stream() 
    .filter(this::female) 
    .filter(this::hasChildrenUnder10) 
    .collect(toList()) 

//... 

private boolean female(Person p) { return p.getGender() == Female; } 
private boolean hasChildrenUnder10(Person parent) { 
    return parent.getChildren().stream() 
        .anyMatch(c -> c.getAge() < 10)); 
} 
+0

+1 Ich würde den Kinderfilter immer noch in seine eigene Funktion umgestalten und dann eine Methodenreferenz hier verwenden. Vielleicht etwas wie 'filter (this :: hasChildrenUnderTen)', nur um den Stream in diesem Fall besser lesbar zu machen. –

+0

Danke. Es funktionierte –

+1

Ein etwas prägnanterer Ersatz für 'filter (pred) .findAny(). IsPresent' ist' anyMatch (pred) '. –

1

Sie haben 2 for Schleifen, dass an einem gewissen Punkt bedeutet, dass Sie einen anderen Strom benötigen. Hier, wenn Sie map anrufen, ordnen Sie Ihre Mütter Listen von Kindern zu. Sie machen dann weiter, als ob Sie einen Strom von Kindern hätten, aber Sie haben tatsächlich einen Strom von Sammlungen von Kindern.