首页 > 解决方案 > 将一个集合划分为具有相等字段的对象的子集?

问题描述

我目前正在尝试在 java 中实现数据结构,并希望将一组输入对象划分为具有相同字段的不同对象子集。

An example use case

我们希望将人员列表划分为特定日期出生的人员的子列表。

Input:person1 1990 年出生,person2 2000 年出生,person3 1990 年出生。

Output

1 -> 人 1,人 3

2 -> 人2

public Map<Integer, List<Foo>> getIntToFooMap(List<Foo> foos) { 
    Map<Integer, List<Foo>> map = new TreeMap<>(); // need keys to be automatically ordered.
    List<Foo> foosWithSameSetId = new ArrayList<>();
    if (!foos.isEmpty) { 
       for (Foo foo: foos) {
           for (Foo foo2: foos) {
               if (foo.getSetId().equals(foo2.getSetId())) {
                   foosWithSameSetId.add(foo2);
               }
           }
        map.put(foo.getSetId(), foosWithSameSetId);
        foosWithSameSetId.clear();
       }
    }
  return map;
 }

上面的代码不是最优的,时间复杂度是二次的,也不是线程安全的。有人能告诉我一个更好的方法将 List 或 Set 划分为具有相等字段的对象子集,在这种情况下是setId.

标签: javaalgorithmoopdata-structurestreemap

解决方案


首先,不需要嵌套循环。您只需获取或创建当前foo's的集合setId,并将其添加foo到其中:

for (Foo foo : foos) {
    map.computeIfAbsent(foo.getSetId(), i -> new ArrayList<>()).add(foo);
}

这相当于:

for (Foo foo : foos) {
    List<Foo> list = map.get(foo.getSetId());
    if(null == list) list = new ArrayList<>();
    list.add(foo);
}

现在,您需要记住地图实现的时间复杂度

作为替代方案,groupingBy流收集器会为您的代码添加简洁性:

return foos.stream().collect(
        Collectors.groupingBy(Foo::getSetId, TreeMap::new, Collectors.toList()));

推荐阅读