[英]How to group values from a list with Java Stream API (groupingBy collector)?
[英]How to group Objects by property using using collector groupingBy() on top of flatMap() in Java 8
如何創建下面的Map<String,List<Product>>
。 在這里, String
( Map
的鍵)是Product
的category
。
一種產品可以屬於多個類別,如下例所示。
我正在嘗試使用以下代碼,但無法進行下一個操作:
products.stream()
.flatMap(product -> product.getCategories().stream())
. // how should I progress from here?
結果應如下所示:
{電子=[p1,p3,p4], 時尚=[p1,p2,p4], 廚房=[p1,p2,p3], abc1=[p2], xyz1=[p3],pqr1=[p4]}
Product p1 = new Product(123, Arrays.asList("electonics,fashion,kitchen".split(",")));
Product p2 = new Product(123, Arrays.asList("abc1,fashion,kitchen".split(",")));
Product p3 = new Product(123, Arrays.asList("electonics,xyz1,kitchen".split(",")));
Product p4 = new Product(123, Arrays.asList("electonics,fashion,pqr1".split(",")));
List<Product> products = Arrays.asList(p1, p2, p3, p4);
class Product {
int price;
List<String> categories;
public Product(int price) {
this.price = price;
}
public Product(int price, List<String> categories) {
this.price = price;
this.categories = categories;
}
public int getPrice() {
return price;
}
public List<String> getCategories() {
return categories;
}
}
如果您出於某種原因想使用收集器groupingBy()
,那么您可以定義一個包裝器 class (使用 Java 16+記錄會更方便),它將包含對類別和產品的引用來代表每個組合給定列表中存在的類別/產品。
public record ProductCategory(String category, Product product) {}
Java 16 之前的替代方案:
public class ProductCategory {
private String category;
private Product product;
// constructor and getters
}
然后在利用收集器mapping()
和toList()
的組合作為groupingBy()
的下游收集器。
List<Product> products = // initializing the list of products
Map<String, List<Product>> productsByCategory = products.stream()
.flatMap(product -> product.getCategories().stream()
.map(category -> new ProductCategory(category, product)))
.collect(Collectors.groupingBy(
ProductCategory::category, // ProductCategory::getCategory if you used a class instead of record
Collectors.mapping(ProductCategory::product, // ProductCategory::getProduct if you used a class instead of record
Collectors.toList())
));
但是,與其創建中間對象和生成嵌套流,不如在collect()
的三參數版本(或定義自定義收集器)中描述累積策略,而不是創建中間對象。
這就是它的實現方式:
Map<String, List<Product>> productsByCategory = products.stream()
.collect(
HashMap::new,
(Map<String, List<Product>> map, Product next) -> next.getCategories()
.forEach(category -> map.computeIfAbsent(category, k -> new ArrayList<>())
.add(next)),
(left, right) -> right.forEach((k, v) ->
left.merge(k, v,(oldProd, newProd) -> { oldProd.addAll(newProd); return oldProd; }))
);
我嘗試了一些事情並提出了以下解決方案:
Map<Object, List<Product>> result =
products.stream()
.flatMap(product -> product.getCategories().stream().map(p -> Map.entry(p, product)))
.collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
System.out.println(result);
Output:
xyz1=[org.example.Product@15db9742], electonics=[org.example.Product@6d06d69c, org.example.Product@15db9742, org.example.Product@7852e922], abc1=[org.ex ...
編輯:我已經看到我的解決方案與其他答案非常相似。 但是,我的解決方案使用Map.Entry
而不是用戶定義的 object 將數據帶入正確的形狀。
這也可以通過flatMapping
和toMap
的組合來完成:
Map<String, List<Product>> obj = products.stream()
.collect(
Collectors.flatMapping(
product -> product.categories().stream()
.map(category -> Map.entry(category, List.of(product))),
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> Stream.concat(v1.stream(), v2.stream()).toList()
)
));
這里發生的情況是,首先,每個Product
都轉換為Map.Entry<String, List<Product>>
,其中鍵是類別,值是Product
本身,或者更准確地說是List<Product>
,此列表最初僅包含當前產品。
然后,您可以使用toMap
“解壓” map 條目。 當然,對於那些 key (=category) 相同的情況,必須合並值(即List
和Product
)。
注意:我在這里使用了Map.Entry
,但您也可以編寫自定義 class ,這在語義上更可取(類似於CategoryProductsMapping(String category, List<Product> products)
。
我知道所有者詢問了 groupby 和 flatmap,但以防萬一,我會提到reduce
。
.collect(
方法。HashMap<String, List<Product>> reduce = products.stream().reduce(
new HashMap<>(),
(result, product) -> {
product.getCategories().stream().distinct().forEach(
category -> result.computeIfAbsent(category, k -> new ArrayList<>())
.add(product)
);
return result;
},
(x, y) -> {
throw new RuntimeException("does not support parallel!");
}
);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.