Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java8流:复杂流处理_Java_Java 8_Java Stream - Fatal编程技术网

Java8流:复杂流处理

Java8流:复杂流处理,java,java-8,java-stream,Java,Java 8,Java Stream,我想创建一个方法,在不缓存整个流的情况下对流执行一些复杂的操作(例如替换第7个元素、删除最后一个元素、删除相邻的重复项等) 但是什么样的流api允许我插入这个方法呢?我是否必须创建自己的收集器,以便在收集时将项目发送到其他流?但这将改变数据流从拉向推的方向,对吗 这种方法的可能特征是什么 Stream<T> process(Stream<T> in) 流处理(流入) 这可能是不可能的(在单线程代码中),因为只有在收集整个输入流之后才能返回结果 另一个想法是: void

我想创建一个方法,在不缓存整个流的情况下对流执行一些复杂的操作(例如替换第7个元素、删除最后一个元素、删除相邻的重复项等)

但是什么样的流api允许我插入这个方法呢?我是否必须创建自己的收集器,以便在收集时将项目发送到其他流?但这将改变数据流从拉向推的方向,对吗

这种方法的可能特征是什么

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