Java 8 Lambda Stream forEach with two if-statements - java

I'm trying to refactoring some for-loops into lambda expressions, most of them are working fine, but I'm struggling with for-loops that contains two if-statements.
Code
for (B2KTransactionDTO b2kTransactionDTO : result) {
//Generate loyaltyMatchId based on transaction input
String loyaltyMatchId = getLoyaltyMatchIdBasedOnTransactionDTO(b2kTransactionDTO);
if (loyaltyMatchIdAmountMap.containsKey(loyaltyMatchId)) {
BigDecimal cashback = loyaltyMatchIdAmountMap.get(loyaltyMatchId);
b2kTransactionDTO.addLoyaltyPoints(cashback);
}
String loyaltyMatchInsuranceId = getLoyaltyMatchInsuranceIdBasedOnTransactionDTO(b2kTransactionDTO);
if (loyaltyMatchInsuranceIdAmountMap.containsKey(loyaltyMatchInsuranceId)) {
BigDecimal cashback = loyaltyMatchInsuranceIdAmountMap.get(loyaltyMatchInsuranceId);
b2kTransactionDTO.addLoyaltyPoints(cashback);
}
}
I refactored this to the following code:
result.forEach(b2kTransactionDTO -> {
//Generate loyaltyMatchId based on transaction input
String loyaltyMatchId = getLoyaltyMatchIdBasedOnTransactionDTO(b2kTransactionDTO);
if (loyaltyMatchIdAmountMap.containsKey(loyaltyMatchId)) {
BigDecimal cashback = loyaltyMatchIdAmountMap.get(loyaltyMatchId);
b2kTransactionDTO.addLoyaltyPoints(cashback);
}
String loyaltyMatchInsuranceId = getLoyaltyMatchInsuranceIdBasedOnTransactionDTO(b2kTransactionDTO);
if (loyaltyMatchInsuranceIdAmountMap.containsKey(loyaltyMatchInsuranceId)) {
BigDecimal cashback = loyaltyMatchInsuranceIdAmountMap.get(loyaltyMatchInsuranceId);
b2kTransactionDTO.addLoyaltyPoints(cashback);
}
});
Is it possible to even futher lambda-nize this?
Thanks

This is not the answer you are looking for.
I would say, don't use lambda's here.
Lambda's can help you to utilise parralelism features of cpu's to speed up processing of large datasets.
In my experience it's not worth the hassle of utilising a lambda with smaller datasets(<10(0).000);
Keep your code readable and maintainable and pluggable.
Learn to make objects that handle purposes. Don't put all the things in one object and end up with veryLongVariableNamesThatDescribeAPurpose.
When long variable names occur it should be a signal to you that you can refactor that code into several objects that handle a single purpose.
If you really want to use lambda's try the following:
Seperate objects for value holders and processors and methods for seperate tasks, it will be easier in to utilise lambda's when needed because you can simply plug in the methods for the tasks required and pass on the value holder objects to get your desired results.
So, not the answer you're looking for, but this was too long really to explain in a comment.

I think you also should consider refactor your code because it looks like there's a lot of duplication between the match and matchInsurance stuff.
private static void addLoyaltyPoints(Collection<B2KTransactionDTO> result, Map<String, BigDecimal> ids, Function<B2KTransactionDTO, String> extractId)
{
result.stream()
.filter(b -> ids.containsKey(extractId.apply(b)))
.forEach(b -> b.addLoyaltyPoints(ids.get(extractId.apply(b))));
}
Then the whole code you posted could become:
addLoyaltyPoints(result, loyaltyMatchIdAmountMap, Use::getLoyaltyMatchIdBasedOnTransactionDTO);
addLoyaltyPoints(result, loyaltyMatchInsuranceIdAmountMap, Use::getLoyaltyMatchInsuranceIdBasedOnTransactionDTO);
Edit: another version of addLoyaltyPoints (more concise, less expressive):
private static void addLoyaltyPoints(Collection<B2KTransactionDTO> result, Map<String, BigDecimal> ids, Function<B2KTransactionDTO, String> extractId)
{
result.stream()
.forEach(b -> b.addLoyaltyPoints(ids.getOrDefault(extractId.apply(b), new BigDecimal(0))));
}

Related

How to set a value to variable based on multiple conditions using Java Streams API?

I couldn't wrap my head around writing the below condition using Java Streams. Let's assume that I have a list of elements from the periodic table. I've to write a method that returns a String by checking whether the list has Silicon or Radium or Both. If it has only Silicon, method has to return Silicon. If it has only Radium, method has to return Radium. If it has both, method has to return Both. If none of them are available, method returns "" (default value).
Currently, the code that I've written is below.
String resolve(List<Element> elements) {
AtomicReference<String> value = new AtomicReference<>("");
elements.stream()
.map(Element::getName)
.forEach(name -> {
if (name.equalsIgnoreCase("RADIUM")) {
if (value.get().equals("")) {
value.set("RADIUM");
} else {
value.set("BOTH");
}
} else if (name.equalsIgnoreCase("SILICON")) {
if (value.get().equals("")) {
value.set("SILICON");
} else {
value.set("BOTH");
}
}
});
return value.get();
}
I understand the code looks messier and looks more imperative than functional. But I don't know how to write it in a better manner using streams. I've also considered the possibility of going through the list couple of times to filter elements Silicon and Radium and finalizing based on that. But it doesn't seem efficient going through a list twice.
NOTE : I also understand that this could be written in an imperative manner rather than complicating with streams and atomic variables. I just want to know how to write the same logic using streams.
Please share your suggestions on better ways to achieve the same goal using Java Streams.
It could be done with Stream IPA in a single statement and without multiline lambdas, nested conditions and impure function that changes the state outside the lambda.
My approach is to introduce an enum which elements correspond to all possible outcomes with its constants EMPTY, SILICON, RADIUM, BOTH.
All the return values apart from empty string can be obtained by invoking the method name() derived from the java.lang.Enum. And only to caver the case with empty string, I've added getName() method.
Note that since Java 16 enums can be declared locally inside a method.
The logic of the stream pipeline is the following:
stream elements turns into a stream of string;
gets filtered and transformed into a stream of enum constants;
reduction is done on the enum members;
optional of enum turs into an optional of string.
Implementation can look like this:
public static String resolve(List<Element> elements) {
return elements.stream()
.map(Element::getName)
.map(String::toUpperCase)
.filter(str -> str.equals("SILICON") || str.equals("RADIUM"))
.map(Elements::valueOf)
.reduce((result, next) -> result == Elements.BOTH || result != next ? Elements.BOTH : next)
.map(Elements::getName)
.orElse("");
}
enum
enum Elements {EMPTY, SILICON, RADIUM, BOTH;
String getName() {
return this == EMPTY ? "" : name(); // note name() declared in the java.lang.Enum as final and can't be overridden
}
}
main
public static void main(String[] args) {
System.out.println(resolve(List.of(new Element("Silicon"), new Element("Lithium"))));
System.out.println(resolve(List.of(new Element("Silicon"), new Element("Radium"))));
System.out.println(resolve(List.of(new Element("Ferrum"), new Element("Oxygen"), new Element("Aurum")))
.isEmpty() + " - no target elements"); // output is an empty string
}
output
SILICON
BOTH
true - no target elements
Note:
Although with streams you can produce the result in O(n) time iterative approach might be better for this task. Think about it this way: if you have a list of 10.000 elements in the list and it starts with "SILICON" and "RADIUM". You could easily break the loop and return "BOTH".
Stateful operations in the streams has to be avoided according to the documentation, also to understand why javadoc warns against stateful streams you might take a look at this question. If you want to play around with AtomicReference it's totally fine, just keep in mind that this approach is not considered to be good practice.
I guess if I had implemented such a method with streams, the overall logic would be the same as above, but without utilizing an enum. Since only a single object is needed it's a reduction, so I'll apply reduce() on a stream of strings, extract the reduction logic with all the conditions to a separate method. Normally, lambdas have to be well-readable one-liners.
Collect the strings to a unique set. Then check containment in constant time.
Set<String> names = elements.stream().map(Element::getName).map(String::toLowerCase).collect(toSet());
boolean hasSilicon = names.contains("silicon");
boolean hasRadium = names.contains("radium");
String result = "";
if (hasSilicon && hasRadium) {
result = "BOTH";
} else if (hasSilicon) {
result = "SILICON";
} else if (hasRadium) {
result = "RADIUM";
}
return result;
i have used predicate in filter to for radium and silicon and using the resulted set i am printing the result.
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Element> elementss = new ArrayList<>();
Set<String> stringSet = elementss.stream().map(e -> e.getName())
.filter(string -> (string.equals("Radium") || string.equals("Silicon")))
.collect(Collectors.toSet());
if(stringSet.size()==2){
System.out.println("both");
}else if(stringSet.size()==1){
System.out.println(stringSet);
}else{
System.out.println(" ");
}
}
}
You could save a few lines if you use regex, but I doubt if it is better than the other answers:
String resolve(List<Element> elements) {
String result = elements.stream()
.map(Element::getName)
.map(String::toUpperCase)
.filter(str -> str.matches("RADIUM|SILICON"))
.sorted()
.collect(Collectors.joining());
return result.matches("RADIUMSILICON") ? "BOTH" : result;
}

How to convert complex double for loop to Java 8 Streams reduce complexity and improve performance

I'm trying to rewrite this complex double for loop which seems to be a bit expensive, I think rewriting this using streams would improve readability and perhaps even avoid unnecessary looping iterations.
I'd like to convert this imperative code here:
public Documents clearDocuments(List<Document> toDeleteDocs) {
Documents softDeleteList = new Documents();
List<EntityKeyVersion> deleteList = entityManager.bulkDeleteEntities(ekList,EntityClient.DeleteMode.SOFT_DELETE);
for (Document doc : toDeleteDocs) {
for (EntityKeyVersion ekv : deleteList) {
if (ekv.getEntityKey().contentEquals(doc.getEntityKey())) {
doc.setVersion((ekv.getEntityVersion()));
softDeleteList.getDocument().add(doc);
} else if (softDeleteList.getDocument().size() == deleteList.size()) {
break; // once soft delete list is fully populated, break out of loop to save unnecessary iterations
}
}
}
return softDeleteList;
}
to make use of the java 8 streams,
this is what I tried so far, I think I have the double loops down but the inner conditions as filters might be wrong seems I'm missing quite a bit:
Documents softDeleteList = new Documents();
List<EntityKeyVersion> deleteList = entityManager.bulkDeleteEntities(ekList,EntityClient.DeleteMode.SOFT_DELETE);
return softDeleteList.getDocument().stream()
.filter(doc -> deleteList.stream()
.peek(jh -> )
.anyMatch(ekv -> ekv.getEntityKey().contentEquals(doc.getEntityKey())))
.collect(Collectors.toList());
Any help or assistance would be much appreciated, thanks in advance

Extract all True properties and add to a list

I have a java class with 3 boolean property like this
boolean isActive;
boolean isEnable;
boolean isNew;
every property is related to an enum (e.g. ACTIVE,ENABLE,NEW).
I want to have 2 lists of enum. One which has only the enums related to true property value and one for the false one.
just to be clear. using if-else statement I could have
Set<FlagEnum> flagSet = new HashSet<>();
Set<FlagEnum> falseFlagSet = new HashSet<>();
if (object.isActive()) {
flagSet.add(ACTIVE);
} else {
falseFlagSet.add(ACTIVE);
}
if (object.isEnable()) {
flagSet.add(ENABLE);
} else {
falseFlagSet.add(ENABLE);
}
if (object.isNew()) {
flagSet.add(NEW);
} else {
falseFlagSet.add(NEW);
}
is there a way to avoid all these if-else?
I tried with something like
Map<boolean, List<Pair<boolean, FlagEnum>>> res = Stream.of(
new Pair<>(object.isActive(), ACTIVE),
new Pair<>(object.isNew(), NEW),
new Pair<>(object.isEnable(), ENABLE))
.collect(Collectors.partitioningBy(Pair::getKey));
but the resulted structure is an additional complexity which I would like to avoid.
In my real case, I have more than 15 boolean properties...
You can simplify this in various ways. Which of them make sense, depends on your exact requirements.
You can derive the falseFlagSet trivially from the flagSet using EnumSet.complementOf after populating the flagSet:
EnumSet<FlagEnum> falseFlagSet = EnumSet.complementOf(flagSet);
This assumes that all FlagEnum values have corresponding flags. If that's not the case then you need to construct a EnumSet with all enums that have flags and subtract flagSet from that using removeAll.
#1 already removes the need for the else in your cascade, simplifying the code to
if (object.isActive()) {
flagSet.add(ACTIVE);
}
if (object.isEnable()) {
flagSet.add(ENABLE);
}
if (object.isNew()) {
flagSet.add(NEW);
}
If you have enough different flags, then you can create a mapping from getter method to FlagEnum value like this:
Map<Function<YourClass,Boolean>,FlagEnum> GETTERS = Map.of(
YourClass::isActive, FlagEnum.ACTIVE,
YourClass::isNew, FlagEnum.NEW,
YourClass::isEnable, FlagEnum.ENABLE);
Then you can use this to make the whole process data-driven:
EnumSet<FlagEnum> getFlagSet(YourClass yourObject) {
EnumSet<FlagEnum> result = EnumSet.noneOf(FlagEnum.class);
for (Map.Entry<Function<YourClass,Boolean>, FlagEnum> getter : GETTERS.entrySet()) {
if (getter.getKey().apply(yourObject)) {
result.add(getter.getValue());
}
}
return result;
}
If the number of flags is very big, then you could switch entirely to reflection and detect the flags and matching getters dynamically using string comparison, but I would not suggest that approach. If you need something like that then you probably should switch to a framework that supports that kind of feature and not implement it yourself.
That last two obviously only makes sense when the number of flags is big. If it's actually just 3 flags, then I wouldn't mind and just have 3 simple if statements.
As a slight tangent: GETTERS above should definitely be an immutable map (wrap it in Collections.unmodifiableMap or use something like Guava ImmutableMap) and it could be argued that the same applies to the return value of the getFlagSet method. I've left those out for succinctness.
You can use a private helper method for this.
private void addFlagSet(boolean condition, FlagEnum flagEnum,
Set<FlagEnum> flagSet, Set<FlagEnum> falseFlagSet) {
Set<FlagEnum> chosenFlagSet = condition ? flagSet: falseFlagSet;
chosenFlagSet.add(flagEnum);
}
Call it as:
addFlagSet(object.isActive(), FlagEnum.ACIVE, flagSet, falseFlagSet);
addFlagSet(object.isNew(), FlagEnum.NEW, flagSet, falseFlagSet);
addFlagSet(object.isEnable(), FlagEnum.ENABLE, flagSet, falseFlagSet);
You could probably use Reflection to get all methods, then check if a getReturnType() == boolean.class. Problem is the connection between the method's name and the enum. If every single one is named like the method without the 'is', you could use FlagEnum.valueOf() to retrieve the enum value from the method name and use it.
I think this could be the easiest and clearest way to do what I need
Map<Boolean, Set<FlagEnum>> flagMap = new HashMap<>();
flagMap.computeIfAbsent(object.isActive(), h -> new HashSet()).add(ACTIVE);
flagMap.computeIfAbsent(object.isEnabled(), h -> new HashSet()).add(ENABLE);
flagMap.computeIfAbsent(object.isNew(), h -> new HashSet()).add(NEW);
//to get TRUE set simply :
flagMap.get(true);
what do you think?

Java 8 Stateful consumer

Is conditional composition of Consumers possible in Java 8? Basically I'm looking to create a custom Lambda interface similar to Consumer but that only works with one type of object. Let's call it, Stateful and it contains multiple statuses (we'll say two for the purpose of this example):
public class Stateful {
private int status1;
private int status2;
}
We have a lot of areas in our code where we do an operation on a Stateful and, if the status has changed, we would do another operation. I was wondering if we could use composition to handle this in a more compact and elegant manner. Right now we would do something like:
SimpleEntry<Integer, Integer> oldStates = new SimpleEntry(stateful.getStatus1(), stateful.getStatus2());
applyLogicOnStateful(stateful); //do some operation that may change state values
if(isStatusChanged(oldStates, stateful) { //compare oldStates integers to status integers
doSomethingElse(stateful);
}
where I think something like this would look better:
statefulConsumer
.accept((stateful)->applyLogicOnStateful(stateful))
.ifStatusChanged((stateful)->doSomethingElse(stateful));
but I don't know if we would be able to track the change in status from before the first consumer to after. Maybe I need to create a lambda that takes two consumers as input?
I'm definitely looking to do this without the assistance of a 3rd party library, although you're welcome to promote one here if it is helpful.
Here is a function that will return a Consumer<Stateful> that will extract the former state, do the change, compare results, and conditionally operate on the changed object.
public static Consumer<Stateful> getStatefulConsumer(
Function<Stateful,SimpleEntry<Integer,Integer>> getStatus, // extract status from Stateful
Consumer<Stateful> applyLogic, // apply business logic
BiPredicate<SimpleEntry<Integer,Integer>,SimpleEntry<Integer,Integer>> compareState, // test statuses for change
Consumer<Stateful> onChange) // doSomethingElse
{
return stateful -> {
SimpleEntry<Integer,Integer> oldStatus = getStatus.apply(stateful);
applyLogic.accept(stateful);
if(!compareState.test(oldStatus, getStatus.apply(stateful))){
onChange.accept(stateful);
}
};
}
You might use it like this:
Consumer<Stateful> ifChanged = getStatefulConsumer(s -> new SimpleEntry<> ( s.status1, s.status2 ),
s -> changeSomething(s), Objects::equals, s->doSomething(s));
You could generify the extracted status so that different stateful types could have different extracted status types, or even use Stateful::clone to copy the status.
The solution I am working with right now is to create a Lambda interface that takes the Stateful instance and two Consumers as input:
public interface StatefulConsumer {
void accept(Stateful stateful, Consumer<Stateful> consumer, Consumer<Stateful> ifStateChangedConsumer);
}
and an implementation:
final StatefulConsumer IfStateChanges = new StatefulConsumer() {
#Override
public void accept(Stateful stateful, Consumer<Stateful> consumer, Consumer<Stateful> ifStateChangedConsumer) {
SimpleEntry<Integer, Integer> oldStates = new SimpleEntry(stateful.getStatus1(), stateful.getStatus2());
consumer.accept(stateful); //do some operation that may change state values
if(isStatusChanged(oldStates, stateful) { //compare oldStates integers to status integers
ifStateChangedConsumer.accept(stateful);
}
}
};
which could be called like this:
IfStateChanges.accept(stateful,
(Stateful s)->applyLogicOnStateful(stateful),
(Stateful s)->doSomethingElse(stateful))
It could also be implemented as a Predicate or a Function that takes a stateful and a consumer as input and returns a boolean for use in an if Statement

Is there an elegant way to get the first non null value of multiple method returns in Java?

You have already seen this many times yourself, of that I'm sure:
public SomeObject findSomeObject(Arguments args) {
SomeObject so = queryFirstSource(args); // the most likely source first, hopefully
if (so != null) return so;
so = querySecondSource(args); // a source less likely than the first, hopefully
if (so != null) return so;
so = queryThirdSource(args); // a source less likely than the previous, hopefully
if (so != null) return so;
// and so on
}
We have different sources where an object we search could be. As a more vivid example we could image that we first check if a userid is in a list of privileged users. If not we check if the userid is in the list of allowed users. Else we return null. (It's not the best example but I hope it's a vivid-enough one.)
Guava offers us some helpers that could beautify that code above:
public SomeObject findSomeObject(Arguments args) {
// if there are only two objects
return com.google.common.base.Objects.firstNonNull(queryFirstSource(args), querySecondSource(args));
// or else
return com.google.common.collect.Iterables.find(
Arrays.asList(
queryFirstSource(args)
, querySecondSource(args)
, queryThirdSource(args)
// , ...
)
, com.google.common.base.Predicates.notNull()
);
}
But, as the more experienced among us will have already seen, this may perform bad if the lookups (i.e. queryXXXXSource(args)) take a certain amount of time. This is because we now query all sources first and then pass the results to the method that finds the first among those results which is not null.
In contrast to the first example, where the next source is only evaluated when the former does not return something, this second solution may look better at first but could perform much worse.
Here's where we come to my actual question and to where I suggest something of that I hope someone has already implemented the base of it or of that someone might propose a even smarted solution.
In plain English: Has someone already implemented such a defferedFirstNonNull (see below) or something similar? Is there an easy plain-Java solution to achieve this with the new Stream framework? Can you propose another elegant solution that achieves the same result?
Rules: Java 8 is allowed as well as active maintained and well-known third party libraries like Google's Guava or Apache's Commons Lang with Apache License or similar (No GPL!).
The proposed solution:
public SomeObject findSomeObject(Arguments args) {
return Helper.deferredFirstNonNull(
Arrays.asList(
args -> queryFirstSource(args)
, args -> querySourceSource(args)
, args -> queryThirdSource(args)
)
, x -> x != null
)
}
So the method defferedFirstNonNull would evaluate each lambda expression after another and as soon as the predicate (x -> x != null) is true (i.e. we found a match) the method would return the result immediately and would not query any further source.
PS: I know that the expressions args -> queryXXXXSource(args) could be shortened to queryXXXXSource. But that would render the proposed solution harder to read because it's not obvious on first sight what is going to happen.
Yes, there is:
Arrays.asList(source1, source2, ...)
.stream()
.filter(s -> s != null)
.findFirst();
This is more flexible, since it returns an Optional not null in case a not-null source is found.
Edit: If you want lazy evaluation you should use a Supplier:
Arrays.<Supplier<Source>>asList(sourceFactory::getSource1, sourceFactory::getSource2, ...)
.stream()
.filter(s -> s.get() != null)
.findFirst();
It depends on some factors you are not defining. Do you have a fixed, rather small set of query…Source actions as shown in your question or are you rather heading to having a more flexible, extensible list of actions?
In the first case you might consider changing the query…Source methods to return an Optional<SomeObject> rather than SomeObject or null. If you change your methods to be like
Optional<SomeObject> queryFirstSource(Arguments args) {
…
}
You can chain them this way:
public SomeObject findSomeObject(Arguments args) {
return queryFirstSource(args).orElseGet(
()->querySecondSource(args).orElseGet(
()->queryThirdSource(args).orElse(null)));
}
If you can’t change them or prefer them to return null you can still use the Optional class:
public SomeObject findSomeObject(Arguments args) {
return Optional.ofNullable(queryFirstSource(args)).orElseGet(
()->Optional.ofNullable(querySecondSource(args)).orElseGet(
()->queryThirdSource(args)));
}
If you are looking for a more flexible way for a bigger number of possible queries, it is unavoidable to convert them to some kind of list or stream of Functions. One possible solution is:
public SomeObject findSomeObject(Arguments args) {
return Stream.<Function<Arguments,SomeObject>>of(
this::queryFirstSource, this::querySecondSource, this::queryThirdSource
).map(f->f.apply(args)).filter(Objects::nonNull).findFirst().orElse(null);
}
This performs the desired operation, however, it will compose the necessary action every time you invoke the method. If you want to invoke this method more often, you may consider composing an operation which you can re-use:
Function<Arguments, SomeObject> find = Stream.<Function<Arguments,SomeObject>>of(
this::queryFirstSource, this::querySecondSource, this::queryThirdSource
).reduce(a->null,(f,g)->a->Optional.ofNullable(f.apply(a)).orElseGet(()->g.apply(a)));
public SomeObject findSomeObject(Arguments args) {
return find.apply(args);
}
So you see, there are more than one way. And it depends on the actual task what direction to go. Sometimes, even the simple if sequence might be appropriate.
I would write it like this (you may not need generics here but why not do it):
public static <A, T> Optional<T> findFirst(Predicate<T> predicate, A argument,
Function<A, T>... functions) {
return Arrays.stream(functions)
.map(f -> f.apply(argument))
.filter(predicate::test)
.findFirst();
}
And you can call it with:
return findFirst(Objects::nonNull, args, this::queryFirstSource,
this::querySecondSource,
this::queryThirdSource);
(assuming your queryXXX methods are instance methods)
The methods will be applied in order until one returns a value that matches the predicate (in the example above: returns a non null value).

Categories

Resources