How to perform nested 'if' statements using Java 8/lambda?
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());
Related videos on Youtube
Comments
-
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 almost 9 yearsbut is it possible to do it with
map
,filter
, etc? I am trying to learn the basics of lambda functions. Thanks. -
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 almost 9 years@Z-1 A lambda expression arises from the use of a functional interface (an interface with only 1
abstract
method, such asRunnable
; may have multipledefault
methods). For example, aThread
accepts aRunnable
in it's constructor. We could writenew Thread(() -> { });
. You can even create your own functional interfaces. So to answer "possible to do it withmap
andfilter
": yes. It works for all lambdas. -
moon almost 9 yearsThis is interesting. Thanks for the benchmark. I noticed that too, Streams is much slower than traditional for loop.
-
Mantis almost 9 yearsDepends 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 almost 9 yearsthe stream overhead does indeed get smaller as the loop range grows larger. doesn't quite vanish in this case though.
-
marcus over 7 yearsI like how you not only answer the question but you also teach how to think about the problem to get to the solution.