One area that I often finding confusing with java 8 streams is when an intermediate result can be empty, and you need to take alternate paths if it's empty or not empty.
For instance, if I have code like this:
String pymtRef = defaultValue;
Optional<PaymentTender> paymentTender = paymentTenders.stream()
.filter(pt -> (pt.getFlag() == Flag.N || pt.getFlag() == null)).findFirst();
if (paymentTender.isPresent()) {
pymtRef = paymentTender.get().getId();
}
return pymtRef;
I would like to figure out how to remove the conditional block and do this in a single stream.
If I simply call ".map" on the filter result, that can work if it found a matching entry. If not, I get a NoSuchElementException.
I might instead use "ifPresent()", but the return type of that is "void".
Is there any way to make this cleaner?
Update:
The solution using "orElse()" works fine.
The entire method now looks something like this:
public String getPaymentReference(OrderContext orderContext) {
List<PaymentTender> paymentTenders = getPaymentTenders(orderContext);
if (paymentTenders.size() == 1) {
return paymentTenders.get(0).getId();
}
return paymentTenders.stream()
.filter(pt -> (pt.getAutoBill() == AutoBill.N || pt.getAutoBill() == null))
.findFirst().map(pt -> pt.getId()).orElse(DEFAULT_VALUE);
}
Can you think of a way to include the first conditional in the stream without making it more complex?
Calling get() straight after map will yield an exception if the Optional has an empty state, instead call orElse after map and provide a default value:
paymentTenders.stream()
.filter(pt -> (pt.getFlag() == Flag.N || pt.getFlag() == null))
.findFirst()
.map(PaymentTender::getId)
.orElse(someDefaultValue);
Edit:
As for:
Can you think of a way to include the first conditional in the stream
without making it more complex?
No, this is better the way you've done it. it's more readable and easier to follow.
introducing any type of logic to make it into one pipeline (if possible) will just end of being complex and hence harder to follow and understand.
You can do it in one statement via
public String getPaymentReference(OrderContext orderContext) {
List<PaymentTender> paymentTenders = getPaymentTenders(orderContext);
return paymentTenders.stream()
.filter(paymentTenders.size() == 1? pt -> true:
pt -> pt.getAutoBill() == AutoBill.N || pt.getAutoBill() == null)
.findFirst().map(PaymentTender::getId).orElse(DEFAULT_VALUE);
}
Note that this will not repeat the evaluation of the paymentTenders.size() == 1 for every element, but use a different function, depending on the state. When the condition is fulfilled, pt -> true will accept any element, which will result in the sole element being accepted as intended. Otherwise, the ordinary predicate, pt -> pt.getAutoBill() == AutoBill.N || pt.getAutoBill() == null is used.
Related
retList.sort((comp1, comp2) ->
compartmentOrderMap.get(comp2.getCompartment()).compareTo(compartmentOrderMap
.get(comp1.getCompartment())));
I want to add a null check before comparing. How can I do that?
retList.sort((comp1, comp2) ->
if(compartmentOrderMap.get(comp2.getCompartment()) != null && compartmentOrderMap.get(comp1.getCompartment()) != null)
compartmentOrderMap.get(comp2.getCompartment()).compareTo(compartmentOrderMap
.get(comp1.getCompartment()));
);
//I want to do something like this
Your operation
retList.sort((comp1, comp2) ->
compartmentOrderMap.get(comp2.getCompartment())
.compareTo(compartmentOrderMap.get(comp1.getCompartment())));
is equivalent to
retList.sort(Comparator.comparing(
c -> compartmentOrderMap.get(c.getCompartment()),
Comparator.reverseOrder()));
With this factory based form, you can easily replace the value comparator with a null safe variant, e.g.
retList.sort(Comparator.comparing(
c -> compartmentOrderMap.get(c.getCompartment()),
Comparator.nullsFirst(Comparator.reverseOrder())));
You have to decide for a policy. Instead of nullsFirst you can also use nullsLast.
you have to put {} inside the lambda for multiple line code:
retList.sort((comp1, comp2) -> {
if(compartmentOrderMap.get(comp2.getCompartment()) != null && compartmentOrderMap.get(comp1.getCompartment()) != null)
return compartmentOrderMap.get(comp2.getCompartment()).compareTo(compartmentOrderMap
.get(comp1.getCompartment()));
else
// throw a RuntimeException or return some integer value based on your logic
});
Use if/then/else to specify your needs. If you want all of this within one line, check the ternary operator on
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html
It is explained including some examples here:
https://www.tutorialspoint.com/Java-Ternary-Operator-Examples
boolean isRoleOld,isRoleNew;
for (Relations relation : listOfRelations)
{
if (Constants.ROLE_OLD.equalsIgnoreCase(relation.getRole()))
{
isRoleOld = true;
}
if (Constants.ROLE_NEW.equalsIgnoreCase(relation.getRole()))
{
isRoleNew = true;
}
}
if (isRoleOld && isRoleNew)
{
“Success”
}else{
throw Exception();
}
What i have done yet is
if (listOfRelations.stream()
.anyMatch(relation -> Constants.ROLE_OLD.equalsIgnoreCase(relation.getRole()))
&&
listOfRelations.stream()
.anyMatch(relation -> Constants.ROLE_NEW.equalsIgnoreCase(relation.getRole())))
{
System.out.println("Success");
}
How to use streams from Java8 to optimize this code. Using a anymatch twice is not the point.
Any help will be appreciated.
You could use a stream to map to each Role, and filter to identify old/new role match, then count the distinct matches.
long count = listOfRelations.stream().map(Relations::getRole)
.filter(role -> Constants.ROLE_OLD.equalsIgnoreCase(role)
|| Constants.ROLE_NEW.equalsIgnoreCase(role))
.map(String::toLowerCase)
.distinct().count();
if (count != 2) {
throw new Exception();
}
System.out.println("Success");
However although this does only one pass through the stream it does not exit early once matched both roles so won't be ideal for large data-sets.
Actually, the solution with two .anyMatch checks is better because of short-circuiting when the required value is matched and therefore these checks may usually complete "sooner" than a full single run over the entire stream. In case ROLE_OLD value is missing, && short-circuiting occurs and no check for ROLE_NEW is executed. The worst case is when ROLE_OLD is located at the end of the input listOfRelations, and no ROLE_NEW is there.
Similar loop-based solution would use break as soon as both ROLE_OLD and ROLE_NEW have been detected; in the worst case, the collection is fully iterated one time.
List<Mt4Strategy> openStrategies = ...
OrderType sample = openStrategies.get(0).calculate().getOrderType();
boolean success = true;
for (int i = 1; i < openStrategies.size(); i++) {
Mt4Action calculate = openStrategies.get(i).calculate();
if (calculate.getOrderType() != sample) {
success = false;
break;
}
}
OrderType is an enum.
I don't know what the first element contains and as a result am forced to make openStrategies.get(0).... I want to get rid of this get(0), but how?
I tried to use lambda like this:
OrderType sample = openStrategies.get(0).calculate().getOrderType();
boolean success = IntStream.range(1, openStrategies.size()).mapToObj(i ->
openStrategies.get(i).calculate()).noneMatch(calculate ->
calculate.getOrderType() != sample);
It's a good start but does not resolve my get(0).
Can using a lambda get rid of it? How I can write this to check success without get(0)? Lambda solution in priority something similar to last case .noneMatch.
You apparently want to determine whether all the input list elements have the same order type. A stream ought to make this pretty simple. For example,
boolean success = openStrategies.stream()
.map(s -> s.calculate().getOrderType())
.distinct()
.limit(2)
.count() == 1;
Note here the distinctness comparisons are done with equals rather than ==. If you really need ==, it's more difficult.
This checks for exactly one value in the list. If the input can be empty and you want the result to be true in that case, change == 1 to <= 1.
The limit(2) isn't needed for correctness but allows the search to stop as soon as a second distinct value is found, so it's more efficient.
There are other ways to do this.
Responding to comment
There are various hacky ways you could get the common value without calling .get(0), but none that would be clearer (at least that I can think of). It's silly to code things in oddball ways just to avoid a call you don't like the looks of.
is some elegant way to find and modify some specific object in java? I have method like that:
public update(MyObj o) {
for (MyObj objToModify: DATA) {
if (objToModify.getId() == o.getId()) {
objToModify.setName(o.getName());
// and so on ...
}
}
}
Is possible to rewrite to lambda for example, or some other feature of Java 8? I had a lot of properties so I will prefer some option where I couldn't write manually set up all of new properties.
You can do it in the following way, this will go over the whole stream and update elements even if there is more then one matching:
DATA.stream().filter(a -> a.getId() == o.getId()).forEach(a -> a.setName(o.getName()));
Or if you are sure that you only need to update one element:
DATA.stream().filter(a -> a.getId() == o.getId()).
findAny().ifPresent(a -> a.setName(o.getName()));
Both solutions will throw NullPointerException if DATA has null elements the same as your original solution, if it's a posiibility and you want to prevent it you need to also check that a is not null in filter.
You can use lambda expression to do what you are trying to do
public update(MyObj o) {
DATA.forEach(objToModify -> {
if (objToModify.getId() == o.getId()) {
objToModify.setName(o.getName());
// and so on ...
}
});
}
although i am not sure if using lambda expression in your case will be more efficient than using for-each loop.
If I wish to perform two checks on a string, that it is not null, and that it is not 0 length, I could do this-
if(string != null) {
if(string.length() > 0) {
//Do something
}
}
Or I could do this
if(string != null && string.length() > 0) {
//Do something
}
As the first check is executed first, the second comparison doesn't happen and a NullPointerException isn't thrown.
Is the second method guaranteed to work in all cases? And if so, would it be considered bad practice to use it?
No, it is perfectly fine and guaranteed to work in all cases.
Reference from Java specification: https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7
The one worth mentioning here: as you write such checks very often, it is advisable to put them into some dedicated helper method; such as:
static boolean doesStringContentContent(String value) {
or something alike.
And from an "improved" readability perspective, you might prefer coding that like
if (value == null) return false;
if (value.isEmpty()) return false;
return true;
But that doesn't matter too much - the important part is that you should not start copying around this check.
And finally, the one other possibility would to not return a boolean, but to have a
void checkStringHasContent(String value) {
which could throw a NullPointerException resp. some other thingy for an empty string.
you are using && (and) operator in second expression. and nested if condition in first expression. in both cases you will get same result. because &&(and) operator execute second condition if only if first condition is true.
basically you are doing same thing in different manner.