java - 使用 Collection.stream 按特定属性动态分组

标签 java collections java-8 grouping 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)));

但我想让它更有活力一点。 我只想指定一个应该用于 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())
        ));
}

代码的第一部分将字段名称数组转换为 MethodHandleList .对于每个字段,为该字段检索一个 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/

相关文章:

java - Signapk.jar 给出错误 java.lang.ClassNotFoundException : sun. misc.BASE64Encoder

java - RSA加密Android和Java中的解密: javax. crypto.BadPaddingException:解密错误

scala - 集合如何对元素类型使用隐式转换?

java - 使用 Java 8 流生成整数对

java - 使用java流将最后遇到的值放入 map

java - 作为 Java 应用程序运行会弹出选择 Java 应用程序屏幕,无需进一步说明

c# - 在字典中选择特定对象类型并将其删除的最有效方法

python - 错误: unhashable type = 'list'

java - 想从java中的char数组创建一个字符流

java - ThreadLocal 供应商?