I have the following class:
@AllArgsConstructor
@Getter
@Setter
public static class Manipulate {
private int id;
private int quantity;
}
And I have two lists a
and b
.
List<Manipulate> a = new ArrayList<>();
a.add(new Manipulate(1,100));
a.add(new Manipulate(2,200));
List<Manipulate> b = new ArrayList<>();
b.add(new Manipulate(1,10));
b.add(new Manipulate(2,20));
I need to filter these two lists based on the id
property.
And I want to subtract quantities of objects contained in b
from quantities of objects contained in a
and store the result into a List
.
My attempt:
List<Manipulate> c = a.stream().map(k -> {
b.stream().filter(j -> j.getId() == k.getId())
.forEach(i -> {
int i1 = k.getQuantity() - i.getQuantity();
k.setQuantity(i1);
});
return k;
});
I'm getting the following compilation error:
Required type: List <Manipulate> Provided: Stream<Object>
no instance(s) of type variable(s) R exist so that Stream<R> conforms to List<Manipulate>
There are several issues with your code:
map()
is an intermediate operation , it means that it doesn't produce the resulting value but returns another stream. In order to produce a result from the stream you need to apply a terminal operation (eg collect()
, reduce()
, findFirst()
). For more information, refer to the API documentation .
In Functional programming , it's not a good practice to mutate arguments passed to a function (and that's what you're doing inside the map()
).
Your code is based on a brute-force logic (which always imply the worst possible performance): for every element in a
iterate over the all elements in b
. Instead, we can index all the id
that are present in the list b
by placing them into a hash-based collection (that would allow to find out whether a particular id
is present in the b
in constant time) and associate each id
with the corresponding quantity
. Ie we can generate HashMap
, that maps each id
in the b
to it's quantity
.
Lists a
and c
would be identical because they would contain the same references. That means there's no point in generating the list c
unless you want it to contain only elements having id
that are present in the list b
.
That's how your code might be reimplemented:
List<Manipulate> a = // initializing list a
List<Manipulate> b = // initializing list b
// mapping each `id` in the `b` to it's `quantity`
Map<Integer, Integer> quantityById = b.stream()
.collect(Collectors.toMap(
Manipulate::getId, // keyMapper
Manipulate::getQuantity // valueMapper
));
// generating the list `c`, containing only elements
// with `id` that are present in the `b`
List<Manipulate> c = a.stream()
.filter(m -> quantityById.containsKey(m.getId()))
.collect(Collectors.toList()); // or .toList() for Java 16+
// updating `quantity` property of each element in `c`
for (Manipulate m : c)
m.setQuantity(
m.getQuantity() - quantityById.get(m.getId())
);
In case if you had no intention to change the data in a
, then you need to create new instances of Manipulate
for every matching id
. And it's perfectly fine to do in the stream:
List<Manipulate> a = // initializing list a
List<Manipulate> b = // initializing list b
Map<Integer, Integer> quantityById = // generate a map like shown above
List<Manipulate> c = a.stream()
.filter(m -> quantityById.containsKey(m.getId()))
.map(m -> new Manipulate(m.getId(), m.getQuantity() - quantityById.get(m.getId())))
.collect(Collectors.toList()); // or .toList() for Java 16+
Note: you need to add the third argument to Collectors.toMap()
in case if there could be duplicated id
in the list b
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.