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();
        }
    }
shmosel
  • 49,289
  • 6
  • 73
  • 138
Nick01
  • 349
  • 2
  • 8
  • 22

2 Answers2

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);
}
Holger
  • 285,553
  • 42
  • 434
  • 765
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.

shmosel
  • 49,289
  • 6
  • 73
  • 138
  • 1
    “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)`… – Holger Oct 10 '17 at 06:58