简体   繁体   English

有序流的状态过滤器

[英]Stateful filter for ordered stream

I have a problem and I wonder if there is a solution using Streams. 我有一个问题,我想知道是否有使用Streams的解决方案。

Imagine you have an ordered stream of Objects; 想象一下,你有一个有序的物体流; let's assume a stream of Integers. 让我们假设一个整数流。

 Stream<Integer> stream = Stream.of(2,20,18,17,4,11,13,6,3,19,4,10,13....)

Now I want to filter all values where the difference of a value and the previous number before this value is greater than n . 现在我想过滤所有值,其中值与此值之前的前一个数字之差大于n

stream.filter(magicalVoodoo(5))
// 2, 20, 4, 11, 3, 19, 4, 10 ...

I there any possibility to do this? 我有可能这样做吗?

Yes, this is possible, but you will need a stateful predicate that keeps track of the previous value for doing the comparison. 是的,这是可能的,但您需要一个有状态谓词来跟踪进行比较的先前值。 This does mean it can only be used for sequential streams: with parallel streams you'd run into race conditions. 这意味着它只能用于顺序流:对于并行流,您将遇到竞争条件。

Luckily, most streams default to sequential, but if you need to do this on streams from an unknown source, you may want to check using isParallel() and either throw an exception, or convert it to a sequential stream using sequential() . 幸运的是,大多数流默认为顺序,但如果您需要在来自未知源的流上执行此操作,您可能需要使用isParallel()进行检查并抛出异常,或使用sequential()将其转换为顺序流。

An example: 一个例子:

public class DistanceFilter implements IntPredicate {

    private final int distance;
    private int previousValue;

    public DistanceFilter(int distance) {
        this(distance, 0);
    }

    public DistanceFilter(int distance, int startValue) {
        this.distance = distance;
        this.previousValue = startValue;
    }

    @Override
    public boolean test(int value) {
        if (Math.abs(previousValue - value) > distance) {
            previousValue = value;
            return true;
        }
        return false;
    }

    // Just for simple demonstration
    public static void main(String[] args) {
        int[] ints = IntStream.of(2, 20, 18, 17, 4, 11, 13, 6, 3, 19, 4, 10, 13)
                .filter(new DistanceFilter(5))
                .toArray();

        System.out.println(Arrays.toString(ints));
    }
}

I used IntStream here, because it is a better type for this, but the concept would be similar for Stream<Integer> (or other object types). 我在这里使用了IntStream ,因为它是一个更好的类型,但是Stream<Integer> (或其他对象类型)的概念类似。

Streams are not designed for this kind of task. 流不是为此类任务而设计的。 I would use a different way to accomplish this, which doesn't use streams. 我会使用不同的方法来实现这一点,它不使用流。 But, if you really must use streams, the solution has to circumvent certain restrictions due to the design of streams and lambdas, and therefore looks quite hacky: 但是,如果你真的必须使用流,那么解决方案必须避免由于流和lambda的设计而导致的某些限制,因此看起来非常h​​acky:

int[] previous = new int[1];
previous[0] = firstElement;
... = stream.filter(n -> {
        boolean isAllowed = (abs(n - previous[0]) > 5);
        if (isAllowed)
            previous[0] = n;
        return isAllowed;})

Notice that the variable previous is a one-element array. 请注意,变量previous是一个单元素数组。 That's a hack due to the fact that the lambda is not allowed to modify variables (it can modify an element of an array, but not the array itself). 这是一个hack,因为lambda不允许修改变量(它可以修改数组的元素,但不能修改数组本身)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM