【问题标题】:Stateful filter for ordered stream有序流的状态过滤器
【发布时间】:2018-10-11 13:20:38
【问题描述】:

我有一个问题,我想知道是否有使用 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 ...

我有可能这样做吗?

【问题讨论】:

  • 您可以将Stream 更改为List 吗?在这种情况下,事情会简单得多
  • "不,这不可能使用流,至少不容易。流 API 抽象出处理元素的顺序:流可以并行处理,也可以以相反的顺序处理. 所以“下一个元素”和“前一个元素”在流抽象中是不存在的。 stackoverflow.com/questions/31650157/…
  • 顺便说一句,让我们从你的输出中只取前三个数字,为​​什么24 在那里?需要解释一下吗?
  • 所以区别是不是前一个,而是下一个,你现在也想要绝对值;当然你的描述和你的实际例子没有任何意义
  • 流绝对不是为了这个,这个问题确实是关于一些 for 循环和一段时间很可能

标签: java java-stream


【解决方案1】:

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

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

一个例子:

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&lt;Integer&gt;(或其他对象类型)的概念类似。

【讨论】:

    【解决方案2】:

    流不是为此类任务而设计的。我会使用不同的方式来完成这个,它不使用流。但是,如果你真的必须使用流,由于流和 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 是一个单元素数组。这是一个 hack,因为不允许 lambda 修改变量(它可以修改数组的元素,但不能修改数组本身)。

    【讨论】:

    • 您不需要在流中使用 lambda。您还可以使用实现谓词的对象(例如IntPredicate)。
    猜你喜欢
    • 2013-05-12
    • 2013-02-22
    • 2021-05-29
    • 2014-08-09
    • 2018-05-28
    • 2016-09-13
    • 1970-01-01
    • 1970-01-01
    • 2022-10-24
    相关资源
    最近更新 更多