4

I have a class

public class ProductStock {

   private Long id;
   private Integer quantity;

}

and a list like

List<ProductStock> productStocks = 
{
  ProductStock(1, 1),
  ProductStock(2, 1),
  ProductStock(3, 1),
  ProductStock(1, 1),
  ProductStock(4, 1),
  ProductStock(5, 1),
  ProductStock(2, 1)
}

I want to group productStocks by id. What is the best way to convert this list like bellow

productStocks = 
{
  ProductStock(1, 2),
  ProductStock(2, 2),
  ProductStock(3, 1),
  ProductStock(4, 1),
  ProductStock(5, 1)
}
2
  • 1
    Use Java8 streams and a map. Besides that "best" is quite vague but in any case you might want to try something yourself. Commented Nov 16, 2017 at 12:39
  • Use a map and aggregate them there. Commented Nov 16, 2017 at 12:40

4 Answers 4

4

With Java8 streams you could try it like this:

productStocks = new ArrayList<>( productStocks.stream().collect( 
       Collectors.toMap( p -> p.id, p -> p, (p,o) -> { p.quantity += o.quantity; return p;} ) )
                 .values() );

What this does:

  • productStocks.stream().collect( Collectors.toMap(...) ) creates a stream for the list and collects the values into a map.
  • p -> p.id uses the product's id as the map key
  • p -> p uses the product as the map value, if it doesn't exist already
  • (p,o) -> { p.quantity += o.quantity; return p;} merges the existing product p and the "new" product value o by adding the quantity into p and returning that. Alternatively you could create a new Product instance: (p,o) -> new Product(p.id, p.quantity + o.quantity)
  • finally we build a new list out of the map's values

Note that collecting the elements into a map like like might not preserve the order of the elements. If you want to keep the order as defined in the source list, you could add a 4th parameter to collect(...): LinkedHashMap::new.

Sign up to request clarification or add additional context in comments.

Comments

4

You can group the products by iterating the List and add products to LinkedHashMap to preserve order:

void mergeProducts() {
    Map<Long, Integer> pMap = new HashMap<Long, Integer>();
    for (ProductStock ps :  productStocks) {
        if (pMap.containsKey(ps.getId())) {
            int qty = pMap.get(ps.getId());
            qty += ps.getQuantity();
            pMap.put(ps.getId(), qty);
        } else {
            pMap.put(ps.getId(), ps.getQuantity());
        }
    }

    System.out.println("Product grouping: ");
    for (Map.Entry<Long, Integer> entry : pMap.entrySet()) {
        System.out.println(entry.getKey() + " " + entry.getValue());
    }
}

Comments

3

I'd do it that way:

Map<Long, Integer> counting = productStocks.stream().collect(
                Collectors.groupingBy(ProductStock::getId, Collectors.counting()));

Create an new Map with the id as key and the Count as value.

1 Comment

One note though: Collectors.counting() will count the products not aggregate the quantities. Try with initial quantities other than 1.
1

You can use a Map for aggregating all the ProductStock in one place. Then you can use the contents of this Map to create the new List of ProductStock that will have the aggregated data.

Map<Long, Long> productStockMap = productStocks.stream().collect(Collectors.groupingBy(ProductStock::getId, Collectors.counting()));
productStocks = new ArrayList<>();
for(Map.Entry<Long, Long> entry: productStockMap.entrySet()) {
    productStocks.add(new ProductStock(entry.getKey(), entry.getValue().intValue()));
}

Result:

============Before============

ProductStock{id=1, quantity=1}
ProductStock{id=2, quantity=1}
ProductStock{id=3, quantity=1}
ProductStock{id=1, quantity=1}
ProductStock{id=4, quantity=1}
ProductStock{id=5, quantity=1}
ProductStock{id=2, quantity=1}

=============After============

ProductStock{id=1, quantity=2}
ProductStock{id=2, quantity=2}
ProductStock{id=3, quantity=1}
ProductStock{id=4, quantity=1}
ProductStock{id=5, quantity=1}

1 Comment

Note: due to auto(un)boxing instead of (int)entry.getValue().intValue() you can write only entry.getValue() but even without that the cast to int should not be necessary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.