我正在尝试使用 Java 8 Collection-Stream 按多个属性对对象列表进行分组。
这很好用:
public class MyClass
{
public String title;
public String type;
public String module;
public MyClass(String title, String type, String module)
{
this.type = type;
this.title = title;
this.module= module;
}
}
List<MyClass> data = new ArrayList();
data.add(new MyClass("1","A","B"));
data.add(new MyClass("2","A","B"));
data.add(new MyClass("3","A","C"));
data.add(new MyClass("4","B","A"));
Object result = data.stream().collect(Collectors.groupingBy((MyClass m)
-> m.type, Collectors.groupingBy((MyClass m) -> m.module)));
但我想让它更有活力一点。 我只想指定一个应该用于 GroupBy 的字符串数组(或列表)。
类似于:
Object groupListBy(List data, String[] groupByFieldNames)
{
//magic code
}
我想调用:
groupListBy(data, new String[]{"type","module"});
如何使 groupBy-Method 更具动态性,就像我的示例中那样?
最佳答案
使代码更动态化的主要问题是您事先不知道有多少元素可以分组。在这种情况下,最好按所有元素的 List
进行分组。这是可行的,因为如果两个列表的所有元素都相等且顺序相同,则两个列表相等。
在这种情况下,我们将按由每种数据类型和模块组成的列表进行分组,而不是先按类型再按模块进行分组。
private static Map<List<String>, List<MyClass>> groupListBy(List<MyClass> data, String[] groupByFieldNames) {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
List<MethodHandle> handles =
Arrays.stream(groupByFieldNames)
.map(field -> {
try {
return lookup.findGetter(MyClass.class, field, String.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}).collect(toList());
return data.stream().collect(groupingBy(
d -> handles.stream()
.map(handle -> {
try {
return (String) handle.invokeExact(d);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}).collect(toList())
));
}
代码的第一部分将字段名称数组转换为 MethodHandle
的 List
.对于每个字段,为该字段检索一个 MethodHandle
:这是通过从 MethodHandles.lookup()
中获取查找来完成的。并使用 findGetter
查找给定字段名称的句柄:
Produces a method handle giving read access to a non-static field.
其余代码创建分类器以根据来源进行分组。在数据实例上调用所有句柄以返回 String
值列表。此 Stream
被收集到 List
中以用作分类器。
示例代码:
public static void main(String[] args) {
List<MyClass> data = new ArrayList<>();
data.add(new MyClass("1", "A", "B"));
data.add(new MyClass("2", "A", "B"));
data.add(new MyClass("3", "A", "C"));
data.add(new MyClass("4", "B", "A"));
System.out.println(groupListBy(data, new String[] { "type", "module" }));
}
输出:
{[B, A]=[4], [A, B]=[1, 2], [A, C]=[3]}
当覆盖 MyClass.toString()
以仅返回 title
时。
关于java - 使用 Collection.stream 按特定属性动态分组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34770177/