Java8流:复杂流处理
我想创建一个方法,在不缓存整个流的情况下对流执行一些复杂的操作(例如替换第7个元素、删除最后一个元素、删除相邻的重复项等) 但是什么样的流api允许我插入这个方法呢?我是否必须创建自己的收集器,以便在收集时将项目发送到其他流?但这将改变数据流从拉向推的方向,对吗 这种方法的可能特征是什么Java8流:复杂流处理,java,java-8,java-stream,Java,Java 8,Java Stream,我想创建一个方法,在不缓存整个流的情况下对流执行一些复杂的操作(例如替换第7个元素、删除最后一个元素、删除相邻的重复项等) 但是什么样的流api允许我插入这个方法呢?我是否必须创建自己的收集器,以便在收集时将项目发送到其他流?但这将改变数据流从拉向推的方向,对吗 这种方法的可能特征是什么 Stream<T> process(Stream<T> in) 流处理(流入) 这可能是不可能的(在单线程代码中),因为只有在收集整个输入流之后才能返回结果 另一个想法是: void
Stream<T> process(Stream<T> in)
流处理(流入)
这可能是不可能的(在单线程代码中),因为只有在收集整个输入流之后才能返回结果
另一个想法是:
void process(Stream<T> in, Stream<T> out)
void流程(流入、流出)
由于java不允许向现有流中插入项(作为out
参数提供),因此也似乎有点缺陷
那么,我如何在java中进行一些复杂的流处理呢?您可以调用并返回任何标准流操作,例如
筛选,映射,减少,等等,并让它们执行一些复杂的操作,例如需要外部数据的操作。例如,FilterAjacentDuplicates
和ReplacentElement
可以这样实现:
public static <T> Stream<T> filterAdjacentDupes(Stream<T> stream) {
AtomicReference<T> last = new AtomicReference<>();
return stream.filter(t -> ! t.equals(last.getAndSet(t)));
}
public static <T> Stream<T> replaceNthElement(Stream<T> stream, int n, T repl) {
AtomicInteger count = new AtomicInteger();
return stream.map(t -> count.incrementAndGet() == n ? repl : t);
}
StreamEx.of(stream)
.zipWith(IntStreamEx.ints().boxed())
.removeValues(pos -> pos == idx)
.keys();
公共静态流过滤器相邻重复(流){
AtomicReference last=新的AtomicReference();
返回stream.filter(t->!t.equals(last.getAndSet(t));
}
公共静态Stream replaceNthElement(Stream Stream,int n,T repl){
AtomicInteger计数=新的AtomicInteger();
返回stream.map(t->count.incrementAndGet()==n?repl:t);
}
用法示例:
List<String> lst = Arrays.asList("foo", "bar", "bar", "bar", "blub", "foo");
replaceNthElement(filterAdjacentDupes(lst.stream()), 3, "BAR").forEach(System.out::println);
// Output: foo bar BAR foo
List lst=Arrays.asList(“foo”、“bar”、“bar”、“blub”、“foo”);
replacenthement(filterajacentdupes(lst.stream()),3,“BAR”).forEach(System.out::println);
//输出:foo bar foo
然而,正如注释中所指出的,这并不是流API应该如何使用的。特别是,当给定并行流时,这两种操作将失败。您可以调用并返回任何标准流操作,例如过滤器,映射,减少,等等,并让它们执行一些复杂的操作,例如需要外部数据的操作。例如,FilterAjacentDuplicates
和ReplacentElement
可以这样实现:
public static <T> Stream<T> filterAdjacentDupes(Stream<T> stream) {
AtomicReference<T> last = new AtomicReference<>();
return stream.filter(t -> ! t.equals(last.getAndSet(t)));
}
public static <T> Stream<T> replaceNthElement(Stream<T> stream, int n, T repl) {
AtomicInteger count = new AtomicInteger();
return stream.map(t -> count.incrementAndGet() == n ? repl : t);
}
StreamEx.of(stream)
.zipWith(IntStreamEx.ints().boxed())
.removeValues(pos -> pos == idx)
.keys();
公共静态流过滤器相邻重复(流){
AtomicReference last=新的AtomicReference();
返回stream.filter(t->!t.equals(last.getAndSet(t));
}
公共静态Stream replaceNthElement(Stream Stream,int n,T repl){
AtomicInteger计数=新的AtomicInteger();
返回stream.map(t->count.incrementAndGet()==n?repl:t);
}
用法示例:
List<String> lst = Arrays.asList("foo", "bar", "bar", "bar", "blub", "foo");
replaceNthElement(filterAdjacentDupes(lst.stream()), 3, "BAR").forEach(System.out::println);
// Output: foo bar BAR foo
List lst=Arrays.asList(“foo”、“bar”、“bar”、“blub”、“foo”);
replacenthement(filterajacentdupes(lst.stream()),3,“BAR”).forEach(System.out::println);
//输出:foo bar foo
然而,正如注释中所指出的,这并不是流API应该如何使用的。特别是,当给定并行流时,这两种操作将失败。作为示例使用的复杂操作都遵循流中一个元素的操作模式,这取决于流中的其他元素。Java流是专门设计的,不允许在没有收集或减少的情况下进行这些类型的操作。Streams操作不允许直接访问其他成员,一般来说,具有副作用的非终端操作是一个坏主意
请注意流
javadoc中的以下内容:
集合和流虽然表面上有一些相似之处,但目标不同。集合主要涉及对其元素的有效管理和访问。相反,流不提供直接访问或操作其元素的方法,而是以声明方式描述其源以及将在该源上聚合执行的计算操作
更具体地说:
大多数流操作接受描述用户指定行为的参数。。。要保留正确的行为,请使用以下行为参数:
必须无干扰(它们不会修改流源);和
在大多数情况下,必须是无状态的(它们的结果不应该依赖于流管道执行期间可能更改的任何状态)
及
如果流操作的行为参数是有状态的,则流管道结果可能是不确定的或不正确的。有状态lambda(或实现适当功能接口的其他对象)的结果取决于流管道执行期间可能更改的任何状态
itermediate和terminal无状态和有状态操作的所有复杂性都在和中进行了详细描述
这种方法既有优点也有缺点。一个显著的优点是它允许并行处理流。一个显著的缺点是,在其他一些语言中很容易的操作(例如跳过流中的每三个元素)在Java中很困难
注意,您将看到许多代码(包括接受的答案)忽略了流操作的行为参数应该是无状态的这一建议。为了工作,此代码依赖于语言规范未定义的Java实现的行为:即,流按顺序处理。规范中没有任何内容可以阻止Java处理元素以相反顺序或随机顺序实现。这样的实现将使任何有状态流操作立即表现出不同的行为。无状态操作的行为将继续完全相同。因此,概括地说,有状态操作依赖于
StreamEx.of(stream)
.zipWith(IntStreamEx.ints().boxed())
.removeValues(pos -> pos == idx)
.keys();
StreamEx.of(stream).collapse(Object::equals);
public class Foo {
public static void run() {
List<String> lst = Arrays.asList("foo", "bar", "bar", "bar", "blub", "foo");
lst.stream()
.filter(Foo.filterAdjacentDupes())
.map(Foo.replaceNthElement(3, "BAR"))
.forEach(System.out::println);
// Output: foo bar BAR foo
}
public static <T> Predicate<T> filterAdjacentDupes() {
final AtomicReference<T> last = new AtomicReference<>();
return t -> ! t.equals(last.getAndSet(t));
}
public static <T> UnaryOperator<T> replaceNthElement(int n, T repl) {
final AtomicInteger count = new AtomicInteger();
return t -> count.incrementAndGet() == n ? repl : t;
}
}