按字段和嵌套对象的字段用Java流分组对象

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

Group object by field and nested object's fields with Java streams

问题

以下是翻译好的内容:

我想要做的是根据特定字段对列表中的元素进行分组,以创建一个映射(Map)。期望的输出是:Map<String, Map<String, Map<String, MasterObject>>>。键的顺序是日期、类型、颜色。

我的代码如下:

public class MasterObject {
   private String date;
   private List<SubObject> subObject;
}

public class SubObject {
   private String type;
   private String color;
}

我尝试过使用 Collectors.groupingBy,如果字段是同一对象的成员(例如日期),它可以很好地工作,但我尝试将其用于包含对象的情况(例如 subObject),但尚未成功。我的输入是 List<MasterObject>。可以通过使用 Map 的 putget 方法来手动完成,但也许有一种更简洁的方法可以通过 Java 流来实现。

迄今为止,我尝试过以下方法:

Map<String, Map<List<String>, List<MasterObject>>> collect = faList.stream().collect(
    Collectors.groupingBy(f -> f.getDate(),
    Collectors.groupingBy(f -> f.getSubObject().stream().map(z -> z.getType()).collect(Collectors.toList()))));

在我上面的示例中,我尚未成功按类型对元素进行分组,而是得到了一个 List<String> 类型的键。此外,我的列表还应该按颜色进行分组。

英文:

What I would like to do is group elements of a List in order to create a map, based on specific fields. The desired output is the following: Map&lt;String,Map&lt;String,Map&lt;String,MasterObject&gt;&gt;&gt;. The order of the keys is date, type, color.

My code is as follows

public class MasterObject{
   private String date;
   private List&lt;SubObject&gt; subObject;
}

public class SubObject{
   private String type;
   private String color;
}

What I have tried and used is Collectors.groupingBy which works great if your fields are members of the same Object (ex. date), but haven't managed to make it work for containing objects (ex. subObject). My input is a List&lt;MasterObject&gt;. It could be done the hard way by using Map's put and get but maybe there is a much cleaner way to make it work with Java streams.

What I have tried thus far is the following:

    Map&lt;String, Map&lt;List&lt;String&gt;, List&lt;MasterObject&gt;&gt;&gt; collect = faList.stream().collect(
    Collectors.groupingBy(f -&gt; f.getDate(),
    Collectors.groupingBy(f -&gt; f.getSubObject().stream().map(z -&gt; z.getType()).collect(Collectors.toList()))));

In my sample above, I haven't managed to achieve to group elements by type, instead my key is a List&lt;String&gt;.Also my list should have a group by color as well.

答案1

得分: 5

我会使用自定义对象来保留扁平化的值:

public class CustomObject {
    private String date;
    private String type;
    private String color;
    private MasterObject masterObj;

    // constructor, getters, setters
}

试试这个:

SubObject sub1 = new SubObject("typeA", "red");
SubObject sub2 = new SubObject("typeA", "blue");
SubObject sub3 = new SubObject("typeB", "green");
SubObject sub4 = new SubObject("typeB", "green");
SubObject sub5 = new SubObject("typeC", "red");
SubObject sub6 = new SubObject("typeC", "blue");

List<MasterObject> masterObjList = new ArrayList<>();

masterObjList.add(new MasterObject("01/01/2020", Arrays.asList(sub1, sub2, sub3)));
masterObjList.add(new MasterObject("02/01/2020", Arrays.asList(sub4, sub5, sub6)));

Map<String, Map<String, Map<String, MasterObject>>> result = masterObjList.stream().flatMap(
        o -> o.getSubObject().stream().map(s -> new CustomObject(o.getDate(), s.getType(), s.getColor(), o)))
        .collect(Collectors.groupingBy(CustomObject::getDate, Collectors.groupingBy(CustomObject::getType,
                Collectors.toMap(CustomObject::getColor, CustomObject::getMasterObj))));

result.entrySet().forEach(e -> {
    System.out.println(e.getKey());
    e.getValue().entrySet().forEach(e1 -> {
        System.out.println("\t" + e1.getKey());
        e1.getValue().entrySet().forEach(e2 -> {
            System.out.println("\t\t" + e2.getKey());
        });
    });
});

输出:

01/01/2020
typeB
green
typeA
red
blue
02/01/2020
typeC
red
blue
typeB
green
英文:

I would use a custom object to keep flattened values:

public class CustomObject {
private String date;
private String type;
private String color;
private MasterObject masterObj;
// constructor, getters, setters
}

Try this:

SubObject sub1 = new SubObject(&quot;typeA&quot;, &quot;red&quot;);
SubObject sub2 = new SubObject(&quot;typeA&quot;, &quot;blue&quot;);
SubObject sub3 = new SubObject(&quot;typeB&quot;, &quot;green&quot;);
SubObject sub4 = new SubObject(&quot;typeB&quot;, &quot;green&quot;);
SubObject sub5 = new SubObject(&quot;typeC&quot;, &quot;red&quot;);
SubObject sub6 = new SubObject(&quot;typeC&quot;, &quot;blue&quot;);
List&lt;MasterObject&gt; masterObjList = new ArrayList&lt;&gt;();
masterObjList.add(new MasterObject(&quot;01/01/2020&quot;, Arrays.asList(sub1, sub2, sub3)));
masterObjList.add(new MasterObject(&quot;02/01/2020&quot;, Arrays.asList(sub4, sub5, sub6)));
Map&lt;String, Map&lt;String, Map&lt;String, MasterObject&gt;&gt;&gt; result = masterObjList.stream().flatMap(
o -&gt; o.getSubObject().stream().map(s -&gt; new CustomObject(o.getDate(), s.getType(), s.getColor(), o)))
.collect(Collectors.groupingBy(CustomObject::getDate, Collectors.groupingBy(CustomObject::getType,
Collectors.toMap(CustomObject::getColor, CustomObject::getMasterObj))));
result.entrySet().forEach(e -&gt; {
System.out.println(e.getKey());
e.getValue().entrySet().forEach(e1 -&gt; {
System.out.println(&quot;\t&quot; + e1.getKey());
e1.getValue().entrySet().forEach(e2 -&gt; {
System.out.println(&quot;\t\t&quot; + e2.getKey());
});
});
});

Output:

01/01/2020
typeB
green
typeA
red
blue
02/01/2020
typeC
red
blue
typeB
green

答案2

得分: 2

@Hülya的解决方案很棒,在复杂数据结构上的扩展性更好。

另一种更简洁的方法是,在处理性能重要的情况下可能更快(因为避免了中间对象),只需查看元素并即时在映射中添加:

public Map<String, Map<String, Map<String, MasterObject>>> group(List<MasterObject> objects) {

  Map<String, Map<String, Map<String, MasterObject>>> byDate = new HashMap<>();
  for (MasterObject m : objects) {
    Map<String, Map<String, MasterObject>> byType = byDate.computeIfAbsent(m.date, k -> new HashMap<>());
    for (SubObject sub : m.subObject) {          
      Map<String, Map<String, MasterObject>> byColor = byType.computeIfAbsent(sub.type, k -> new HashMap<>());
      byColor.put(sub.color, m);
    }
  }
  return byDate;
}

毕竟,groupBy 就是这样,遍历所有元素并将它们放入正确的存储桶中。有时,迭代的方法比函数式方法更短小,也同样易读。

只是为了好玩,在 C++ 中,使用 lambda 很烦人(在最近的版本中有所改进),而命令式的方法甚至比 Java 中更加简洁:

map<string, map<string, map<string, MasterObject>>> group(const vector<MasterObject> &objects) {

  map<string, map<string, map<string, MasterObject>>> result;
  for (auto&& m : objects) {
    for (auto &&sub : m.subObject) {
      result[m.date][sub.type][sub.color] = m;
    }
  }
  return result;
}
英文:

The solution by @Hülya is great, and scale better on complex data structure.

An alternative and more concise way to do it, potentially faster (if perf is important) due to avoiding the intermediate object, is to just look on the element and add in the map on the go:

public Map&lt;String, Map&lt;String, Map&lt;String, MasterObject&gt;&gt;&gt; group (List&lt;MasterObject&gt; objects) {
Map&lt;String, Map&lt;String, Map&lt;String, MasterObject&gt;&gt;&gt; byDate = new HashMap&lt;&gt;();
for (MasterObject m : objects) {
Map&lt;String, Map&lt;String, MasterObject&gt;&gt; byType = byDate.computeIfAbsent(m.date, k -&gt; new HashMap&lt;&gt;();); 
for (SubObject sub : m.subObject) {          
Map&lt;String, Map&lt;String, MasterObject&gt;&gt; byColor = byType.computeIfAbsent(sub.type, k -&gt; new HashMap&lt;&gt;(););
byColor.put(sub.color, m);
}
}
return byDate;
}

After all a groupBy is just that, iterating over all elements and put them in the right bucket. And sometime the iterative approach is shorter/smaller and as readable as the functional one.

Just for fun, in C++, lambda are annoying to use (it improved in recent versionsà and the imperative approach is even more concise than in java:

map&lt;string, map&lt;string, map&lt;string, MasterObject&gt;&gt;&gt; group (const vector&lt;MasterObject&gt; &amp;objects) {
map&lt;string, map&lt;string, map&lt;string, MasterObject&gt;&gt;&gt; result;
for (auto&amp;&amp; m : objects) {
for (auto &amp;&amp;sub : m.subObject) {
result[m.date][sub.type][sub.color] = m;
}
}
return result;
}

huangapple
  • 本文由 发表于 2020年7月23日 21:47:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/63055824.html
匿名

发表评论

匿名网友

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

确定