有序流的状态过滤器

时间:2018-10-11 13:20:39

标签: java java-stream

我有问题,我想知道是否有使用Streams的解决方案。

想象一下,您有一个订购的对象流;让我们假设一个整数流。

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

现在,我想过滤一个值与该值之前的前一个数字之差大于 n 的所有值。

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

我有可能这样做吗?

2 个答案:

答案 0 :(得分:3)

是的,这是可能的,但是您将需要一个有状态的谓词来跟踪进行比较的先前值。这确实意味着它只能用于顺序流:使用并行流时,您会遇到竞争状况。

幸运的是,大多数流默认为顺序流,但是如果您需要对来自未知源的流执行此操作,则可能需要使用isParallel()进行检查并抛出异常,或者使用以下命令将其转换为顺序流sequential()

一个例子:

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));
    }
}

我在这里使用了IntStream,因为它是一个更好的类型,但是对于Stream<Integer>(或其他对象类型)来说,这个概念是相似的。

答案 1 :(得分:0)

流不是为此类任务而设计的。我将使用另一种方式来完成此任务,而不使用流。但是,如果您确实必须使用流,则由于流和lambda的设计,该解决方案必须规避某些限制,因此看起来很hacky:

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;})

请注意,变量previous是一个单元素数组。之所以如此,是因为不允许lambda修改变量(它可以修改数组的元素,但不能修改数组本身)。