javajava-8java-streamfunctional-interfacesupplier

Correct way to apply different functions based on condition in stream


What would be the correct way to implement this functional logic with streams?

  1. Stream through list of items

  2. Check if condition 1 passes, if so apply function 1 to the item.

    Check if condition 2 passes, if so apply function 2 to the item.

    If both conditions do not pass, do nothing.

  3. collect results

Do I create predicates with suppliers or how does this look? I'm not very familiar with functional Java.


Solution

  • You're probably best off with a simple for loop, but you could do:

    List<Item> list;
    list.stream()
        .forEach(item -> {
           boolean condition1 = predicate1.test(item);
           boolean condition2 = predicate2.test(item);
           if(condition1 && !condition2) {
                applyFunction1(item);
           } else if(!condition1 && condition2) {
                applyfunction2(item);
           }
       });
    

    This operates directly on the underlying list. Use map if you want to collect to a different collection (but obviously it depends on the modifying function implementation whether it contains clones or the original, now also modified, objects):

    List<Item> list;
    List<Item> modifiedList = list.stream()
       .map(item -> {
           if(condition1 && !condition2) {
                return function1.apply(item);
           } else if(!condition1 && condition2) {
                return function2.apply(item);
           }
           return item;
       })
       .toList();
    

    If you do not care about the final ordering of items, here's another approach, which you can see gets silly...:

        List<Item> list;
        Predicate<Item> predicate1;
        Predicate<Item> predicate2;
        Function<Item, Item> function1;
        Function<Item, Item> function2;
    
        Stream<Item> modifiedBy1 = list.stream()
            .filter(predicate1.and(predicate2.negate()))
            .map(function1);
        Stream<Item> modifiedBy2 = list.stream()
            .filter(predicate1.negate().and(predicate2))
            .map(function1);
        Stream<Item> unmodified = list.stream()
            .filter(predicate1.negate().and(predicate2.negate()));
        
        Stream<Item> recombined = Stream
            .of(modifiedBy1, modifiedBy2, unmodified) //a Stream of Streams
            //you can influence the order of elements by swapping these around
            //the result is essentially sorted by which conditions where matched
            .flatMap(Function.identity()); //flatten the streams back to their constituent objects
        //could also have used 2x Stream.concat here