2016-12-29 6 views
0

sich die folgende pojo Klasse Lombok AnmerkungenJava 8 summarise durch mehr Prädikat

@Setter 
@Getter 
@Builder 
@ToString 
public class User { 
    private String firstName; 
    private String lastName; 
    private Gender gender; 
    private Integer age; 
    private Integer points; 
} 

Anforderung kommentierte Verwendung ist für folgende Prädikate LongSummaryStatistics von 'Punkten' Attribute zu erhalten:

  1. Predicate<User> adultMenPredicate = user -> Gender.MALE == user.getGender && user.getAge()>18
  2. Predicate<User> adultWomenPredicate = user -> Gender.FEMALE == user.getGender && user.getAge()>18
  3. Predicate<User> minorPredicate = user -> user.getAge()<18

Meine aktuelle Implementierung ist:

private LongSummaryStatistics getPointStats(List<User> users, Predicate<User> predicate) { 
    return users.stream().filter(predicate).mapToLong(User::getPoints).summaryStatistics(); 
} 

System.out.println("point stats for adult men: " + getPointStats(users, adultMenPredicate)); 
System.out.println("point stats for adult women: " + getPointStats(users, adultWomenPredicate)); 
System.out.println("point stats for minors: " + getPointStats(users, minorPredicate)); 

Hier haben wir den Benutzer Sammlung dreimal iterieren. Ist es möglich, dies in nur einer Iteration zu erhalten?

+0

Nicht mit Streams. Aus rechnerischer Sicht ist der Vorgang des dreifachen Iterierens über die Liste konstant, so dass Leistungsprobleme ignoriert werden. Halten Sie es so, wie es ist, viel lesbarer und testbar. Wenn Sie wirklich (wirklich) Leistung brauchen, gehen Sie für ein for-each, indem Sie Ihre eigenen Statistiken und wenn-dann-sonst im for-each erstellen – JeanValjean

Antwort

2

Ich habe so etwas herausgefunden:

public static void main(String [] args) { 
    List<User> users = ImmutableList.of(new User("a", "s", MALE, 19, 22), 
             new User("a", "s", MALE, 15, 49), 
             new User("a", "s", MALE, 22, 11), 
             new User("a", "s", FEMALE, 19, 1), 
             new User("a", "s", MALE, 12, 22)); 

    Map<Type, Integer> collect = users.stream() 
      .map(u -> Tuple.tuple(u, resolveType(u))) 
      .collect(Collectors.groupingBy(Tuple::right, Collectors.summingInt(t -> t.left().points))); 
    System.out.println(collect); 
} 

public static Type resolveType(final User user) { 
    if (user.gender == MALE && user.age > 18) { 
     return Type.ADULT_MALE; 
    } else if (user.gender == FEMALE && user.age > 18) { 
     return Type.ADULT_FEMALE; 
    } else { 
     return Type.MINOR; 
    } 
} 

public enum Type { 
    ADULT_MALE, ADULT_FEMALE, MINOR 
} 

Ich denke, es ist eine ausgewogene Lösung - sehr effizient und gut lesbar. Ich mag es nicht, wenn-else-Anweisungen, damit Sie es mit Karte wie ersetzen:

private static final Map<Predicate<User>, Type> predicates = ImmutableMap.of(
     user -> user.getGender() == MALE && user.getAge() >= 18, Type.ADULT_MALE, 
     user -> user.getGender() == FEMALE && user.getAge() >= 18, Type.ADULT_FEMALE, 
     user -> user.getAge() < 18, Type.MINOR 
); 

public static Type resolveType(final User user) { 
    return predicates.entrySet().stream() 
      .filter(entry -> entry.getKey().test(user)) 
      .findFirst() 
      .map(Map.Entry::getValue) 
      .orElseThrow(RuntimeException::new); 
} 

Er druckt:

{ADULT_MALE=33, MINOR=71, ADULT_FEMALE=1} 

Ich denke, Sie müssen nicht über die Leistung, wenn Sie‘Sorge re mit großen Sammlungen zu tun haben.

// bearbeiten Nur um es klar zu machen. Meine Tuple-Implementierung sieht so aus:

@ToString 
@EqualsAndHashCode 
public class Tuple<L, R> { 
    public static <L, R> Tuple<L, R> tuple(L left, R right) { 
     return new Tuple<>(left, right); 
    } 

    private final L left; 
    private final R right; 

    private Tuple(L left, R right) { 
     this.left = left; 
     this.right = right; 
    } 

    public L left() { 
     return left; 
    } 

    public R right() { 
     return right; 
    } 
} 
+0

Wie erhalten wir min max Durchschnitt etc, die Attribute von 'LongSummaryStatistics' sind? – harvey123

+1

Aber ich denke, wir können groupyBy anstelle von Tupel wie hier erwähnt verwenden: http://StackOverflow.com/Questions/29293453/Java-8-Lambda-Expression-Group-all-Items-in-sequence-which-Satisfy-the -given-pr – harvey123

+1

Ich habe bereits groupingBy Kollektor verwendet, aber ich gruppiere nach links Feld des Tupels. Tuple wurde hier verwendet, weil ich sowohl auf den Benutzer als auch den aufgelösten Typ zugreifen muss. Ich erhalte die Summe der Punkte, indem ich summingInt verschachtelten Kollektor verwende. Werfen Sie einen Blick auf Collectors-Klasse. Es gibt viele Sammler, die für Sie hilfreich sein könnten: AveragingInt, MinBy, MaxBy usw. –

Verwandte Themen