检测流中的重复组

Mic*_*das 10 java java-8 java-stream

我想确保列表中的所有数字都组合在一起.让我通过例子解释一下:

{1, 1, 1, 2, 2}    // OK, two distinct groups
{1, 1, 2, 2, 1, 1} // Bad, two groups with "1"
{1, 2, 3, 4}       // OK, 4 distinct groups of size 1
{1, 1, 1, 1}       // OK, 1 group
{3, 4, 3}          // Bad, two groups with "3"
{99, -99, 99}      // Bad, two groups with "99"
{}                 // OK, no groups
Run Code Online (Sandbox Code Playgroud)

这是我获取流的方式:

IntStream.of(numbers)
    ...
Run Code Online (Sandbox Code Playgroud)

现在我需要为"OK"示例传递或返回true,并AssertionError在"坏"示例上抛出或返回false.如何使用Stream API执行此操作?

这是我目前使用附加Set创建的解决方案:

Set<Integer> previousNumbers = new HashSet<>();
IntStream.of(numbers)
        .reduce(null, (previousNumber, currentNumber) -> {
                    if (currentNumber == previousNumber) {
                        assertThat(previousNumbers).doesNotContain(currentNumber);
                        previousNumbers.add(currentNumber);
                    }
                    return currentNumber;
                }
        );
Run Code Online (Sandbox Code Playgroud)

Tag*_*eev 6

使用我的免费StreamEx库:

IntStreamEx.of(numbers).boxed().runLengths().toMap();
Run Code Online (Sandbox Code Playgroud)

IllegalStateException如果有重复的组,则抛出此代码.

这里使用runLengths()方法.它折叠相等的相邻元素,用Map.Entrykey 替换它们,其中key是input元素,value是重复的数量.最后toMap()使用哪个是快捷方式.collect(Collectors.toMap(Entry::getKey, Entry::getValue)).我们正在使用在重复键时.toMap()抛出的事实IllegalStateException(除非提供了自定义mergeFunction).

作为成功执行的免费奖励,您将拥有一个地图,其中键是输入元素,值是系列的长度.


Flo*_*own 5

在我看来,这个问题根本不适合Stream API,但我很好奇这是如何实现的(但是以一种高效的方式).

问题是你必须跟踪看到的元素,整个测试应该有短路行为.所以我想出了这个解决方案(没有Streams):

public static boolean hasUniqueGroups(int[] arr) {
    Objects.requireNonNull(arr);
    Set<Integer> seen = new HashSet<>();
    for (int i = 0; i < arr.length; i++) {
        if (i == 0 || arr[i] != arr[i - 1]) {
            if (!seen.add(arr[i])) {
                return false;
            }
        }
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

下一步是介绍Stream API和解决方案如下所示:

public static boolean hasUniqueGroups(int[] arr) {
    Objects.requireNonNull(arr);
    Set<Integer> seen = new HashSet<>();
    return IntStream.range(0, arr.length)
            .filter(i -> i == 0 || arr[i] != arr[i - 1])
            .mapToObj(i -> arr[i])
            .allMatch(seen::add);
}
Run Code Online (Sandbox Code Playgroud)

注意:为了并行化,Stream您应该使用线程安全的Set.

  • @StuartMarks使用`Collector`是我的第一次尝试,但它没有短路行为.因此,它不适用于此问题. (3认同)
  • 不错,+1.这里的关键见解是谓词`arr [i]!= arr [i-1]`检测到组的开头.对于更一般的问题,我已经使用了一个收集器来生成结果,但对于这个特殊情况,使用`allMatch(seen :: add)`非常聪明.顺便说一句,名称`hasMultipleGroups`有错误的意义; 也许`hasUniqueGroups`会更好吗? (2认同)