Tho*_* Z. 8 java collections grouping java-8 java-stream
我试图通过使用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)));
Run Code Online (Sandbox Code Playgroud)
但我想让它更有活力.我只想指定一个应该用于GroupBy的String-Array(或List).
就像是:
Object groupListBy(List data, String[] groupByFieldNames)
{
//magic code
}
Run Code Online (Sandbox Code Playgroud)
我想打电话给:
groupListBy(data, new String[]{"type","module"});
Run Code Online (Sandbox Code Playgroud)
如何使groupBy-Method更具动态性,就像在我的例子中一样?
Tun*_*aki 13
使代码更具动态性的主要问题是,您事先并不知道要分组的元素数量.在这种情况下,最好按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())
));
}
Run Code Online (Sandbox Code Playgroud)
代码的第一部分将字段名称数组转换List为MethodHandle.对于每个字段,MethodHandle为该字段检索a :这是通过从以下位置获取查找MethodHandles.lookup()并查找给定字段名称的句柄来完成的findGetter:
生成一个方法句柄,提供对非静态字段的读访问权限.
其余代码创建分类器来分组.在数据实例上调用所有句柄以返回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" }));
}
Run Code Online (Sandbox Code Playgroud)
输出:
{[B, A]=[4], [A, B]=[1, 2], [A, C]=[3]}
Run Code Online (Sandbox Code Playgroud)
何时MyClass.toString()被覆盖以返回title唯一的.
您也可以考虑提供一个功能列表(有一个必填项)来对元素进行分组,而不是名称列表.
这些函数应该将元素映射MyClass到对象,以便您可以使用Function<MyClass, ?>.
private static Map<List<Object>, List<MyClass>> groupListBy(List<MyClass> data, Function<MyClass, ?> mandatory, Function<MyClass, ?>... others) {
return data.stream()
.collect(groupingBy(cl -> Stream.concat(Stream.of(mandatory), Stream.of(others)).map(f -> f.apply(cl)).collect(toList())));
}
Run Code Online (Sandbox Code Playgroud)
一些电话示例:
groupListBy(data, m -> m.type); //group only by type
groupListBy(data, m -> m.type, m -> m.module); //group by type and by module
Run Code Online (Sandbox Code Playgroud)
当然,您可以将此方法设为通用,以便返回Map<List<Object>, List<U>>包含该类型函数的方法U -> Object.