I'm changing from ugly nested for loops to a beautiful designed lambda expressions in java.
Here is my actual code
for (String foo : foos) {
for (Bar bar : bars) {
if (bar.getFoo().equals(foo)) {
FooBar fooBar = new FooBar();
fooBar.setBar(bar);
listOfFooBar.add(fooBar);
break;
}
}
}
My actual lambda code to replace code above
foos.forEach(i -> bars.stream().filter(p -> p.getFoo().equals(i)).findFirst().ifPresent(p -> {
FooBar s = new FooBar();
fooBar.setBar(bar);
listOfFooBar.add(fooBar);
}));
My question is, there is a way to populate listOfFooBar
with some kind of collect() method?
Something like listOfFooBar = foos.forEach(.....).collect(Collectors.toList());
One fact is that bars will always contain every foo, foos is basically a small part of bars.
If there is a better way (in terms of performance or elegance) to do that lambda, please share.
Since there is only one Bar per Foo, you could start by creating a map linking Foos to Bars:
Map<String, Bar> barsByFoo = bars.stream().collect(toMap(Bar::getFoo, b -> b));
If you have a lot more bars than foos, you can filter:
Map<String, Bar> barsByFoo = bars.stream()
.filter(b -> foos.contains(b.getFoo()))
.collect(toMap(Bar::getFoo, b -> b));
Your nested for loops can then be written:
List<FooBar> listOfFooBar = foos.stream()
.map(barsByFoo::get)
.filter(Objects::nonNull)
.map(FooBar::new)
.collect(toList());
This assumes there is a FooBar(Bar)
constructor.
Or you could take the problem from the other side and use an (I think) equivalent algo (you would probably benefit from using a Set<Foo>
in that case):
List<FooBar> listOfFooBar = bars.stream()
.filter(bar -> foos.contains(bar.getFoo()))
.map(FooBar::new)
.collect(toList());
Either way, it generally helps to step back from your initial loop as a different algo/approach is generally beneficial to a clean lambda solution.
If you want to go the whole nine yards:
List<FooBar> listOfFooBar = foos.stream()
.flatMap(foo -> bars.stream().filter(bar-> bar.getFoo().equals(foo)).findFirst()
.map(Stream::of).orElse(Stream.empty()))
.map(bar -> {
FooBar fooBar = new FooBar();
fooBar.setBar(bar);
return fooBar;
})
.collect(Collectors.toList());
If you had a FooBar
constructor that accepts a Bar
then you could save some lines and write
.map(FooBar::new)
FWIW in Java 9 you will be able to write
.findFirst().stream()
Assuming a suitable constructor it would then shorten to
List<FooBar> listOfFooBar = foos.stream()
.flatMap(foo -> bars.stream().filter(bar-> bar.getFoo().equals(foo)).findFirst().stream()))
.map(FooBar::new)
.collect(Collectors.toList());
EDIT: Using @Misha's suggestion you can shorten it even more:
List<FooBar> listOfFooBar = foos.stream()
.flatMap(foo -> bars.stream().filter(bar-> bar.getFoo().equals(foo)).limit(1)))
.map(FooBar::new)
.collect(Collectors.toList());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With