如何收集一个POJO ID到POJO集合上的属性集的映射。

huangapple go评论73阅读模式
英文:

How to collect a map of a pojo id to a set of a property of a collection on the pojo

问题

我正在尝试使用流来将POJO映射到POJO上的属性公开的项目集合。我意识到这不够清楚,所以我将展示如何在没有流的情况下完成它。

我有一个产品类别的枚举,一个产品的枚举以及一个包含产品的商店列表,如下所示:

public enum Category {
    FRUIT, VEGETABLE, MEAT;
}

public enum Product {
    APPLE(Category.FRUIT),
    PEAR(Category.FRUIT),
    BANANA(Category.FRUIT),
    CARROT(Category.VEGETABLE),
    POTATO(Category.VEGETABLE),
    CABBAGE(Category.VEGETABLE),
    MINCE(Category.MEAT),
    CHOP(Category.MEAT),
    STEAK(Category.MEAT);

    private final Category category;

    Product(Category category) {
        this.category = category;
    }

    public String value() {
        return name();
    }

    public static Product fromValue(String v) {
        return valueOf(v);
    }

    public Category getCategory() {
        return this.category;
    }
}

public class Shop {

    private long id;
    private String name;
    private EnumSet<Product> products;

    public Shop(long id, String name, Collection<Product> products) {
        this.id = id;
        this.name = name;
        this.products = Optional.ofNullable(products).map(EnumSet::copyOf).orElse(null);
    }
}

我创建了一个方法来创建一些虚拟数据:

public static List<Shop> getListOfShops() {
    Shop shop1 = new Shop(1, "Green Grocer", EnumSet.of(Product.APPLE, Product.BANANA, Product.CARROT, Product.CABBAGE));
    Shop shop2 = new Shop(2, "Supermarket", EnumSet.of(Product.APPLE, Product.BANANA
        , Product.CARROT, Product.CABBAGE, Product.CHOP, Product.MINCE));
    Shop shop3 = new Shop(3, "Butcher", EnumSet.of(Product.STEAK));

    return Arrays.asList(shop1, shop2, shop3);
}

我想要创建一个商店类别的映射:

Map<String, EnumSet<Category>> shopCategories = new HashMap<>();

其中内容如下:

{Butcher=[MEAT], Supermarket=[FRUIT, VEGETABLE, MEAT], Green Grocer=[FRUIT, VEGETABLE]}

我已经使用一些基本的循环实现了这一点,但我希望能够将其转换为流。

public static Map<String, EnumSet<Category>> getCategories(List<Shop> shops) {

    Map<String, EnumSet<Category>> shopCategories = new HashMap<>();

    for (Shop s : shops) {
        Set<Category> cats = new HashSet<>();
        for (Product p : s.getProducts()) {
            cats.add(p.getCategory());
        }
        shopCategories.put(s.getName(), EnumSet.copyOf(cats));
    }

    return shopCategories;
}

我有一个模糊的想法如何将其转换为商店到产品的映射,使用终端操作进行收集。大致如下:

shops.stream().collect(Collectors.toMap(s -> s::getName, PaymentProductWithCapabilities::getProducts));

我在理解如何继续映射类别方面遇到了困难。有人可以指点我正确的方向吗?

英文:

I'm trying to use streams to get a map of a pojo to a Set of items exposed by a property on the pojo. I realise that isn't clear so I'll show how I've done it without streams.

I have and enum of product categories, and enum of Products and a list of shops with the product like this

public enum Category {
	FRUIT, VEGITABLE, MEAT;
}

public enum Product {
	APPLE ( Category.FRUIT ),
	PEAR ( Category.FRUIT ),
	BANANA ( Category.FRUIT ),
	CARROT ( Category.VEGITABLE ),
	POTATO ( Category.VEGITABLE ),
	CABBAGE ( Category.VEGITABLE ),
	MINCE ( Category.MEAT ),
	CHOP ( Category.MEAT ),
	STEAK ( Category.MEAT );

	private final Category category;

	Product( Category category ) {
		this.category = category;
	}

	public String value() {
		return name();
	}

	public static Product fromValue( String v ) {
		return valueOf( v );
	}

	public Category getCategory() {
		return this.category;
	}
}

public class Shop {

	private long id;
	private String name;
	private EnumSet&lt;Product&gt; products;

	public Shop( long id, String name, Collection&lt;Product&gt; products ) {
		this.id = id;
		this.name = name;
		this.products = ofNullable( products ).map( EnumSet::copyOf ).orElse( null );
	}
}

I have made a method which creates some dummy data...

public static List&lt;Shop&gt; getListOfShops() {
		Shop shop1 = new Shop( 1, &quot;Green Grocer&quot;, EnumSet.of( Product.APPLE, Product.BANANA, Product.CARROT, Product.CABBAGE ) );
		Shop shop2 = new Shop( 2, &quot;Supermarket&quot;, EnumSet.of( Product.APPLE, Product.BANANA
			, Product.CARROT, Product.CABBAGE, Product.CHOP, Product.MINCE ) );
		Shop shop3 = new Shop( 3, &quot;Butcher&quot;, EnumSet.of( Product.STEAK ) );

		return Arrays.asList( shop1, shop2, shop3 );
	}

I want to create a map of shop categories

Map&lt;String, EnumSet&lt;Category&gt;&gt; shopCategories = new HashMap&lt;&gt;();

which will have contents like this

> {Butcher=[MEAT], Supermarket=[FRUIT, VEGITABLE, MEAT], Green
> Grocer=[FRUIT, VEGITABLE]}

I have achieved this with some basic loops which I was hoping I was smart enough to convert into streams

public static Map&lt;String, EnumSet&lt;Category&gt;&gt; getCategories( List&lt;Shop&gt; shops ) {

	Map&lt;String, EnumSet&lt;Category&gt;&gt; shopCategories = new HashMap&lt;&gt;();

	for ( Shop s : shops ) {
		Set&lt;Category&gt; cats = new HashSet&lt;Category&gt;();
		for ( Product p : s.getProducts() ) {
			cats.add( p.getCategory() );
		}
		shopCategories.put ( s.getName(), EnumSet.copyOf (cats) );
	}

	return shopCategories;
}

I have a vague idea how to convert it to a map of shop to products with a collector as the terminal operation. Something along the lines of

shops.stream().collect( Collectors.toMap ( s -&gt; s::getName ,
	PaymentProductWithCapabilities::getProducts ) );

I'm struggling for an entry point into understanding how to proceed to mapping the Categories instead ? Can anyone point me in the right direction ?

答案1

得分: 2

你可以使用Collectors.toMap中的valueMapper将你正在流式传输的商店的产品转换为预期的Map的值,就像你的迭代风格一样:

return shops.stream()
        .collect(Collectors.toMap(Shop::getName,
                s -> EnumSet.copyOf(s.getProducts().stream()
                        .map(Product::getCategory)
                        .collect(Collectors.toSet()))));

要精确一点,你的实现会覆盖Map中相同名称的两个商店的值,可以在Collectors.toMap中使用合并函数来实现,例如(a, b) -> b

或者,正如Gene评论中建议的那样,更好的做法是:

return shops.stream()
        .collect(Collectors.toMap(Shop::getName,
                s -> s.getProducts().stream()
                        .map(Product::getCategory)
                        .collect(Collectors.toCollection(
                                () -> EnumSet.noneOf(Category.class))),
                (a, b) -> b));
英文:

You could transform the products of the shop you are streaming on, into the value of the expected Mapusing the valueMapper in Collectors.toMap, similar to your iterative style as:

return shops.stream()
        .collect(Collectors.toMap(Shop::getName,
                s -&gt; EnumSet.copyOf(s.getProducts().stream()
                        .map(Product::getCategory)
                        .collect(Collectors.toSet()))));

To be precise, your implementation overrides the value of the Map, if the same name is found for two shops, which in the Collectors.toMap can be implemented with the merge function such as (a,b) -&gt; b.

Or better as suggested by Gene in a comments:

return shops.stream()
        .collect(Collectors.toMap(Shop::getName,
                s -&gt; s.getProducts().stream()
                        .map(Product::getCategory)
                        .collect(Collectors.toCollection(
                                () -&gt; EnumSet.noneOf(Category.class))),
                (a, b) -&gt; b));

答案2

得分: 1

同样的结果可以使用Collectors.groupingByCollectors.flatMapping来实现 -

Map<String, EnumSet<Category>> categories =
        shops.stream()
            .collect(
                Collectors.groupingBy(
                    Shop::getName,
                    Collectors.flatMapping(
                        s -> s.getProducts().stream().map(Product::getCategory),
                        Collectors.toCollection(() -> EnumSet.noneOf(Category.class)))));

Collectors.groupingBy 根据 Shop::getName 对流进行分组,而 Collectors.flatMapping 则将产品列表扁平化,并使用 map 将产品映射到 Product 的类别。Collectors.flatMapping 还有一个 Collector<? super U, A, R> downstream 参数,可以用来收集 EnumSet<Category>

英文:

Same can be achieved using Collectors.groupingBy and Collectors.flatMapping as -

Map&lt;String, EnumSet&lt;Category&gt;&gt; categories =
        shops.stream()
            .collect(
                groupingBy(
                    Shop::getName,
                    flatMapping(
                        s -&gt; s.getProducts().stream().map(Product::getCategory),
                        toCollection(() -&gt; EnumSet.noneOf(Category.class)))));

Collectors.groupingBy groups stream based on Shop::getName and Collectors.flatMappingflattens List of Products and map maps product to the Product category. Collectors.flatMapping also Collector&lt;? super U, A, R&gt; downstream which can be used to collect EnumSet&lt;Category&gt;.

huangapple
  • 本文由 发表于 2020年8月8日 00:03:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/63305418.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定