当前位置: 首页 > 面试题库 >

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

东方俊材
2023-03-14
问题内容

我正在尝试使用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())
        ));
}

代码的第一部分将字段名称数组转换为ListMethodHandle。对于每个字段,将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" }));
}

输出:

{[B, A]=[4], [A, B]=[1, 2], [A, C]=[3]}

MyClass.toString()重写when 返回title唯一的。



 类似资料:
  • 我使用查询DSL生成实体EntitySerializer,以便使用QueryDSL(与SpringData集成)查询JPA实体。

  • 我还有这个包含ID列表: 我想筛选以便筛选的不包含id来自的对象,因此我尝试使用stream: 我找不到做这件事的正确函数。有人有什么建议让我试试吗?

  • 本文向大家介绍详解Python中的动态属性和特性,包括了详解Python中的动态属性和特性的使用技巧和注意事项,需要的朋友参考一下 导语:本文章记录了本人在学习Python基础之元编程篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。 一、利用动态属性处理JSON数据源 属性:在Python中,数据的属性和处理数据的方法统称属性。 元编程:用元类进行编程,元类→类→对象,元

  • 问题内容: 基本上,我有一个表,其中的字段标识创建消息的用户。 当我显示两个用户之间的对话(一组消息)时,我希望能够通过来对消息进行分组,但要采用一种棘手的方式: 假设有一些消息(按排序): 我想按以下顺序获得3个消息组: 它应该按 user_id 分组,直到看到一个不同的分组,然后再按该分组。 我正在使用PostgreSQL,并且很乐意使用特定于它的东西,无论哪种都能提供最佳性能。 问题答案:

  • 问题内容: 我已经编写了一个PropertyUtils类(来自互联网),它将加载属性 而PropertiesUtil类如下所示 稍后,我可以通过调用PropertiesUtil.getProperty()方法来获取属性。 但是现在我要稍作修改,以便如果myApp.properties被用户修改/更改,则应再次加载 可能我需要FileWatcher类 但我的怀疑是 如何使用classpath:myA

  • 我已经写了一个PropertyUtils类(来自互联网),它将加载属性 PropertiesUtil类如下所示 稍后,我可以通过调用PropertiesUtil来获取属性。getProperty()方法。 但现在我想稍微修改一下,如果myApp。属性被用户修改/更改,应该重新加载 可能我需要FileWatcher类 但我的疑虑是 如何使用classpath创建File对象:myApp/myApp.