I have the following POJO:
public class Order {
private String name;
private String status;
private BigDecimal total;
// getters, setters and ctors down here
}
I am looping through a List<Order> and trying to update all their status fields to a value of "ORDERED". The old (pre Streaming API) way of doing this was:
for (Order order : orders) {
order.setStatus("ORDERED");
}
I'm trying to figure out the Java 8 ("Streaming") way of accomplishing the same thing. My best attempt thus far:
orders.stream().map(order -> order.setStatus("H"));
Produces a compiler error:
"Incompatible types. Required List but 'map' was inferred to Stream: no instance(s) of type variable(s) R exist so that Stream conforms to List"
Any ideas where I'm going awry?
Use forEach:
orders.forEach(order -> order.setStatus("H"));
You do not want to use Stream.map() because it requires a return value which replaces the original value in the stream. You are also missing a terminal operation in your stream, so even if you fix that by returning the original value it wont work. Stream.forEach() is a terminal operation you can use for this.
To update each object in your list you can just use orders.forEach(). This is the same as orders.stream().forEach().
orders.forEach(o -> o.setStatus("H"));
If you want to update only some values of your List you can use Stream.filter() before:
orders.stream()
.filter(o -> "ABC".equals(o.getName())
.forEach(o -> o.setStatus("H"));
Related
Currently, that's my code:
Iterable<Practitioner> referencedPractitioners = this.practitionerRepository.findAllById(
Optional.ofNullable(patient.getPractitioners())
.map(List::stream)
.orElse(Stream.of())
.map(Reference::getIdPart)
.collect(Collectors.toList())
);
As you can see, I'm using this.practitionerRepository.findAllById(Iterable<String> ids), in order to get all using a single communication with database.
I was trying to change it using this:
Optional.ofNullable(patient)
.map(org.hl7.fhir.r4.model.Patient::getPractitioners)
.map(List::stream)
.orElse(Stream.of())
.map(Reference::getIdPart)
.collect(????????);
How could I use this.practitionerRepository.findAllById(Iterable<String> ids) into a custom collector in collect method?
Remember I need to get all entities at once. I can't get them one by one.
You can use Collectors.collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) specialized collector for that.
Make a list of IDs using the Collector.toList() collector and then
Pass a reference practitionerRepository::findAllById to convert from List<String> to Iterable<Practitioner>
Example:
Iterable<Practitioner> referencedPractitioners = Optional.ofNullable(patient)
.map(Patient::getPractitioners)
.map(List::stream)
.orElseGet(Stream::of)
.map(Reference::getIdPart)
.collect(Collectors.collectingAndThen(toList(), practitionerRepository::findAllById));
This question already has answers here:
How to Convert List<String> to List<Object>
(7 answers)
Closed last year.
I have a Collection of Strings which I want to iterate and do a DB query on each of them and collect the response of each query into a Collection of Objects. I am sure we can do this through a for loop iterator but is there a way to do it with Java8 Streams ? This is what I came up with -
static Collection<Action> getActions(Collection<String> actionIds, RequestContext rc) {
List<Collection<Action>> ac = actionIds.stream().map(str -> hashmap.get(str)).collect(Collectors.toList());
return ac.get(0);
}
Action is a custom class. I read that I may need to do something like this - https://itsallbinary.com/java-8-create-custom-streams-collector/ .
Is this necessary ? Or any easier ways ?
If I use this .collect(toCollection(ArrayList::new)), it gives me Collection<Collection<Action>>
It seems that hashmap is a Map<String, Collection<Action>> which can be converted into filtered Collection<Action> using Stream::flatMap, as suggested in the comments.
Possibly it's worth to filter available actionIds as a defensive measure. Also, some specific implementation of the collection needs to be selected or at least Set or List depending on the actual requirements (e.g. if the order of the actions needs to be maintained, or if the duplicate actions need to be filtered, etc.)
Most likely, a mere Collectors::toList would be fine for the result:
static Collection<Action> getActions(Collection<String> actionIds, RequestContext rc) {
return actionIds.stream()
.filter(hashmap::containsKey) // make sure nulls not returned
.map(hashmap::get) // Stream<Collection<Action>>
.flatMap(Collection::stream) // Stream<Action>
.collect(Collectors.toList()); // List<Action>
}
"Unique" values may be retrieved using Stream::distinct before collecting to list or by collecting to a set instead of list (using LinkedHashSet to maintain the order of insertion):
static Collection<Action> getDistinctActions(Collection<String> actionIds, RequestContext rc) {
return actionIds.stream()
.filter(hashmap::containsKey) // make sure nulls not returned
.flatMap(actions -> actions.stream()) // Stream<Action>
.collect(Collectors.toCollection(LinkedHashSet::new));
}
I have this current logic:
List<String> priceUnitCodes = ofNullable(product.getProductPrices())
.map(ProductPrices::getProductPrices)
.flatMap(productPrices -> productPrices.stream()) // << error highlight
.map(ProductPrice::getPriceBase)
.map(PriceBase::getPriceUnit)
.map(UniversalType::getCode)
.collect(Collectors.toList());
Where in IntelliJ the flatMap part is highlighted and shows the following error hint:
no instance(s) of type variable(s) U exist so that Stream<ProductPrice> conforms to Optional<? extends U>
I know that Optionals and Stream are two different things but I wonder if there is a way to combine them so I can follow up an Optional<List<?>> with a Stream afterwards.
If you're on Java 9+, you can use Optional.stream, followed by flatMap:
ofNullable(product.getProductPrices())
.map(ProductPrices::getProductPrices)
.stream()
.flatMap(Collection::stream) //assuming getProductPrices returns a Collection
...
Optional.stream returns an empty stream if the optional is empty.
Since you are starting with an Optional, you have to decide what to return when that Optional is empty.
One way is to put the Stream pipeline inside the Optional's map:
List<String> priceUnitCodes = ofNullable(product.getProductPrices())
.map(ProductPrices::getProductPrices)
.map(productPrices -> productPrices.stream()
.map(ProductPrice::getPriceBase)
.map(PriceBase::getPriceUnit)
.map(UniversalType::getCode)
.collect(Collectors.toList())
.orElse(null);
Or course, if the map operations inside the Stream pipeline may return null, additional changes will be required (to avoid NullPointerException).
On the other hand, if they can never return null, they can be chained into a single map:
List<String> priceUnitCodes = ofNullable(product.getProductPrices())
.map(ProductPrices::getProductPrices)
.map(productPrices -> productPrices.stream()
.map(pp -> pp.getPriceBase().getPriceUnit().getCode())
.collect(Collectors.toList())
.orElse(null);
An alternate solution would be to get the value of the Optional using orElse and this can be done without upgrading to Java-9. It would look like:
List<String> priceUnitCodes = Optional.ofNullable(product.getProductPrices())
.map(ProductPrices::getProductPrices)
.orElse(Collections.emptyList()) // get the value from Optional
.stream()
.map(ProductPrice::getPriceBase)
.map(PriceBase::getPriceUnit)
.map(UniversalType::getCode)
.collect(Collectors.toList());
I'm experiencing a ClassCastException with the following piece of code.
Set<Long> purchaseIds = confirmationCodeToPurchase
.entrySet()
.stream()
.map(Map.Entry::getValue)
.map(purchase -> (Long)purchase.getPurchaseId())
.collect(Collectors.toSet()))
confirmationCodeToPurchase is a map from a confirmation code (String) to a Purchase type.
I'm extracting just the values of the map (the purchases), getting the purchaseId of each, and putting them into a set.
I get this error:
java.lang.ClassCastException: java.lang.Long cannot be cast to Purchase
and the line at which it errors is the collection line. .collect(Collectors.toSet()))
From your error description, it looks like your method purchase.getPurchaseId() returns an object that is not of type Long. That's why you get a class cast exception. Instead of casting, you can build the Long object from the return value of this method, assuming it is either String or an integer type.
Replace
.map(purchase -> (Long)purchase.getPurchaseId())
with
.map(purchase -> Long.valueOf(purchase.getPurchaseId()))
I have a collection of objects, with the following type:
{
String action_name; //add or delete
long action_time;
String action_target;
}
Need to get the latest merged operation on each action_target
Sample input data:
[add|1001|item1, add|1002|item2, delete|1003|item1, add|1004|item1]
Expected result:
[add|1002|item2, add|1004|item1]
Sample input data:
[add|1001|item1, add|1002|item2, delete|1003|item1]
Expected result:
[add|1002|item2]
Sample input data:
[delete|1001|item1, add|1002|item2, add|1003|item1]
Expected result:
[add|1002|item2, add|1003|item1]
Is this approachable using Java8 stream APIs? Thanks.
You want to group by one criteria (the action_target) combined with reducing the groups to the maximum of their action_time values:
Map<String,Item> map=items.stream().collect(
Collectors.groupingBy(item->item.action_target,
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(item->item.action_time)),
Optional::get)));
This returns a Map<String,Item> but, of course, you may call values() on it to get a collection of items.
Beautified with static imports, the code looks like:
Map<String,Item> map=items.stream().collect(groupingBy(item->item.action_target,
collectingAndThen(maxBy(comparing(item->item.action_time)), Optional::get)));
Your additional request of taking care of idempotent "add" and follow-up "delete" actions can be simplified to “remove items whose last action is "delete"” which can be implemented just by doing that after collecting using a mutable map:
HashMap<String,Item> map=items.stream().collect(groupingBy(
item->item.action_target, HashMap::new,
collectingAndThen(maxBy(comparing(item->item.action_time)), Optional::get)));
map.values().removeIf(item->item.action_name.equals("delete"));