我试图通过使用
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","B")); data.add(new MyClass("3","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的String-Array(或List).
就像是:
Object groupListBy(List data,String[] groupByFieldNames) { //magic code }
我想打电话给:
groupListBy(data,new String[]{"type","module"});
如何使groupBy-Method更具动态性,就像在我的例子中一样?
解决方法
使代码更具动态性的主要问题是,您事先并不知道要分组的元素数量.在这种情况下,最好按所有元素的列表进行分组.这是有效的,因为如果两个列表的所有元素相同并且顺序相同,则它们是相等的.
在这种情况下,我们将按照由每种数据类型和模块组成的列表进行分组,而不是按类型和模块进行分组.
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值列表.此流被收集到List中以用作分类器.
示例代码:
public static void main(String[] args) { List<MyClass> data = new ArrayList<>(); data.add(new MyClass("1","B")); data.add(new MyClass("2","B")); data.add(new MyClass("3","C")); data.add(new MyClass("4","A")); System.out.println(groupListBy(data,new String[] { "type","module" })); }
输出:
{[B,A]=[4],[A,B]=[1,2],C]=[3]}
当MyClass.toString()被覆盖以仅返回标题时.