【问题标题】:How to grouping by similar key with java-lambda?如何使用 java-lambda 按相似键进行分组?
【发布时间】:2019-08-23 10:42:15
【问题描述】:

我有一个列表,元素的一个字段以一些相同的字符开头,我想对列表进行分组。

class Foo{
        String a;
        String b;
...
}

我怎样才能得到结果?

输入

List<Foo> list = Lists.newArrayList();
list.add(new Foo("FM11","1"));
list.add(new Foo("FM1122","2"));

输出

Map<String,List<String>>
{"FM11":["1","2"],"FM1122":["1","2"]}

我想使用正则表达式,如果匹配,将b合并到列表中,键使用a。我该怎么办?

Pattern.matches(a+".*")

【问题讨论】:

  • ...stream.collect(Collectors.groupingBy...)
  • 鉴于您的示例输入,我无法看到应该如何计算输出。您是否只想要与每个 a 关联的所有 bs 的叉积?
  • 我想得到一个所有a都与key相似的map,然后在Foo中得到b,类似a,b -&gt; toList
  • 如果您只有两个值,请使用 Collectors.partitionBy。否则,groupingBy。
  • substring(0, 4) 分组。作为旁注,你知道吗,why you are using Lists.newArrayList() instead of new ArrayList&lt;&gt;()?

标签: java lambda java-stream


【解决方案1】:

属于同一组的所有键的列表都是相同的,因此您可以通过为每个组只收集一个列表然后将它们映射到原始 A 值来节省工作。

使用 Stream API,解决方案可能看起来像

List<Foo> list = Arrays.asList(new Foo("FM11","1"), new Foo("FM1122","2"));

Map<String,List<String>> groups = list.stream()
    .collect(Collectors.groupingBy(foo -> foo.getA().substring(0, 4),
        Collectors.mapping(Foo::getB, Collectors.toList())));

Map<String,List<String>> result = list.stream()
    .collect(Collectors.toMap(Foo::getA, foo -> groups.get(foo.getA().substring(0, 4))));

result.forEach((k,v) -> System.out.println(k+" -> "+v));
FM1122 -> [1, 2]
FM11 -> [1, 2]

在不使用 Stream API 的情况下,您可以更有效地执行此操作。通过循环,我们可以一次性完成这两项操作,将在地图中查找组的工作减半。

Map<String,List<String>> groups = new HashMap<>(), result = new HashMap<>();

for(Foo foo: list) {
  List<String> bList
      = groups.computeIfAbsent(foo.getA().substring(0, 4), x -> new ArrayList<>());
  bList.add(foo.getB());
  result.put(foo.getA(), bList);
}

结果是一样的。

【讨论】:

    【解决方案2】:

    试试这个

    List<Foo> list = new ArrayList<>();
    list.add(new Foo("FM11","1"));
    list.add(new Foo("FM11","2"));
    Map<String,List<String>> result =  list.stream()
        .collect(Collectors.toMap(foo -> foo.getA(), foo -> list.stream()
            .filter(foo2-> foo2.getA().substring(0, 4).equals(foo.getA().substring(0, 4)))
            .map(Foo::getB)
            .collect(Collectors.toList())));
    

    输出

    {FM1122=[1, 2], FM11=[1, 2]}
    

    【讨论】:

    • 我认为 OP 希望使用 Foo.a 作为键并将所有 Foo.bFoo.a 与当前键具有相同前缀的值列表添加到值列表中。他的列表只有两个条目,并且不包括您所包括的 list.add(new Foo("FM11","2"));
    • @Eritrean Hm...不确定实际要求是什么,因为 OP 只是简单地说 grouping by similar key with java-lambda
    • 这是非常低效的(二次时间复杂度)。它还创建了许多不必要的子字符串实例。您可以使用foo2.getA().regionMatches(0, foo.getA(), 0, 4),而不是foo2.getA().substring(0, 4).equals(foo.getA().substring(0, 4))。但它并没有解决执行list.size() × list.size() 操作的一般问题……
    【解决方案3】:

    对于列表中的每个 foo,您可以再次遍历列表并过滤那些具有相同属性 a 前缀的列表。暂时将其保存到临时变量并将其添加到您的地图中。

        List<Foo> list = new ArrayList<>();
        list.add(new Foo("FM11","1"));
        list.add(new Foo("FM1122","2"));
        Map<String,List<String>> map = new HashMap<>();
        list.forEach(f -> {
            List<String> temp = list.stream()
                    .filter(e->e.getA().substring(0, 4).equals(f.getA().substring(0, 4)))
                    .map(e->e.getB())
                    .collect(Collectors.toList());
    
            map.computeIfAbsent(f.getA(), k -> new ArrayList<>()).addAll(temp);
        });
        System.out.println(map);
    

    【讨论】:

      猜你喜欢
      • 2020-05-26
      • 2022-08-10
      • 1970-01-01
      • 1970-01-01
      • 2022-01-13
      • 1970-01-01
      • 2011-01-14
      • 1970-01-01
      • 2011-06-27
      相关资源
      最近更新 更多