Java计算整数数组中每个元素出现的次数

8

我写了以下代码段来计算每个元素出现的次数。有没有更短的方法可以实现这个功能?

int[] arr = {1, 6, 2, 8, 5, 4, 7, 7, 5, 7};
Arrays.stream(arr)
        .collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
        .stream()
        .collect(Collectors.groupingBy(s -> s))
        .forEach((k, v) -> System.out.println(k+" "+v.size()));

我希望您只显示出现超过1次的元素。因此,我尝试进行以下修改,结果出现错误。
.forEach((k, v) -> if(v.size() > 1) System.out.println(k+" "+v.size()));

这该如何正确实现?
5个回答

12

对于后一个问题,您必须进行更改。

.forEach((k, v) -> if(v.size() > 1) System.out.println(k+" "+v.size()));

.forEach((k, v) -> {if(v.size() > 1) System.out.println(k+" "+v.size());});

对于第一部分,不清楚为什么需要第一个collect之后跟着第二个Stream管道。如果目的是将IntStream转换为Stream<Integer>,请使用boxed()

Arrays.stream(arr)
      .boxed()
      .collect(Collectors.groupingBy(s -> s))
      .forEach((k, v) -> System.out.println(k+" "+v.size()));

正如Dici所建议的那样,您还可以链式使用Collectors来将每个数字与其出现次数分组:

Map<Integer,Integer> occurrences = 
    Arrays.stream(arr)
          .boxed()
          .collect(Collectors.groupingBy(s -> s, Collectors.counting()));

4
使用Collectors.groupingBy(Function.identity(), Collectors.counting())是一种稍微更好的方法。结合一些静态导入,代码看起来会更加美观。 - Dici
还可以将结果作为Map<Integer,Integer>返回,而不是将其打印出来吗? - DhiwaTdG
1
@DhiwaTdG 是的,你可以使用Dici的建议。 - Eran
我尝试过那个,但它返回了所有的值,而我希望只返回出现超过一次的元素。 - DhiwaTdG
1
@DhiwaTdG 你可能需要通过另一个流水线来过滤掉只出现一次的数字。我不确定你是否可以在同一个流水线中完成这个操作。 - Eran

3
如果您愿意使用第三方库,Eclipse Collections有一个名为“Bag”的类型,可以按以下方式使用:
Bags.mutable.with(1, 6, 2, 8, 5, 4, 7, 7, 5, 7)
    .selectDuplicates()
    .forEachWithOccurrences((k, count) -> System.out.println(k+" "+count));

如果您必须将 int[] arr 变量保持为 int 数组,则可以按以下方式使用 IntBag
IntBags.mutable.with(arr)
    .selectDuplicates()
    .forEachWithOccurrences((k, count) -> System.out.println(k+" "+count));

IntBag 是一个原始集合,因此不会将 int 值装箱成 Integer 包装器。

注意:我是 Eclipse Collections 的提交者。


2
List<Integer> numsList=Arrays.asList(1,2,3,5,2,4,3,1,2,2,4,4,5);
Map<Integer, Long> map=numsList.stream().collect(Collectors.groupingBy(Integer::intValue,Collectors.counting()));
map.forEach((k,v)->{System.out.println(k+" = "+v);});

0

我也想分享我的解决方案!!!

// Solution 1 [Improved from Eran's solution & suggestion]
int[] arr = {1, 6, 2, 8, 5, 4, 7, 7, 5, 7};
Map<Integer, Long> counts = Arrays.stream(arr)
    .boxed()
    .collect(collectingAndThen(groupingBy(n -> n, counting()),
        map -> map.entrySet().stream()
            .filter(n -> n.getValue() > 1)
            .collect(toMap(Entry::getKey, Entry::getValue))
));
System.out.println(counts.toString());

// Solution 2 [Improved from Dici's suggestion]
int[] arr = {1, 6, 2, 8, 5, 4, 7, 7, 5, 7};
Map<Object, Long> counts = Arrays.stream(arr)
    .collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
    .stream()
    .collect(groupingBy(Function.identity(), counting()));
counts.values().removeIf(count -> count < 2);
System.out.println(counts.toString());  

1
请注意,通过 groupingBy 规范,并不能保证返回的映射是可变的。因此,这样的解决方案在未来的 Java (或非 OpenJDK-based 的实现中) 可能会失败。 - Tagir Valeev
1
你可以通过使用.collect(groupingBy(Function.identity(), HashMap::new, counting()))轻松解决@Tagir提到的问题。然后,结果保证是可变的HashMap,你可以使用removeIf。请注意,这也可以与collectingAndThen一起使用。顺便说一下,在使用System.out.println时不需要toString()... - Holger

0

也可以使用频率来完成:

 List<Integer> list = ImmutableList.of(1, 2, 3, 4, 5, 6, 3, 4, 5);
 Map<Integer, Integer> result = list.stream().distinct().collect(Collectors.toMap(Function.identity(), token -> Collections.frequency(list, token)));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接