简体   繁体   中英

Filter Objects having the same property from two Lists and Update them and Store it in a 3rd List in Java 8

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM