2016-12-29 1 views
0

Рассмотрим следующий класс POJO аннотированный с помощью Lombok примечаниямиJava 8 реферирования многократным предиката

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

требование, чтобы получить LongSummaryStatistics атрибута 'точек' для следующих предикатов:

  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

Моя текущая реализация является:

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

Здесь мы перебор коллекции пользователи трижды. Можно ли это сделать всего за одну итерацию?

+0

Не с потоками. С вычислительной точки зрения, операция итерации три раза по списку является постоянной, поэтому игнорируйте проблемы с производительностью. Держите его таким, как он, более читабельным и проверяемым. Если вы действительно (действительно) нуждаетесь в производительности, идите для каждого из них, создавая свою собственную статистику, а if-then-else в for-each – JeanValjean

ответ

2

Я понял, что-то вроде этого:

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 
} 

Я предполагаю, что это сбалансированное решение - весьма эффективным и читаемым. мне не нравится, если-иначе заявления, так что вы можете заменить его на карте, как:

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

Он печатает:

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

Я думаю, вам не придется беспокоиться о производительности, если вас» с огромными коллекциями.

// edit Просто, чтобы все было ясно. Реализация моего кортежа выглядит так:

@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

. Как мы получаем min max average и т. Д., Которые являются атрибутами «LongSummaryStatistics»? – harvey123

+1

Но я думаю, мы можем использовать groupingBy вместо кортежа, как упомянуто здесь: http://stackoverflow.com/questions/29293453/java-8-lambda-expression-group-all-items-in-sequence-which-satisfy-the -given-pr – harvey123

+1

Я уже использовал groupingBy collector, но я группирую левое поле кортежа. Tuple использовался здесь, потому что мне нужен доступ как к пользователю, так и к разрешенному типу. Я получаю сумму баллов за счет использования вложенного коллектора sumingInt. Взгляните на класс коллекционеров. Существует много коллекционеров, которые могут вам помочь: averagingInt, minBy, maxBy и т. Д. –

Смежные вопросы