I'm making a service call and trying to handle response.
Response might have a list of something. That list might be null.
Moreover, if list not null or not empty, then
it needs to be filtered.
In the code "entry" reference might be null if filtering gives nothing or response list is empty or null.
Currently i'm getting NPE when i try to use stream() on a null response list.
How can i handle this situation?
#Getter
public class ServiceResponse {
List<ResponseEntry> entryList;
}
#Getter
public class ResponseEntry {
String value;
}
ServiceResponse serviceResponse = service.getServiceResponse();
ResponseEntry entry = serviceResponse.getEntryList()
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
if (entry == null) { ... }
if list not null or not empty, then it needs to be filtered.
No need for Optional here, as it's not intended to replace simple if checks.
ResponseEntry entry = null;
List<ResponseEntry> responseEntries = serviceResponse.getEntryList();
if(responseEntries != null && !responseEntries.isEmpty()){
entry = responseEntries.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
}
reads "if responseEntries is not null and responseEntries is not empty then apply the filter operation and find the first item or else null". Very readable.
On the other hand, the optional approach:
ResponseEntry entry = Optional.ofNullable(serviceResponse.getEntryList())
.orElseGet(() -> Collections.emptyList())
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst();
if(!entry.isPresent()){ ... } // or entry.ifPresent(e -> ...) depending on the logic you're performing inside the block
unnecessarily creates objects that could be avoided and not really the intention of optional to be used as a substitute for simple "if" checks.
Stream.ofNullable (Java-9)
Returns a sequential Stream containing a single element, if non-null,
otherwise returns an empty Stream.
Current Code
ResponseEntry entry = serviceResponse.getEntryList() // List<ResponseEntry>
.stream() // NPE here // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter
.findFirst() // Optional<ResponseEntry>
.orElse(null); // or else null
Updated Code
ResponseEntry entry = Stream.ofNullable(serviceResponse.getEntryList()) // Stream<List<ResponseEntry>>
.flatMap(List::stream) // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter here
.findFirst() // Optional<ResponseEntry>
.orElse(null); // or else null
Optional.stream (Java-9)
returns a sequential Stream containing only that value, otherwise
returns an empty Stream.
ResponseEntry entry = Optional.ofNullable(serviceResponse.getEntryList())
.stream() // Stream<List<ResponseEntry>>
.flatMap(List::stream) // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter here
.findFirst() // Optional<ResponseEntry>
.orElse(null); // or else null
Optional.isEmpty(Java-11)
If a value is not present, returns true, otherwise false
Optional<ResponseEntry> entry = Optional.ofNullable(serviceResponse.getEntryList()) // Optional<List<ResponseEntry>>
.orElseGet(Collections::emptyList) // or else empty List
.stream() // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter
.findFirst(); // Optional<ResponseEntry>
if (entry.isEmpty()) { // !entry.isPresent in java-8
// Do your work here
}
In Java 9, you could use the new method Objects.requireNonNullElse(T,T):
Objects.requireNonNullElse(serviceResponse.getEntryList(),
Collections.emptyList())
Apache Commons Collections actually has a method ListUtils.emptyIfNull(List<T>) which returns an empty list if the argument list is null. That's even better, but Objects.requireNonNullElse is the closest thing to it in Java SE.
If you're restricted to just Java 8, then I agree with Aomine's answer that trying to do something like go through Optional is worse than an if statement.
You could simply use the ternary operator:
ServiceResponse serviceResponse = service.getServiceResponse();
List<ResponseEntry> list = serviceResponse.getEntryList();
ResponseEntry entry = (list == null ? Collections.emptyList() : list)
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
if (entry == null) { ... }
Sometimes, traditional is better IMO.
Another option would be to use the Optional monad:
Optional<ResponseEntry> entry = Optional.ofNullable(serviceResponse.getEntryList()).flatMap(list ->
list.stream().filter(e -> "expectedValue".equals(e.getValue())).findFirst()
);
if (!entry.isPresent()) {
…
}
You might even use orElseGet instead of that if statement if your objective is to build (and return) a value, instead of executing a side effect.
I am new to Optional and I may be wrong. Logic can be written like below if you want to have logic including only optional.
ServiceResponse serviceResponse = service.getServiceResponse();
ResponseEntry entry =
Optional.of(CollectionUtils.isNotEmpty(serviceResponse.getEntryList()))
.filter(BooleanUtils::isTrue)
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
Related
Is there a way I could perform a filter within the steps of the following function call?
The following works fine but I wish to add a null/empty check on getResults which returns a list of Results object.
I could try to .stream() on it like getResults().stream().filter();
But kind of pointless since getResults().stream() could potentially throw a null pointer already.
Also this returns a stream of Result. But I want to do a check on the List of Result itself, like:
.filter(CollectionUtils::isNotEmpty)
Is there a way to do this?
public MyFunc(Helper helper) {
Function<String, HttpEntity<Request>> createRequestFunction = helper::createRequest;
this.fetch = createRequestFunction
.andThen(helper::getResponse)
.andThen(Response::getQuery)
.andThen(QueryResult::getResults)
// I want to make a filter here .filter(CollectionUtils::isNotEmpty) to ensure
// getResults (It is a List<Result> type) is not null or empty
.andThen(results -> results.stream()
.map(Result::getKey)
.filter(StringUtils::isNotBlank)
.map(s -> s.split("\\|"))
.filter(data -> data.length == 2)
.map(data -> data[1])
.collect(Collectors.toList()));
}
createRequestFunction has type of Function<String, HttpEntity<Request>>
Function itself doesn't have a filter operation. It is possible to either append additional function via andThen or prepend via compose.
What is expected behaviour if QueryResult::getResults is null? What is the value of this.fetch should be?
One possible option is to wrap result of QueryResult::getResults into an Optional. Something similar to this:
this.fetch = createRequestFunction
.andThen(helper::getResponse)
.andThen(Response::getQuery)
.andThen(QueryResult::getResults)
.andThen(Optinal::ofNullable);
so the result of this.fetch is Optinal<List<Result>> and it is possible to execut different logic based on the fact if optional is empty or not.
e.g
return
this.fetch // Optinal<List<Result>>
.getOrElse(Collections.emptyList()) // value of optional if it is not empty, or empty list otherwise
.stream()
.map(Result::getKey)
.filter(StringUtils::isNotBlank)
.map(s -> s.split("\\|"))
.filter(data -> data.length == 2)
.map(data -> data[1])
.collect(Collectors.toList())
Java 8 here. I need to search two lists of POJOs for a string and want to use the Stream/Optional APIs correctly.
If the name appears in the first list ("lunches") then I want to return an optional containing it. Else, if the name appears in the second list ("dinners") then I want to return an optional containing it. Otherwise I want to return Optional.empty() if the name doesn't existing in either list. My best attempt thus far:
public class Restaurant {
private String id;
private String name;
private List<Food> lunches;
private List<Food> dinners;
public Optional<Food> findFoodByName(String name) {
return Optional.of(lunches.stream()
.filter(food -> food.getName()
.equalsIgnoreCase(name))
.findFirst())
.orElse(dinners.stream()
.filter(food -> food.getName()
.equalsIgnoreCase(name))
.findFirst());
// .orElse(null); TODO: how to return empty optional if neither in 'lunches' nor 'dinners'?
}
}
Can anyone help me cross the finish line here?
Combine both the list using Stream.of and check for element or return Optional.empty()
Stream.of(lunches, dinners)
.flatMap(List::stream)
.filter(s -> s.getName()
.equalsIgnoreCase(name))
.findFirst();
As per the suggestion from #Holger you can also use Stream.concat to concat two streams and then check for element
Stream.concat(lunches.stream(), dinners.stream())
.filter(s -> s.getName()
.equalsIgnoreCase(name))
.findFirst();
You can do like this too:
Optional<Food> firstTry = lunches.stream()
.filter(f -> f.getName().equalsIgnoreCase(name))
.findFirst();
return firstTry.map(Optional::of)
.orElse(dinners.stream()
.filter(f -> f.getName().equalsIgnoreCase(name)).findFirst());
Or in Java9
firstTry.or(() -> dinners.stream().filter(s -> s.equalsIgnoreCase(name)).findFirst());
As #Slaw commented correctly use of orElseGet() avoid eagerly computing.
Optional<Food> firstTry = lunches.stream().filter(...)...findFirst();
Supplier<Optional<Food>> secondTry = () -> dinners.stream()...findFirst();
and at the end
return firstTry.map(Optional::of).orElseGet(secondTry);
To improve performance I want to use the same variable in both filter() and map() of a Java 8 stream.
Example-
list.stream()
.filter(var -> getAnotherObject(var).isPresent())
.map(var -> getAnotherObject(var).get())
.collect(Collectors.toList())
The called method getAnotherObject() looks like-
private Optional<String> getAnotherObject(String var)
In the above scenario I have to call the method getAnotherObject() twice.If I go with a regular for loop then I have to call the method getAnotherObject() only once.
List<String> resultList = new ArrayList<>();
for(String var : list) {
Optional<String> optionalAnotherObject = getAnotherObject(var);
if(optionalAnotherObject.isPresent()) {
String anotherObject = optionalAnotherObject.get();
resultList.add(anotherObject)
}
}
Even with stream I can put all my code in map()-
list.stream()
.map(var -> {
Optional<String> anotherObjectOptional = getAnotherObject(var);
if(anotherObjectOptional.isPresent()) {
return anotherObjectOptional.get();
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
But I believe there must be an elegant way using filter().
You can create a stream like this
list.stream()
.map(YourClass::getAnotherObject)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
YourClass refer to the name of the class where getAnotherObject method is defined
You can use flatMap. Usually this is used to flatten stuff, but here you can
map the element to that element if the optional has a value
map the element to an empty stream if the optional has no value
Like this:
stream
.map(x -> getAnotherObject(x))
.flatMap(x -> x.map(Stream::of).orElse(Stream.of())))
Our objects have "properties"; and their current state is represented as Map<String, Object> where the key resembles the name of the property. The values can have different types, my current task is only dealing with Boolean properties though.
Beyond the current status, also "updates" to objects are organized via such maps.
Now I have to prevent that a property that is currently true gets disabled (turned to false).
Using streams, this here works:
Set<String> currentlyEnabled = currentObjectPropertiesMap.
.entrySet()
.stream()
.filter(e -> Boolean.TRUE.equals(e.getValue()))
.map(Entry::getKey)
.collect(Collectors.toSet());
Set<String> goingDisabled = updatedObjectPropertiesMap
.entrySet()
.stream()
.filter(e -> Boolean.FALSE.equals(e.getValue()))
.map(Entry::getKey)
.collect(Collectors.toSet());
currentlyEnabled.retainAll(goingDisabled);
if (currentlyEnabled.isEmpty()) {
return;
} else {
throw new SomeExceptionThatKnowsAllBadProperties(currentlyEnabled);
}
The above code first fetches a set of all properties that are true, then it separately collects all properties that will turn false. And if the intersection of these two sets is empty, I am fine, otherwise error.
The above works, but I find it clumsy, and I dislike the fact that the currentlyEnabled set is misused to compute the intersection.
Any suggestion how this can be done in a more idiomatic, but readable "stream-ish" way?
You can just select all key-value-pairs whose value is true, and then via the key, check if the value from the "update"-map is false.
Set<String> matches = currentObjectPropertiesMap
.entrySet()
.stream()
.filter(e -> Boolean.TRUE.equals(e.getValue()))
.map(Map.Entry::getKey)
.filter(k -> Boolean.FALSE.equals(
updatedObjectPropertiesMap.get(k)
))
.collect(Collectors.toSet());
if(!matches.isEmpty()) throw ...
One solution that does not include explicit set intersection could be:
Set<String> violatingProperties = new HashSet<String>();
for (Entry<String, Object> entry : currentObjectPropertiesMap.entrySet()) {
if (! (Boolean) entry.getValue()) {
continue;
}
if (! updatedObjectPropertiesMap.hasKey(entry.getKey())) {
continue;
}
if (! (Boolean) updatedObjectPropertiesMap.get(entry.getKey())) {
violatingProperties.add(entry.getKey());
}
}
if (violatingProperties.size() > 0) {
throw ...
}
Try anyMatch
boolean anyMatch = currentXXXMap.entrySet()
.stream()
.anyMatch(e -> e.getValue() && !updatedXXXMap.getOrDefault(e.getKey(), true));
I want convert a piece of code from a Connection Pool project i have been working on to use streams
the original code is
for (Map.Entry<JdbConnection,Instant> entry : borrowed.entrySet()) {
Instant leaseTime = entry.getValue();
JdbConnection jdbConnection = entry.getKey();
Duration timeElapsed = Duration.between(leaseTime, Instant.now());
if (timeElapsed.toMillis() > leaseTimeInMillis) {
//expired, let's close it and remove it from the map
jdbConnection.close();
borrowed.remove(jdbConnection);
//create a new one, mark it as borrowed and give it to the client
JdbConnection newJdbConnection = factory.create();
borrowed.put(newJdbConnection,Instant.now());
return newJdbConnection;
}
}
throw new ConnectionPoolException("No connections available");
I have got to the point of this
borrowed.entrySet().stream()
.filter(entry -> Duration.between(entry.getValue(), Instant.now()).toMillis() > leaseTimeInMillis)
.findFirst()
.ifPresent(entry -> {
entry.getKey().close();
borrowed.remove(entry.getKey());
});
JdbConnection newJdbConnection = factory.create();
borrowed.put(newJdbConnection,Instant.now());
return newJdbConnection;
The above can compile but the moment i add orElseThrow after IfPresent i am getting the following
/home/prakashs/connection_pool/src/main/java/com/spakai/ConnectionPool.java:83: error: void cannot be dereferenced
.orElseThrow(ConnectionPoolException::new);
That's because ifPresent returns void. It can't be chained. You could do something like:
Entry<JdbConnection, Instant> entry =
borrowed.entrySet().stream()
.filter(entry -> Duration.between(entry.getValue(), Instant.now())
.toMillis() > leaseTimeInMillis)
.findFirst()
.orElseThrow(ConnectionPoolException::new));
entry.getKey().close();
borrowed.remove(entry.getKey());
What you were looking for would read well:
.findFirst().ifPresent(value -> use(value)).orElseThrow(Exception::new);
But for it to work, ifPresent would have to return the Optional, which would be a little odd. It would mean you could chain one ifPresent after another, doing multiple operations on the value. That might have been a good design, but it isn't the one the creators of Optional went with.
Use map instead of isPresent, and return with an Optional instead of an exception.
borrowed.entrySet().stream()
.filter(entry -> Duration.between(entry.getValue(), Instant.now()).toMillis() > leaseTimeInMillis)
.findFirst()
.map(entry -> {
entry.getKey().close();
borrowed.remove(entry.getKey());
JdbConnection newJdbConnection = factory.create();
borrowed.put(newJdbConnection,Instant.now());
return newJdbConnection;
})