【问题标题】:How to group objects from a list which can belong to two or more groups?如何从可以属于两个或多个组的列表中对对象进行分组?
【发布时间】:2019-03-27 11:40:46
【问题描述】:

我有一个项目列表,其中每个项目都可以属于一个或多个类别。对于一组有限的类别(字符串),我想创建一个地图,其中类别为键,项目列表为值。

假设我的 Item 类定义如下:

public static class Item{
    long id;
    List<String> belongsToCategories;

    public List<String> getBelongsToCategories() {
        return belongsToCategories;
    }

    public void setBelongsToCategories(List<String> belongsToCategories) {
        this.belongsToCategories = belongsToCategories;
    }

    public Item(long id,List<String> belongsToCategories) {
        this.id = id;
        this.belongsToCategories = belongsToCategories;
    } 

    @Override
    public String toString() {
        return "Item{" + "id=" + id + '}';
    }        
}

还有物品清单:

public static void main(String[] args) {
    List<Item> myItemList   = new ArrayList<>();

    myItemList.add(new Item(1,Arrays.asList("A","B")));
    myItemList.add(new Item(2,Arrays.asList("A","C")));
    myItemList.add(new Item(3,Arrays.asList("B","C")));
    myItemList.add(new Item(4,Arrays.asList("D")));
    myItemList.add(new Item(5,Arrays.asList("D","E")));
    myItemList.add(new Item(6,Arrays.asList("A","F")));

    Map<String,List<Item>> myMap= new HashMap<>();

如何从 myList 中填写 myMap?

我认为 Stream API 可以提供帮助,但我不知道当一个 Item 可以属于一个或多个类别时,将哪个分类器放入 groupingBy 方法中

 myItemList.stream().collect(Collectors.groupingBy(classifier));

这个

myItemList.stream().collect(Collectors.groupingBy(Item::getBelongsToCategories));

生产

[D, E]=[Item{id=5}]
[B, C]=[Item{id=3}]
[A, B]=[Item{id=1}]
[D]=[Item{id=4}]
[A, C]=[Item{id=2}]
[A, F]=[Item{id=6}]

预期是艰难的,例如:

A=[Item{id=1}, Item{id=2}, Item{id=6}]
B=[Item{id=1}, Item{id=3}]
C=[Item{id=2}, Item{id=3}]
D=[Item{id=4}, Item{id=5}]
E=[Item{id=5}]
F=[Item{id=6}]

【问题讨论】:

    标签: java java-8 java-stream grouping


    【解决方案1】:

    您可以使用flatMap 映射到SimpleEntry,然后将groupingBy 映射为:

    return items.stream()
            .flatMap(p -> p.getBelongsToCategories()
                    .stream()
                    .map(l -> new AbstractMap.SimpleEntry<>(l, p)))
            .collect(Collectors.groupingBy(Map.Entry::getKey,
                    Collectors.mapping(Map.Entry::getValue,
                            Collectors.toList())));
    

    【讨论】:

      【解决方案2】:

      您可以从Eclipse Collections 使用groupByEach

      Multimap<String, Item> itemsByCategory =
              ListIterate.groupByEach(myItemList, Item::getBelongsToCategories);
      
      System.out.println(itemsByCategory);
      

      输出:

      {D=[Item{id=4}, Item{id=5}], 
      E=[Item{id=5}], 
      F=[Item{id=6}], 
      A=[Item{id=1}, Item{id=2}, Item{id=6}], 
      B=[Item{id=1}, Item{id=3}], 
      C=[Item{id=2}, Item{id=3}]}
      

      您还可以将Collectors2 实用程序类和groupByEach 收集器与Java Stream 一起使用。

      Multimap<String, Item> itemsByCategory = myItemList.stream().collect(
              Collectors2.groupByEach(
                      Item::getBelongsToCategories,
                      Multimaps.mutable.list::empty));
      

      注意:我是 Eclipse Collections 的提交者。

      【讨论】:

        【解决方案3】:

        这是ListMultimap 的解决方案,来自Google Guava 或我的图书馆Abacus-Util

        ListMultimap<String, Item> result = Multimaps.newListMultimap(new HashMap<>(), () -> new ArrayList<>()); // by Google Guava.
        // Or result = N.newListMultimap(); // By Abacus-Util
        items.forEach(item -> item.getBelongsToCategories().forEach(c -> result.put(c, item)));
        

        我认为在不创建不必要的临时 Entry 对象 flatMap 的情况下,它会更加高效和简洁。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-08-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-30
          • 2019-06-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多