Reputation: 347
Consider the following pojo class annotated using Lombok annotations
@Setter
@Getter
@Builder
@ToString
public class User {
private String firstName;
private String lastName;
private Gender gender;
private Integer age;
private Integer points;
}
requirement is to get LongSummaryStatistics
of 'points' attribute for following predicates:
Predicate<User> adultMenPredicate = user -> Gender.MALE == user.getGender && user.getAge()>18
Predicate<User> adultWomenPredicate = user -> Gender.FEMALE == user.getGender && user.getAge()>18
Predicate<User> minorPredicate = user -> user.getAge()<18
My current Implementation is :
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));
Here we are iterating the users collection thrice. Is it possible to get this in just one iteration ?
Upvotes: 0
Views: 466
Reputation: 1533
I've figured out something like that:
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
}
I guess it's a balanced solution - quite efficient and readable. I don't like if-else statements so you can replace it with Map like:
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);
}
It prints:
{ADULT_MALE=33, MINOR=71, ADULT_FEMALE=1}
I guess you don't have to worry about performance unless you're dealing with huge collections.
// edit Just to make it clear. My tuple implementation looks like that:
@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;
}
}
Upvotes: 2