How to perform nested 'if' statements using Java 8/lambda?

57,379

Solution 1

The essential observation here is that your problem involves a non-isomorphic transformation: a single input element may map to zero, one, or two output elements. Whenever you notice this, you should immediately start looking for a solution which involves flatMap instead of map because that's the only way to achieve such a general transformation. In your particular case you can first apply filter for a one-to-zero element mapping, then flatMap for one-to-two mapping:

List<Integer> result =
    IntStream.rangeClosed(1, 10)
             .filter(i -> 10 % i == 0)
             .flatMap(i -> i == 5 ? IntStream.of(i) : IntStream.of(i, 10 / i))
             .boxed()
             .collect(toList());

(assuming import static java.util.stream.Collectors.toList)

Solution 2

You could declare a body for a lambda. For example:

Runnable run = () -> System.out.println("Hey");

Could be

Runnable run = () -> {
    System.out.println("Hey");
};

Within that body, you can create nested statements:

Runnable run = () -> {
    int num = 5;

    if(num == 5) {
        System.out.println("Hey");
    }
};

Solution 3

Use flatMap as you are trying to add elements into the pipeline or a 1-to-many mapping. Map is a one to one mapping.

ArrayList<Integer> result = (ArrayList<Integer>) IntStream.rangeClosed(1, 10)
                .boxed()
                .filter(i -> 10 % i == 0)
                .flatMap((Integer i) -> {return i!=5 ? Stream.of(i, (10/i)):Stream.of(i);})
                .collect(Collectors.toList());

This results in the same list as

ArrayList<Integer> result2 = new ArrayList<Integer>();

        for (int i = 1; i <= 10; i++) {
            if (10 % i == 0) {
                result2.add(i);
                if (i != 5) {
                    result2.add(10 / i);
                }
            }
        }

In case your wondering which way is faster the loop method is ~3 times faster than using streams.

Benchmark                     Mode  Cnt      Score     Error  Units
testStreams.Bench.loops       avgt    5     75.221 ±   0.576  ns/op
testStreams.Bench.streams     avgt    5    257.713 ±  13.125  ns/op

Solution 4

You can do this:

List<Integer> result1 = IntStream
    .rangeClosed(1, 10)
    .boxed()
    .filter(i -> 10 % i == 0)
    .map(i -> (i != 5 ? Stream.of(i, 10 / i) : Stream.of(i)))
    .flatMap(Function.identity())
    .collect(Collectors.toList());
Share:
57,379

Related videos on Youtube

moon
Author by

moon

Learn. Live. Sleep....and occasionally do other things.

Updated on May 15, 2020

Comments

  • moon
    moon almost 4 years

    I have the following code and would like to implement it using lambda functions just for fun. Can it be done using the basic aggregate operations?

    List<Integer> result = new ArrayList<>();
    
    for (int i = 1; i <= 10; i++) {
        if (10 % i == 0) {
            result.add(i);
            if (i != 5) {
                result.add(10 / i);
            }
        }
    }
    

    Using lambda:

    List<Integer> result = IntStream.rangeClosed(1, 10)
                                    .boxed()
                                    .filter(i -> 10 % i == 0)
                                    // a map or forEach function here?
                                    // .map(return 10 / i -> if i != 5)
                                    .collect(Collectors.toList());
    
  • moon
    moon almost 9 years
    but is it possible to do it with map, filter, etc? I am trying to learn the basics of lambda functions. Thanks.
  • randers
    randers almost 9 years
    @Z-1 I'm not sure if filters are able to do this, but the syntax would be .filter( i -> { return yourLogic; } )
  • Dioxin
    Dioxin almost 9 years
    @Z-1 A lambda expression arises from the use of a functional interface (an interface with only 1 abstract method, such as Runnable; may have multiple default methods). For example, a Thread accepts a Runnable in it's constructor. We could write new Thread(() -> { });. You can even create your own functional interfaces. So to answer "possible to do it with map and filter": yes. It works for all lambdas.
  • moon
    moon almost 9 years
    This is interesting. Thanks for the benchmark. I noticed that too, Streams is much slower than traditional for loop.
  • Mantis
    Mantis almost 9 years
    Depends on the application really, operations on ints are some of the simplest operartion you can do, for what you were doing here the overhead in setting up the stream is too high. I found the second answer on this post helpful.
  • the8472
    the8472 almost 9 years
    the stream overhead does indeed get smaller as the loop range grows larger. doesn't quite vanish in this case though.
  • marcus
    marcus over 7 years
    I like how you not only answer the question but you also teach how to think about the problem to get to the solution.