简体   繁体   中英

Layered filtering using Java Stream API

I have some imperative Java conditional code that I want to refactor to use Streams.

Specifically, I have this map that I want to filter into a List based on specific filter criteria.

private  Map<Integer,Thing> thingMap = new HashMap<Integer,Thing>();
// populate thingMap

And here's the code that uses it:

List<Thing> things = new ArrayList<Thing>();

for (Thing thing : thingMap.values()) {
    if (thing.getCategory().equals(category)) {
        if (location == null) {
            things.add(thing);
        } else if (thing.getLocation().equals(location)) {
            things.add(thing);
        }
    }
}

I refactored that to the following. But what's missing is I want the location to be checked only if the category filter passes. Also, I suspect there's a better way to do this:

List<Thing> things = thingMap.entrySet()
                      .stream()
                      .filter(t -> t.getValue().getCategory().equals(category))
                      .filter(t -> 
                          location == null || 
                          t.getValue().getLocation().equals(location)
                       )
                      .map(Map.Entry::getValue)
                      .collect(Collectors.toList());
    

What would be the idiomatic approach to retaining the layered conditional checks using Streams?

Operations chained after a filter will only be executed for elements accepted by the predicate. So there is no need to worry about that.

You could also join the conditions into a single filter step, just like you could join the nested if statements into a single if , by combining the conditions using && . The result is the same.

But note that the loop uses the condition location == null , referring to the variable declared outside the code snippet you have posted, not thing.getLocation() == null .

Besides that, you made other unnecessary changes compared to the loop. The loop iterates over the values() view of the map whereas you used entrySet() for the Stream instead, introducing the need to call getValue() on a Map.Entry four times.

A straight-forward translation of the loop logic is much simpler:

List<Thing> things = thingMap.values().stream()
    .filter(thing -> thing.getCategory().equals(category))
    .filter(thing -> location == null || thing.getLocation().equals(location))
    .collect(Collectors.toList());

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