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;
})
Related
I am using a non stream way to get single element from collection.
List<MyCustomClass> list = OtherObject.getMyList();
if (list.size() != 1) {
throw new RuntimeException();
}
MyCustomClass customClass = list.get(0);
Instead of this multi liner approach, is there some way to achieve this via streams?
You can use reduce(accumulator) and orElseThrow(exceptionSupplier) to ensure the stream produces exactly one result.
MyCustomClass customClass = list.stream()
.reduce((a,b) -> { throw new RuntimeException("Too many values present"); })
.orElseThrow(() -> { throw new RuntimeException("No value present"); });
I was looking for a version with a single collect statement, although it turned out not as concise or elegant as the solution by Andreas. It uses an implementation of Collector that accumulates to a one-element list, while the combiner raises an exception if we have more than one element; the finisher raises an exception when the list is empty.
list.stream().collect(
Collector.of( ArrayList::new,
(a, t) -> { if (!a.isEmpty())
throw new RuntimeException();
a.add(t); },
(a, b) -> { throw new RuntimeException(); },
a -> { if( a.isEmpty() )
throw new RuntimeException();
return a.get(0);} );
You could try returning an optional from findFirst() or findAny().
List<String> strings = new ArrayList<>();
Optional<String> maybeFirst = strings.stream().findFirst();
// we now have an optional, lets force a value
String value = maybeFirst.orElseThrow(IllegalArgumentException::new);
// if there isn't a value, we'll throw an illegal argument exception.
This can collapsed into the following.
String value = strings.stream()
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("There must be at least one string."));
Hope that helps.
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));
How do I put/add eServiceReportsMapByBatchFile with key oldReportId to eServiceReportMap without side-effects?
Map<String, Map<String, Set<EServiceReport>>> eServiceReportMap = new HashMap<>();
reports.forEach(report -> {
String oldReportId = report.getOldId();
Map<String, Set<EServiceReport>> eServiceReportsMapByBatchFile = // processing of batch files
...
eServiceReportMap.put(oldReportId, eServiceReportsMapByBatchFile);
});
return eServiceReportMap;
That is, I want it to become like this:
return reports.stream()
.map(report -> {
String oldReportId = report.getOldId();
Map<String, Set<EServiceReport>> eServiceReportsMapByBatchFile = // processing of batch files
...
// I don't know how and what to return here
}).collect(// I don't know what to do here);
Thank you.
You're looking forward mostly to Collectors.toMap which can be used as :
return reports.stream()
.collect(Collectors.toMap(report -> report.getOldId(),
report -> {
// batch processing for eServiceReportsMapByBatchFile
return eServiceReportsMapByBatchFile;
}));
since you call stream on reports, I assume it is a collection of some kind; in such case there is nothing wrong with your side-effects. Notice that someCollection.stream().forEach and someCollection.forEach are very different things, you are more than OK to have side-effects with SomeCollection::forEach - which is nothing but a plain old loop internally.
You could transform that that to a stream solution, but it's going to be a lot less readable:
reports.stream()
.map(r -> {
String oldReportId = report.getOldId();
Map<String, Set<EServiceReport>> eServiceReportsMapByBatchFile =....
return new SimpleEntry<>(oldReportId, eServiceReportsMapByBatchFile);
})
.collect(Collectors.toMap(
Entry::getKey,
Entry::getValue,
(left, right) -> right; // to match whatever you had until now
))
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);
I'm using Java Reactor Core, and I have a reactive Flux of objects. For each object of the Flux I need to do an external query that will return one different object for each input. The newly generated Flux needs then to be zipped with the original one - so the items of the 2 Flux must be synchronized and generated in the same order.
I'm just re-using the same flow twice, like this:
Flux<MyObj> aTest = Flux.fromIterable(aListOfObj);
Flux<String> myObjLists = aTest.map(o -> MyRepository.findById(o.name)).map(o -> {
if (!o.isPresent()) {
System.out.println("Fallback to empty-object");
return "";
}
List<String> l = o.get();
if (l.size() > 1) {
System.out.println("that's bad");
}
return l.get(0);
});
Flux.zip(aTest, myObjLists, (a, b) -> doSomethingWith(a,b))
Is it the right way to do it? If the myObjLists emits an error, how do I prevent the zip phase to skip the failing iteration?
I've finally opted for using Tuples and Optionals (to prevent null-items that would break the flux), so that I don't need to re-use the initial Flux:
Flux<Tuple<MyObj, Optional<String>>> myObjLists = Flux.fromIterable(aListOfObj)
.map(o -> Tuples.of(o, Optional.ofNullable(MyRepository.findById(o.name))
.flatMap(t -> {
if (!t.getT2().isPresent()) {
System.out.println("Discarding this item");
return Flux.empty();
}
List<String> l = t.getT2().get();
if (l.size() > 1) {
System.out.println("that's bad");
}
return Tuples.of(t.getT1(), l.get(0));
})
.map(t -> doSomethingWith(t.getT1(),t.getT2()))
Note that the flatMap could be replaced with a .map().filter(), removing tuples with missing Optional items