简体   繁体   中英

Java stream API - avoid using same predicate twice to calculate average

class A {
    double value;
    String key;
}

class B {
    List<A> aList;
}

Given List<B> bList , I want to calculate the average of the value s for a specific key k . Assume each B can hold only 0 or 1 times an instance of A with key.equals(k) .

I figured I could first filter aList , and later extract the value using mapToDouble :

double average = blist.stream().filter(
    b -> b.aList.stream().anyMatch(
        a -> a.key.equals(k)
    )
).mapToDouble(
    b -> b.aList.stream().filter(
        a -> a.key.equals(k)
    ).findFirst().get().value
).average().orElse(0);

But there is clearly a redundancy here, since I am filtering the same list by the same predicate twice ( a -> a.key.equals(k) ).

Is there a way to mapToDouble while omitting elements with missing matching key s at the same time?

Edit:

Here is a more concrete example, hopefully it will make it easier to understand:

String courseName;

...

double average = students.stream().filter(
    student -> student.getGrades().stream().anyMatch(
        grade -> grade.getCourseName().equals(courseName)
    )
).mapToDouble(
    student -> student.getGrades().stream().filter(
        grade -> grade.getCourseName().equals(courseName)
    ).findFirst().get().getValue()
).average().orElse(0);

System.out.println(courseName + " average: " + average);

Try this:

double average = bList.stream()
        .flatMap(b -> b.aList.stream())
        .filter(a -> a.key.equals(k))
        .mapToDouble(a -> a.value)
        .average()
        .orElse(Double.NaN);

If your objects have private field and getters, which they really should, it'd be like this:

double average = bList.stream()
        .map(B::getaList)
        .flatMap(List::stream)
        .filter(a -> a.getKey().equals(k))
        .mapToDouble(A::getValue)
        .average()
        .orElse(Double.NaN);

Try this.

double average = blist.stream()
    .map(b -> b.aList.stream()
        .filter(a -> a.key.equals(k))
        .findFirst())
    .filter(a -> a.isPresent())
    .mapToDouble(a -> a.get().value)
    .average().orElse(0);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM