How to write it using streams? Java 8 - java

I wrote a piece of code and wonder how I can write it more elegant, using streams
here it is:
public boolean possibleToAddTask(LocalDate taskDate, final String username) {
List<Task> userTasklist = find(username).getTaskList();
for(Task task : userTasklist) {
if(task.getDate().equals(taskDate)){
return false;
}
}
return true;
}
Here - some boolean is returned from a method. If specified date already exists in some task it returns false, otherwise true (so the return type answers the question raised in method's name :))
I was trying with filters on streams, but It worked just for a while, and then unit tests gave me some unexpected results so I deleted it and wrote it like Its upper. Now I want to beautify it
previously it was like this:
public boolean possibleToAddTask(LocalDate taskDate, final String username) {
List<Task> userTasklist = find(username).getTaskList();
try {
userTasklist.stream().filter(n -> n.getDate().equals(taskDate)).findFirst().get();
return true;
} catch (NoSuchElementException e) {
return false;
}
}
thanks in advance :)

Method findFirst() return an Optional. So you can just check if optional is empty.
return !userTasklist.stream()
.filter(n -> n.getDate().equals(taskDate))
.findFirst().isPresent();
Or even easier approach.
return !userTasklist.stream().anyMatch(n -> n.getDate().equals(taskDate));
EDIT: Now unit tests should pass.

How about doing something lik transforming the List into Set and then calling contains():
return userTasklist.stream().map(Task::getDate).collect(Collectors.toSet()).contains(taskDate);

Related

Same condition checked more time consecutively

I have to assign the result of an operation (a db query or a computation) to a return variable, and eventually perform another operation if the result is not valid (null, empty, ...); however, this pattern happen at least a couple of time, and it seems a code smell to me. Should I refactor it in any way?
String result = planA();
if(!isValid(result))
result = planB();
if(!isValid(result))
result = planC();
if(!isValid(result))
result = planD();
return result;
You could factor out that as a function that returns the first valid result since that is that you actually do.
In fact, in terms of expected behavior, your current code could be written like :
String result = planA();
if(isValid(result))
return planB();
if(isValid(result))
return planC();
if(isValid(result))
return planD();
return result;
You can factor out that with a function :
public String computeResult(Supplier<String>... functions){
String result = null;
for (Supplier<String> function : functions){
result = function.get();
if (isValid(result)){
return result;
}
}
return result;
}
And call that :
computeResult(this::planA, this::planB, this::planC, this::planD, ()-> this.planE(anyParam));
As alternative you could replace the loop by a stream in the function :
public String computeResult(Supplier<String>... functions){
return
Arrays.stream(functions)
.filter(s-> isValid(s.get()))
.findFirst()
.orElse(functions.get(function.length-1));
// if no valid return the last result function as in the initial code
}
If I understand it correctly, planD is the default case if all previous checks failed. I would write it like this
return Stream.of(planA(),planB(),planC())
.dropWhile(s->!isValid(s))
.findFirst()
.orElse(planD());
I would implement a util class that can be used by anyone for any purpose:
public static <T> T computeWithFallbacks(Predicate<T> validityPredicate, Supplier<T>... suppliers) {
T result = null;
for (Supplier<T> supplier : suppliers) {
result = supplier.get();
if (validityPredicate.test(result)) {
return result;
}
}
return result;
}
This way you can provide the validity check predicate and a varargs of value suppliers that will be computed in the given order

For Loop to Java 8 Stream forEach() [duplicate]

This question already has answers here:
Break or return from Java 8 stream forEach?
(14 answers)
Closed 4 years ago.
I have a for loop for a list that checks whether or not index values exist in the db.
Simply if any value doesn't exist, it immediately returns false.
public boolean exists(List<String> keys) {
for(String key: keys) {
boolean exists = service.existsByKey(key);
if(!exists) return false;
}
return true;
}
I tried to change it to java 8 foreach, but it doesn't work as expected.
keys.stream().forEach(k -> {
boolean exists = service.existsByKey(k);
if(!exists) return false;
});
Am I missing something? Why doesn't it go in if(!exists) staetment in forEach()?
Your return statements in forEach method are ignored.
Try to use
boolean exists = key.stream().allMatch(k -> service.existsByKey(k));
You cannot return a value with the forEach construct as it accepts a consumer i.e. a function that takes one parameter and returns nothing(void), instead, you can use allMatch as shown in the other answer ornoneMatch like this:
return keys.stream()
.noneMatch(key -> !service.existsByKey(key))
You use lambda that is mostly a shortcut for anonymous class.
So your code is equivalent to:
keys.stream().forEach(new Consumer<String>() {
#Override
public void accept(String s) {
boolean exists = service.existsByKey(k);
if(!exists) return false;
}
});
It doesn't return from your method (actually it doesn't compile also).

Java 8 optional: ifPresent return object orElseThrow exception

I'm trying to make something like this:
private String getStringIfObjectIsPresent(Optional<Object> object){
object.ifPresent(() ->{
String result = "result";
//some logic with result and return it
return result;
}).orElseThrow(MyCustomException::new);
}
This won't work, because ifPresent takes Consumer functional interface as parameter, which has void accept(T t). It cannot return any value. Is there any other way to do it ?
Actually what you are searching is: Optional.map. Your code would then look like:
object.map(o -> "result" /* or your function */)
.orElseThrow(MyCustomException::new);
I would rather omit passing the Optional if you can. In the end you gain nothing using an Optional here. A slightly other variant:
public String getString(Object yourObject) {
if (Objects.isNull(yourObject)) { // or use requireNonNull instead if NullPointerException suffices
throw new MyCustomException();
}
String result = ...
// your string mapping function
return result;
}
If you already have the Optional-object due to another call, I would still recommend you to use the map-method, instead of isPresent, etc. for the single reason, that I find it more readable (clearly a subjective decision ;-)).
Two options here:
Replace ifPresent with map and use Function instead of Consumer
private String getStringIfObjectIsPresent(Optional<Object> object) {
return object
.map(obj -> {
String result = "result";
//some logic with result and return it
return result;
})
.orElseThrow(MyCustomException::new);
}
Use isPresent:
private String getStringIfObjectIsPresent(Optional<Object> object) {
if (object.isPresent()) {
String result = "result";
//some logic with result and return it
return result;
} else {
throw new MyCustomException();
}
}
Use the map-function instead. It transforms the value inside the optional.
Like this:
private String getStringIfObjectIsPresent(Optional<Object> object) {
return object.map(() -> {
String result = "result";
//some logic with result and return it
return result;
}).orElseThrow(MyCustomException::new);
}
I'd prefer mapping after making sure the value is available
private String getStringIfObjectIsPresent(Optional<Object> object) {
Object ob = object.orElseThrow(MyCustomException::new);
// do your mapping with ob
String result = your-map-function(ob);
return result;
}
or one liner
private String getStringIfObjectIsPresent(Optional<Object> object) {
return your-map-function(object.orElseThrow(MyCustomException::new));
}
The example u put is not a good example. Optional shouldn't be sent as parameter into another function. Good practice is always sending non-null parameter into function. So that we know always that the input will not be null. This can decrease our code uncertainty.

java-8 optional double checking

I like java-8's optional chaning style.
So I want check double null.
class A {
public String getSome() {
return ""; // some string
}
}
class B {
public String getSome() {
return ""; // some string
}
}
class T {
A a;
B b;
public String result() {
if (a.getSome() != null) {
if (b.getSome() != null) {
return a+b;
} else {
throw new RuntimeException();
}
} else {
throw new RuntimeException();
}
}
}
How can I convert T.result() to Optional Style?
I tried this style but IDE told me 'cyclic interface'.
public String result() {
return Optional.ofNullable(a.getSome())
.map(a -> {
return Optional.ofNullable(b.getSome())
.map(b -> {
return a + b;
})
.orElseThrow(RuntimeException::new);
})
.orElseThrow(RuntimeException::new);
}
While #Eran gave a possible solution, I don't think you add simplicity by using chaining and Optionals.
The new Java 8 API and features must not be a replacement for all pre-Java 8 code. There's a lot of questions for example about using Stream to perform some tasks while a simple for loop would do the trick.
In your case since you only want to check if the reference is not null, simply do:
public String result() {
return Objects.requireNonNull(a.getSome()) + Objects.requireNonNull(b.getSome());
}
This should be much simpler :
public String result() {
return Optional.ofNullable(a.getSome()).orElseThrow(RuntimeException::new) +
Optional.ofNullable(b.getSome()).orElseThrow(RuntimeException::new);
}
And if you change the getSome methods to return an Optional<String>, the result method would be even simpler :
public String result() {
return a.getSome().orElseThrow(RuntimeException::new) +
b.getSome().orElseThrow(RuntimeException::new);
}
However, if it's possible that either a or b themselves would be null, you need some extra code to handle that.
Rewriting your method in what you call "Optional style" would result in the following monstrosity:
Optional.ofNullable(a.getSome())
.flatMap(x ->
Optional.ofNullable(b.getSome()).map(x::concat)
).orElseThrow(RuntimeException::new)
Why in the world would you want to do this? You are trying to solve a problem that doesn't exist. Please just follow ZouZou's suggestion and use requireNonNull, but put each requireNonNull in a separate line to make stack traces easier to decipher.
What your IDE complained about is probably your use of a and b as both variables in the method and also as parameter names in the lambdas. You are not allowed to do that.

Error with return in Java?

I get an error in the code from this part of my code:
public boolean findCustomer(String inPersonalNumber){
// check if personal number already exist
for (int i=0; i<customerList.size();i++) {
if(customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)){
return true;
}
}
return true;
}
When I remove the first return true and instead to the last return true, it don't get the error in my eclipse code, but why can't I have the first place and would this be the same? Thanks!
EDIT: The error message from eclipse say: This method must return a result of type boolean. I'm confused because isn't that what I have done?!
Yes, a break must be in the code
Can I write the method in some other way?
EDIT NUMBER 2
Why isn't this code working?
public boolean findCustomer(String inPersonalNumber){
// check if personal number already exist
for (int i=0; i<customerList.size();i++) {
if(customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)){
return true;
}
else {
return false;
}
}
}
This method returns a boolean value so I don't understand why I get an error!? The code looks right to me?
Your edit #2 doesn't compile because there is a possibility that your code won't enter the for-loop. This will be the case if customerList.size() is 0. To fix this, you'll simply need to add a return statement after the for-loop as well:
// check if personal number already exist
for (int i=0; i<customerList.size();i++) {
if(customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)){
return true;
}
else {
return false;
}
}
return false;
Another point here is that this code doesn't logically make much sense: it will only return true or false based on the first item in your list. And this is probably not what you want. So take a closer look at several of the other answer here, many of which are good examples for how you can do this.
public boolean findCustomer(String inPersonalNumber){
boolean result = false;
// check if personal number already exist
for (int i=0; i<customerList.size();i++) {
if(customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)){
result = true;
break;
}
}
return result ;
}
When I remove the first return true and instead to the last return
true, it don't get the error in my eclipse code, but why can't I have
the first place and would this be the same?
If you remove the second return statement the code would be able to run and not return a value - this is not possible as you defined the method to have a return type of Boolean. So it must always return a value no matter what.
Just change the second return statement to false, should do what you want.
Looks like you have turned off the Build Automatically feature of eclipse. It maybe complaining about an error that used to be present when you still hadn't typed in your code fully! This can also happen if you have back-dated your system for some reason.
Also, shouldn't you be returning false if the condition doesn't satisfy?
public boolean findCustomer(String inPersonalNumber) {
// check if personal number already exist
for (int i = 0; i < customerList.size(); i++) {
if (customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)) {
return true;
}
}
return false;
}
First return will return only in case of all conditions satisfied, but this method should be returning boolean as per code. It would be expecting a return in failure case also.
Removing first return won't affect compilation as it has a return in second place which will work without any condtions.
Edit : Answer for your second question
This code has two return's, but what if your customerList is size 0, in that case also, method must return boolean. right? for that only, compiler is asking.
BTW, code doesn't have null checks.
Your final code could be this. Keeping multiple return statements in code in not a good practice.
public boolean findCustomer(String inPersonalNumber) {
boolean retVal = false;
if (!(inPersonalNumber == null || inPersonalNumber.trim().equals("")
|| customerList == null || customerList.size() == 0)) { // inputs are valid to run this check
// check if personal number already exist
for (int i = 0; i < customerList.size(); i++) {
if (inPersonalNumber.equals(customerList.get(i).getCustomerPersonalNumber()) { // to avoid NPE, kept inPersonalNumber in check
retVal = true;
break;
}
}
}
return retVal;
}
Because your for loop looses meaning if you're returning true anyway.
If you want to stop loop use break; instead of first return.

Categories