【问题标题】:How to count groups of integers within arrays, without sorting the array?如何计算数组中的整数组,而不对数组进行排序?
【发布时间】:2019-08-21 09:07:30
【问题描述】:

我的目标是能够计算数组中相同整数的组。例如,在这样的数组中,{1, 1, 1, 2, 2, 3, 1, 1},有 4 个组

  • 至少大小为 1:3 组
  • 至少大小为 2:1 组
  • 至少大小为 3

我在没有对数组进行排序的情况下完成此操作时遇到了问题。当它被排序时,我失去了数组末尾的两个 1 组的计数,因为它被放在其他 1 组旁边。

int result = (int) Stream.of(1, 1, 1, 2, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5, 4, 4, 4, 6)
        .collect(Collectors.groupingBy(i -> i))
        .entrySet().stream()
        .filter(entry -> entry.getValue().size() >= 1) // specify the size
        .count()

return result;

每种尺寸的预期输出如下:

size 1 count == 8

size 2 count == 5

size 6 count == 1

size 8 count == 1

实际输出如下:

size 1 count == 6

size 2 count == 3

size 6 count == 2

size 8 count == 1

差异是数组在计数之前排序的结果。有没有办法做到这一点?

编辑: 组本质上是相同整数重复的任何地方,直到一个不同值的整数在它前面;因此,此代码中大小为 2 的组是索引 0 到 2(包括)、索引 4 - 5(包括)、索引 6 - 15(包括)、索引 16 - 18(包括)和索引 20 -22 的任何组(含)。由于有 5 个组的大小至少为 2,因此应返回 5 个计数。

为了我的目标而编写的命令式代码。

Scanner key = new Scanner("1 1 1 2 1 1 3 3 3 3 3 3 3 3 3 3 4 4 4 5 4 4 4 6");

        int cnt = 0;
        int counter = 0;
        int i = 0;

            while(key.hasNextInt()) {   
                int next = key.nextInt();

                if(next == array[i]) {
                    counter++;
                }

                if(i + 1 < array.length && i -1 >= 0 
                   && counter >=size  
                   && next != array[i + 1] 
                   && next == array[i-size + 1]) {
                    cnt++;
                    counter = 0;
                }
                i++;
            }
        return cnt;

此操作的预期回报与上述相同。

实际回报为:

size 1 count == 7

size 2 count == 5

size 6 count == 3

size 8 count == 1

这个循环的问题是我相信它跳过了数组的第一部分和结束部分。

我没有遇到与 Stream 方式相同的排序问题。

理想情况下,这不需要任何外部实用程序/库

【问题讨论】:

  • 您能否解释一下,根据提供的输入,size 1 count == 8 是怎样的?您能否也定义组并分享循环代码的命令式风格来解释您打算做什么?
  • 已进行更改以使其更加清晰。感谢您的回复。

标签: java arrays java-stream counting


【解决方案1】:

首先,让我先说这并不是 Stream API 的真正用途,但是,它是可能的,虽然可能不是最优雅的方式,但它仍然是可能的。

这里有一个可能的解决方案供您使用

  1. 将所有内容转换为大字符串,然后在数字不同的地方拆分。
  2. 现在通过流传输并收集组及其计数
  3. 将较高组的数量添加到每个较小的组(以获得at least of 逻辑)
  4. 那里,你应该有你需要的所有信息

演示

String[] groups = Stream.of(1, 1, 1, 2, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5, 4, 4, 4, 6)
        .map(String::valueOf)
        .collect(Collectors.joining(","))
        .split("(?<=(\\d))(,)(?!\\1)");

Map<Integer, Long> groupedByGroupSizes = Arrays.stream(groups)
        .map(group -> group.split(",").length)
        .collect(Collectors.groupingBy(x -> x, Collectors.counting()));

TreeMap<Integer, Long> integerLongTreeMap = new TreeMap<>(groupedByGroupSizes);
int size = integerLongTreeMap.size();

for (Integer integer : integerLongTreeMap.keySet()) {
    Long value = integerLongTreeMap.get(integer);
    integerLongTreeMap.put(integer, value + --size);
}

integerLongTreeMap.entrySet().forEach(entry -> System.out.println(String.format("of at least size %s: %s groups", entry.getKey(), entry.getValue())));

打印

of at least size 1: 6 groups
of at least size 2: 3 groups
of at least size 3: 4 groups
of at least size 10: 1 groups

【讨论】:

    【解决方案2】:

    首先我建议找到所有子组。为此,您可以将Stream.collect() 与自定义收集器一起使用:

    List<List<Integer>> sublists = IntStream.of(1, 1, 1, 2, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5, 4, 4, 4, 6)
            .collect(ArrayList::new, (lists, value) -> {
                if (lists.isEmpty() || lists.get(lists.size() - 1).stream().noneMatch(e -> e == value)) {
                    lists.add(new ArrayList<>());
                }
                lists.get(lists.size() - 1).add(value);
            }, (l1, l2) -> {
                throw new RuntimeException("not supported for parallel streams");
            });
    

    结果是:

    [[1, 1, 1], [2], [1, 1], [3, 3, 3, 3, 3, 3, 3, 3, 3, 3], [4, 4, 4], [5], [4, 4, 4], [6]]
    

    现在您可以使用它来对列表大小进行分组:

    Map<Integer, Long> result = sublists.stream()
            .collect(Collectors.groupingBy(List::size, Collectors.counting()));
    result.forEach((size, count) -> System.out.println(String.format("size %s count %s", size, count)));
    

    这会找到所有现有的组大小和打印:

    size 1 count 3
    size 2 count 1
    size 3 count 3
    size 10 count 1
    

    要计算具有最小长度的所有组,您可以使用:

    Map<Integer, Long> result = IntStream.rangeClosed(1, sublists.stream().mapToInt(List::size).max().orElse(0)).boxed()
            .collect(Collectors.toMap(Function.identity(), i -> sublists.stream().filter(l -> l.size() >= i).count()));
    result.forEach((size, count) -> System.out.println(String.format("size %s count %s", size, count)));
    

    打印出来:

    size 1 count 8
    size 2 count 5
    size 3 count 4
    size 4 count 1
    size 5 count 1
    size 6 count 1
    size 7 count 1
    size 8 count 1
    size 9 count 1
    size 10 count 1
    

    要仅获得一组预定义的大小(例如1, 2, 6, 8),您可以修改最后一个解决方案:

    Map<Integer, Long> result = IntStream.of(1, 2, 6, 8).boxed()
            .collect(Collectors.toMap(Function.identity(), i -> sublists.stream().filter(l -> l.size() >= i).count()));
    result.forEach((size, count) -> System.out.println(String.format("size %s count %s", size, count)));
    

    这样的结果是:

    size 1 count 8
    size 2 count 5
    size 6 count 1
    size 8 count 1
    

    【讨论】:

      【解决方案3】:

      如果使用StreamEx 是一个选项,它会很容易

      IntStream s = IntStream.of(1, 1, 1, 2, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5, 4, 4, 4, 6);
      List<List<Integer>> t = IntStreamEx.of (s).boxed().groupRuns(Integer::equals).toList();
      t.forEach(e -> {
        System.out.println(String.format("Group of %s - %s times", e.get(0), e.size()));
      });
      

      【讨论】:

        猜你喜欢
        • 2018-06-06
        • 1970-01-01
        • 1970-01-01
        • 2013-01-20
        • 1970-01-01
        • 2021-06-23
        • 1970-01-01
        • 1970-01-01
        • 2014-05-27
        相关资源
        最近更新 更多