Stream takeWhile 用于同一管道中的排序流

Stream takeWhile for a sorted stream in the same pipeline

我正在尝试使用 java9 的 takeWhile 来过滤已排序的流,以从流的开头获取具有相同字段值的对象。我无法为此编写正确的谓词。这可以通过打破流管道的两个步骤来完成。

Stream.of(objA, objB, objC, objD, objE, objF, objG)
    .takeWhile(" get the value of objA´s field and take 
   as long as as other objects value for that field is the same as objA´s");

分两步我可以做类似的事情

int x = Stream.of(objA, objB, objC, objD, objE, objF, objG).findFirst().get().getSomeValue();

Stream.of(objA, objB, objC, objD, objE, objF, objG).takeWhile(e -> e.getSomeValue() == x);

一个简化的例子可以是

Stream.of(5,5,5,5,13,14,5,5,2,5,5,6)
.takeWhile(get the first four '5'´s)

可以不使用 optional.get 的中间步骤来完成吗?

一个(有点笨拙的)解决方法是使用一个可变的 holder 对象(因为引用必须是 effectively final),它包含 null 或第一个值。我们将在此示例中使用 AtomicReference

AtomicReference<MyClass> holder = new AtomicReference<>();

sortedStream.takeWhile(e -> holder.get() == null || holder.get().equals(e))
.map(e -> { holder.set(e); return e; })
.collect(Collectors.toList());

现在这仍然包含一个额外的步骤,但是由于 Stream 只能处理一次,所以 findFirst() 方法无论如何都行不通。这也会继续设置 holder 对象,即使它已被设置,但这只是一个小麻烦而不是问题。

正如 Holger 所指出的,更好、更精简的版本是

sortedStream.takeWhile(e -> holder.compareAndSet(null, e.getSomeValue()) || e.getSomeValue() == holder.get())
.collect(Collectors.toList());

其中第一个元素 compareAndSet 赋值,返回 true,随后的元素与持有者的值进行比较。

如果您确实使用这种方法,我建议添加指向此 question/answer 的注释,或者至少尝试以某种方式解释这段代码的实现,因为阅读代码的人可能不会立即明白这一点.