Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 streams filtering with priority

I have code that looks like:

for (SomeObject object : objects) {
    if (object.getSomething() == Something.SomethingHighPriority) {
        return object;
    }
}

for (SomeObject object : objects) {
    if (object.getSomething() == Something.SomethingLowPriority) {
        return object;
    }
}

It is used for getting the first element in collection by some condition. Also, the priority is important. I need firstly look for the one element and just if it doesn't exist look for the second one.

I want to rewrite it using Stream API but I know that streams are used just once. For now I rewrote it in such way but it seems more ugly than before.

Optional<SomeObject> object = 
    objects.stream()
           .filter(object -> object.getSomething() == Something.SomethingHighPriority)
           .findFirst();

if (object.isPresent()) {
    return object.get();
}

object = objects.stream()
                .filter(object -> object.getSomething() == Something.SomethingLowPriority)
                .findFirst();

// No other use cases
return object.orElse(null);

Is it possible to make it less boilerplate?

like image 508
Oleksandr Riznyk Avatar asked Oct 17 '18 10:10

Oleksandr Riznyk


2 Answers

You can chain the two pipelines:

return objects.stream()
              .filter(object -> object.getSomething() == Something.SomethingHighPriority)
              .findFirst()
              .orElseGet(() -> objects.stream()
                                      .filter(object -> object.getSomething() == Something.SomethingLowPriority)
                                      .findFirst()
                                      .orElse(null));

An alternative would be to sort the Stream by object.getSomething() in descending order and then return the first element (if it has one of the two required values), but that would take O(NlogN) which is less efficient.

like image 166
Eran Avatar answered Nov 03 '22 01:11

Eran


I think your code is OK, iterating a collection twice is not a bad approach.

But if you want to do it in only one pass, you could collect to a Map:

Map<Something.Priority, Something> map = objects.stream()
    .filter(o -> o.getSomething() == Something.SomethingHighPriority
              || o.getSomething() == Something.SomethingLowPriority)
    .collect(Collectors.toMap(
                SomeObject::getSomething,
                Function.identity(),
                (oldObject, newObject) -> oldObject));

SomeObject highPriority = map.get(Something.SomethingHighPriority);

return highPriority == null ? map.get(Something.SomethingLowPriority) : highPriority;
like image 28
fps Avatar answered Nov 03 '22 02:11

fps