Convert using JAVA 8 Streams - java

I've tried to change this code to Java 8 streams. My code looks like this:
for(D d : n.getD()) {
for(M m : d.getT().getM()) {
if(m.getAC().contains(this)) {
return d;
}
}
}
and I want to convert it to java 8 streams. I've started like this:
n.getD().stream()
.map(m -> m.getT().getM())
but then I don't know if I should map again, or use a filter.

Other possible way is to use anyMatch instead of second filter
return n.getD().stream().filter(
d -> d.getT().getM().stream().anyMatch(
m -> m.getAC().contains(this)
)
).findFirst(); // result will be Optional<D>

one way to handle this:
return n.getD().stream().filter(d -> d.getT().getM().stream().filter(m -> m.getAC().contains(this)).findFirst().isPresent()).findFirst();
in this case a null value is possible.

I don't know about your domain, but to keep it readable I would probably delegate and simplify to something like this:
return n.getD().stream()
.filter(d -> d.getT().containsAC(this))
.findFirst()
.orElse(null);
And then in class T add the delegation method:
public boolean containsAC(AC ac) {
return m.stream().anyMatch(m -> m.getAC().contains(ac));
}

Related

How to convert 2 forEach loops into functional code?

How to convert the following nested forEach loop into functional code?
ABC abc = new ABC();
for (A a : aList) {
for (String b : bList) {
if (Objects.equals(a.getName(), b)) {
abc.setId(a.getId());
abc.setValue(a.getValue());
}
}
}
I've tried to convert it this way, but it didn't work:
aList.forEach(a -> {
bList.stream()
.filter(b -> Objects.equals(b, a.getName()));
abc.setId(a.getId());
abc.setValue(a.getValue());
});
Not sure what is considered a "functional code" and why that is needed here.
My approach, assuming bList is a List<String>:
var abc = aList
.stream()
.filter(this::isKnownName)
.map(this::abcFromA)
.findFirst() // see ¹ bellow
.orElseGet(ABC::new);
using the following helper functions - not really needed, we could use lambda expressions, but I think using this is a little bit more readable ² :
private boolean isKnownName(A a) {
return bList.contains(a.getName());
}
private ABC abcFromA(A a) {
var result = new ABC();
result.setId(a.getId());
result.setValue(a.getValue());
return result;
}
¹ this code is using the first match found in bList, code in question is using the last match
² I would struggle between using this solution at all and using a single loop (with bList.contains() instead of inner loop as posted in question).
You can try
aList.forEach(a -> {
if( bList.stream()
.anyMatch(b -> Objects.equals(b, a.getName()))){
abc.setId(a.getId());
abc.setValue(a.getValue());
}
});
or better
aList.stream().filter(a -> bList.stream()
.anyMatch(b -> Objects.equals(b,a.getName())))
.findAny()
.ifPresent(a -> {
abc.setId(a.getId());
abc.setValue(a.getValue());
});

Using Java Optional within stream mapping

I have code like this:
public void processList(List<String> list) {
for (String item : list) {
Object obj = getObjectForString(item);
if (obj != null) {
doSomethingWithObject(obj);
} else {
System.err.println("Object was null for " + item);
}
}
}
Ideally I would like to streamline this and avoid the null check using list.stream().map( *blah, blah, blah* ), and doSomethingWithObject if the object is not null, but log the error otherwise (by using the orElse method on an optional). I'm not super savvy with this Java 8 functionality and not sure if there is a nice, slick way to do what I want here or not. Suggestions?
Edit to add a failed attempt at this:
list.stream()
.map(p -> getObjectForString(p))
.map(Optional::ofNullable)
.forEach(
p -> p.ifPresentOrElse(
r -> doSomethingWithObject(r),
() -> System.err.println("Object was null")
));
Even if that code behaved the way I want, it still doesn't append the String from the original list to the error message as I would like it to. But maybe that's too much complexity to try to accomplish with streams like this.
we should propagate the item even after conversion. The slick way is using tuple or pair.
I used Tuple from vavr functional library to do the same. And below is the code for your reference
list.stream()
.map(p -> Tuple.of(p, getObjectForString(p)).map2(Optional::ofNullable))
.forEach(p -> p._2.ifPresentOrElse(
r -> doSomethingWithObject(r),
() -> System.err.println("Object was null" + p._1))
);
Another approach would be to collect the items in to separate 2 buckets/partitions based on if the item had an associated object or not. After that, process the 2 buckets as required:
final Boolean HAS_OBJECT = Boolean.FALSE;
Map<Boolean, List<String>> partitionedMap = list.stream()
.collect(Collectors.partitioningBy(item -> !Objects.isNull(getObjectForString(item))));
partitionedMap.get(HAS_OBJECT).stream()
.map(item -> getObjectForString(item))
.forEach(obj -> doSomethingWithObject(obj));
partitionedMap.get(!HAS_OBJECT)
.forEach(item -> System.err.println("Object was null for " + item));
Even though the below method does not avoid a null check as you wanted in your question, this is just another way to achieve the same result. (Only benefit is that it saves 1-2 lines of code!).
The below code uses Runnable (takes no arguments and returns nothing as well) along with Java 8's Function.
NOTE : I would still recommend the normal for loop :-), as I believe that the below might look fancy, but the for loop is more easy to understand in this particular case.
Function<String, Runnable> func = item -> {
Object obj = getObjectForString(item);
return (obj != null) ? ( () -> doSomethingWithObject(obj))
: ( () -> System.err.println("Object was null for " + item));
};
list.stream().map(func).forEach(Runnable::run);

Replacing isPresent with ifPresent and orElse

I have the following logic in my method where I check for the value of an optional parameter, and depending on that I build another object.
AtomicReference<Employee> employeeValue = null;
questions.forEach(question -> {
if(question.isBoolean().isPresent()) {
employeeValue.set(Employee.builder()
.withBooleanValue(Boolean.valueOf(question.value()))
.build());
} else {
employeeValue.set(Employee.builder()
.withStringValue(question.value())
.build());
}
Record record = Record.builder()
.withId(question.id())
.withValue(employeeValue.get())
.build();
answers.add(record);
});
How can I replace the above with ifPresent and orElse? I'm using Java 8 and therefore ifPresentOrElse method is not available. If I am to use ifPresent and orElse separately with anonymous inner function, how do I go about it?
Any help would be much appreciated.
You neither need isPresent() nor ifPresent(). You don’t need peek() (as in the other answer) nor an AtomicReference (as in the question). I believe that this does it:
questions.forEach(question -> {
Employee empl = question.isBoolean()
.map(b -> Employee.builder()
.withBooleanValue(Boolean.valueOf(question.value()))
.build())
.orElseGet(() -> Employee.builder()
.withStringValue(question.value())
.build());
Record record = Record.builder()
.withId(question.id())
.withValue(empl)
.build();
answers.add(record);
});
You can probably apply this idea inside the stream from the other answer if you want. Rather than using Stream.forEach() I’d prefer to collect into a collection like a list and then use answers.addAll().
You can stream through questions and use peek and map-orElse construction to achieve the same result:
questions.stream()
.peek(question -> {
Employee employee = question.isBoolean()
.map(b -> Employee.builder().withBooleanValue(Boolean.valueOf(question.value())).build())
.orElse(Employee.builder().withStringValue(question.value()).build());
employeeValue.set(employee);
}
)
.map(question -> Record.builder().withId(question.id()).withValue(employeeValue.get()).build())
.forEach(answers.add(answer)); // did you mean 'record'?
But to be honest it does not change a lot - your implementation looks maybe less "java eightish" but is fine :)

Java 8 Streams - Filter More Than One Condition

Based on some sports results data, I have a Fixture object which has getHome() and getAway() method. I'd like to shorten this method which I've written to only use a single lambda function (instead of creating a new list and two lambdas), is this possible?
private Collection<FixtureResult> finalResults(Team team) {
List<FixtureResult>finalResults = new ArrayList<>();
List<FixtureResult> homeResults = resultList.stream().filter(fixture ->
fixture.getHome().equals(team))
.collect(toList());
List<FixtureResult> awayResults = resultList.stream().filter(fixture ->
fixture.getAway().equals(team))
.collect(toList());
finalResults.addAll(homeResults);
finalResults.addAll(awayResults);
return finalResults;
}
Simple enough
resultList.stream()
.filter(fixture -> fixture.getHome().equals(team) || fixture.getAway().equals(team)))
.collect(toList());
EDIT: This is on the assumption that order does not matter to you. If your final list needs to have home result and then away, have a look at Elliott Frisch's answer.
If you wan to get fancy with lambdas:
Predicate<FixtureResult> isHome = fr -> fr.getHome().equals(team)
Predicate<FixtureResult> isAway = fr -> fr.getAway().equals(team)
resultList.stream()
.filter(isHome.or(isAway))
.collect(toList()));
You could even extract the compose predicate to test it in isolation, with no streams involved, which is good for more complex predicates:
Predicate<FixtureResult> isHomeOrAway = isHome.or(isAway)
assertTrue(isHomeOrAway(homeFixture));
...
Assuming the order doesn't matter, you can do it on one line. Like,
private Collection<FixtureResult> finalResults(Team team) {
return resultList.stream()
.filter(fixture -> fixture.getHome().equals(team)
|| fixture.getAway().equals(team))
.collect(toList());
}
If the order matters (home results and then away), you can do it with a single List like
private Collection<FixtureResult> finalResults(Team team) {
List<FixtureResult> al = new ArrayList<>(resultList.stream()
.filter(fixture -> fixture.getHome().equals(team)).collect(toList()));
al.addAll(resultList.stream()
.filter(fixture -> fixture.getAway().equals(team)).collect(toList()));
return al;
}
You can simply create a conditions concatenations or can concatenate multiple filter call
Conditions concatenations
myList.stream()
.filter(element -> (condition1 && condition2 && condition3))
Multiple filter call
myList.stream()
.filter(element -> condition1)
.filter(element -> condition2)
.filter(element -> condition3)
You can do the following
someStream.filter(((Predicate<SomeClass>) someObject-> someCondition).or(someObject-> someOtherCondition))
Or you can define your own "or" function that won't cause such a deep hierarchy
#SuppressWarnings("unchecked")
<R> Predicate<R> or(Predicate<R> ...predicates) {
return r -> Arrays.stream(predicates).anyMatch(p -> p.test(r));
}
That gives you a cleaner interface without casting and the nesting
.filter(or(
yourObject -> {
return false;
},
yourObject -> {
return false;
},
yourObject -> {
return false;
},
yourObject -> {
return false;
}
))

Check instanceof in stream

I have the following expression:
scheduleIntervalContainers.stream()
.filter(sic -> ((ScheduleIntervalContainer) sic).getStartTime() != ((ScheduleIntervalContainer)sic).getEndTime())
.collect(Collectors.toList());
...where scheduleIntervalContainers has element type ScheduleContainer:
final List<ScheduleContainer> scheduleIntervalContainers
Is it possible to check the type before the filter?
You can apply another filter in order to keep only the ScheduleIntervalContainer instances, and adding a map will save you the later casts :
scheduleIntervalContainers.stream()
.filter(sc -> sc instanceof ScheduleIntervalContainer)
.map (sc -> (ScheduleIntervalContainer) sc)
.filter(sic -> sic.getStartTime() != sic.getEndTime())
.collect(Collectors.toList());
Or, as Holger commented, you can replace the lambda expressions with method references if you prefer that style:
scheduleIntervalContainers.stream()
.filter(ScheduleIntervalContainer.class::isInstance)
.map (ScheduleIntervalContainer.class::cast)
.filter(sic -> sic.getStartTime() != sic.getEndTime())
.collect(Collectors.toList());
A pretty elegant option is to use method reference of class:
scheduleIntervalContainers
.stream()
.filter( ScheduleIntervalContainer.class::isInstance )
.map( ScheduleIntervalContainer.class::cast )
.filter( sic -> sic.getStartTime() != sic.getEndTime())
.collect(Collectors.toList() );
There is a small problem with #Eran solution - typing class name in both filter and map is error-prone - it is easy to forget to change the name of the class in both places. An improved solution would be something like this:
private static <T, R> Function<T, Stream<R>> select(Class<R> clazz) {
return e -> clazz.isInstance(e) ? Stream.of(clazz.cast(e)) : null;
}
scheduleIntervalContainers
.stream()
.flatMap(select(ScheduleIntervalContainer.class))
.filter( sic -> sic.getStartTime() != sic.getEndTime())
.collect(Collectors.toList());
However there might be a performance penalty in creating a Stream for every matching element. Be careful to use it on huge data sets. I've learned this solution from #Tagir Vailev
Instead of a filter + map like other answers suggest, I would recommend this utility method:
public static <Super, Sub extends Super> Function<Super, Stream<Sub>> filterType(Class<Sub> clz) {
return obj -> clz.isInstance(obj) ? Stream.of(clz.cast(obj)) : Stream.empty();
}
Use it as:
Stream.of(dog, cat fish)
.flatMap(filterType(Dog.class));
Compared to filter + map it has the following advantages:
If the class does not extend your class you will get a compile error
Single place, you can never forget to change a class in either filter or map
Filter by class type with StreamEx
StreamEx.of(myCollection).select(TheThing.class).toList();

Categories