0

I have a stream of object Person. I want to find people with height>6 and if there exists no one that matches that criteria, then I wanna find people with weight>100. I'm achieving it the following way but wondering if I can get it done in a better way.

Optional<Person> personWithHeightGreaterThan6 = persons.stream()
        .filter(person -> person.getHeight()>6)
        .findFirst();
if (personWithHeightGreaterThan6.isPresent()) {
        result = personWithHeightGreaterThan6.get();
} else {
Optional<Person> personWithWeightGreaterThan100 = persons.stream()
        .filter(person -> person.getWeight()>100)
        .findFirst();
    if (personWithWeightGreaterThan100.isPresent()) {
            result = personWithWeightGreaterThan100.get();
        }
    }
5
  • 1
    What's your criteria for "better"? Commented Oct 10, 2017 at 0:41
  • basically I don't like the idea of having multiple if elses. I was hoping if it could be achieved with a single stream. Commented Oct 10, 2017 at 4:31
  • Is it performance or cleanliness you're after? Commented Oct 10, 2017 at 4:36
  • cleanliness would be preferred for my usecase. Commented Oct 10, 2017 at 4:48
  • That's obviously going to be subjective, but see my answer for a possible approach. I may have another idea I'll post once I get to a computer. Commented Oct 10, 2017 at 5:01

2 Answers 2

3

shmosel’s answer can be simplified as

@SafeVarargs
static <T> T findFirst(List<T> elements, Predicate<? super T>... filters) {
    return Arrays.stream(filters)
            .flatMap(f -> elements.stream().filter(f))
            .findFirst()
            .orElse(null);
}

Unfortunately, with the current Stream implementation, using flatMap has a small performance disadvantage over the other solution, as discussed in “Why filter() after flatMap() is “not completely” lazy in Java streams?”, but for most practical use cases, it might be sufficient.

Java 9 is offering an in-between solution with full laziness and almost as simple as the one above:

@SafeVarargs
static <T> T findFirst(List<T> elements, Predicate<? super T>... filters) {
    return Arrays.stream(filters)
            .flatMap(f -> elements.stream().filter(f).findFirst().stream())
            .findFirst()
            .orElse(null);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Didn't think of using flatMap(). Very nice.
2

Here's a method that tries to find an element by matching against an array of filters ordered by priority. If it doesn't find a match against any filter it will return null.

@SafeVarargs
static <T> T findFirst(List<T> elements, Predicate<? super T>... filters) {
    return Arrays.stream(filters)
            .map(f -> elements.stream().filter(f).findFirst())
            .filter(Optional::isPresent)
            .map(Optional::get)
            .findFirst()
            .orElse(null);
}

You can call it like this:

Person result = findFirst(persons,
        person -> person.getHeight() > 6,
        person -> person.getWeight() > 100);

Might be a little overkill, but it's not really clear what kind of improvement you're looking for.

1 Comment

“Might be a little overkill, but” what the OP has asked for. Good job; I just added the alternatives to .filter(Optional::isPresent) .map(Optional::get)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.