Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/334.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
Java “注册流”;“完成”;钩_Java_Java 8_Java Stream - Fatal编程技术网

Java “注册流”;“完成”;钩

Java “注册流”;“完成”;钩,java,java-8,java-stream,Java,Java 8,Java Stream,使用Java 8StreamAPI,我想注册一个“完成挂钩”,大致如下: Stream<String> stream = Stream.of("a", "b", "c"); // additional filters / mappings that I don't control stream.onComplete((Completion c) -> { // This is what I'd like to do: closeResources();

使用Java 8
Stream
API,我想注册一个“完成挂钩”,大致如下:

Stream<String> stream = Stream.of("a", "b", "c");

// additional filters / mappings that I don't control
stream.onComplete((Completion c) -> {
    // This is what I'd like to do:
    closeResources();

    // This might also be useful:
    Optional<Throwable> exception = c.exception();
    exception.ifPresent(e -> throw new ExceptionWrapper(e));
});
而不是当前需要的:

try (Stream<X> meh = Utility.something()
                            .niceLookingSQLDSL()
                            .moreDSLFeatures()
                            .stream()) {

    Collected collectedWithUglySyntacticDissonance =
    meh.filter(a -> true)
       .map(c -> c)
       .collect(collector);
}

使用现有的JDK 8 API有没有一种简单的方法可以做到这一点?

我提出的解决方案如下所示:

class AutoClosingStream<T> implements Stream<T> {

    AutoClosingStream(Stream<T> delegate, Consumer<Optional<Throwable>> onComplete) {}

    // Pipeline ops delegate the op to the real stream and wrap that again
    @Override
    public Stream<T> limit(long maxSize) {
        return new AutoClosingStream(delegate.limit(maxSize), onComplete);
    }

    // Terminal ops intercept the result and call the onComplete logic
    @Override
    public void forEach(Consumer<? super T> action) {
        terminalOp(() -> delegate.forEach(action));
    }

    private void terminalOp(Runnable runnable) {
        terminalOp(() -> { runnable.run(); return null; });
    }

    private <R> R terminalOp(Supplier<R> supplier) {
        R result = null;

        try {
            result = supplier.get();
            onComplete.accept(Optional.empty());
        }
        catch (Throwable e) {
            onComplete.accept(Optional.of(e));
            Utils.sneakyThrow(e);
        }

        return result;
    }
}
类AutoClosingStream实现流{
AutoClosingStream(流委托,使用者onComplete){}
//管道操作将操作委托给真实流,并再次包装该流
@凌驾
公共流限制(长最大大小){
返回新的AutoClosingStream(委托限制(maxSize),onComplete);
}
//终端ops截取结果并调用onComplete逻辑
@凌驾

public void forEach(Consumer最简单的解决方案是将一个流包装到另一个流中,并将其平面映射到自身:

// example stream
Stream<String> original=Stream.of("bla").onClose(()->System.out.println("close action"));

// this is the trick
Stream<String> autoClosed=Stream.of(original).flatMap(Function.identity());

//example op
int sum=autoClosed.mapToInt(String::length).sum();
System.out.println(sum);
//示例流
Stream original=Stream.of(“bla”).onClose(()->System.out.println(“close action”);
//这就是诀窍
Stream autoClosed=Stream.of(original).flatMap(Function.identity());
//示例op
int sum=autoClosed.mapToInt(字符串::长度).sum();
系统输出打印项数(总和);
其有效的原因在于:

每个映射流在其内容放入该流后关闭

但是,在Java10中已经修复了这个问题



我的建议是继续使用
try(…)
需要关闭返回流时的标准解决方案和文档。毕竟,在终端操作后关闭资源的流是不安全的,因为无法保证客户端将实际调用终端操作。改变主意并放弃流是有效的,而不调用
close()
方法,当文档指定它是必需的时,它不是。

Java 8已经有了需要关闭的流如何运行的先例。在它们的示例中,它提到:

Streams有一个BaseStream.close()方法并实现自动关闭,但几乎所有流实例在使用后实际上都不需要关闭。通常,只有源为IO通道的流(例如由Files.lines(Path,Charset)返回的流)将需要关闭。大多数流由集合、数组或生成函数支持,不需要特殊的资源管理。(如果流确实需要关闭,则可以在try-with-resources语句中将其声明为资源。)

所以Java8的建议是在尝试使用资源时打开这些流。一旦您这样做,
Stream
还为您提供了一种添加close hook的方法,几乎完全如您所述:,它接受lambda,告诉它做什么,并返回一个
Stream
,该流在关闭时也会执行该操作


这就是API设计和文档建议您尝试执行的操作。

任何拦截终端操作的解决方案(基于
flatMap
的解决方案除外)(由@Holger提出)都会容易受到以下代码的攻击:

Stream<String> stream = getAutoCloseableStream();
if(stream.iterator().hasNext()) {
    // do something if stream is non-empty
}
这会在内部对第一个流使用
spliterator().tryAdvance()
,然后放弃它(尽管在显式调用结果流
close()
时关闭)。您需要要求用户也不要使用
stream.concat
。据我所知,在您的库内部您正在使用
iterator()
/
spliterator()
非常频繁,因此您需要重新访问所有这些地方以查找可能出现的问题。当然,还有许多其他库也使用了
iterator()
/
spliterator()
,并且可能在这之后短路:所有这些库都将与您的功能不兼容

为什么基于
flatMap
的解决方案在这里工作?因为在第一次调用
hasNext()
tryAdvance()时
它将整个流内容转储到中间缓冲区,并关闭原始流源。因此,根据流大小,您可能会浪费大量中间内存,甚至出现
OutOfMemoryError

您还可以考虑将<代码>幻像引用< /代码> s保存到<代码>流>代码>对象,并监视<代码>参考代码>代码>。在这种情况下,完成将由垃圾回收器触发(这也有一些缺点)。


总之,我的建议是继续使用try with resources。

以开源项目的速度查看AutoClosingReferenceStream、AutoClosingTStream、AutoClosingLongStream和AutoClosingDoubleStream的完整实现


该解决方案与@LukasEder提到的类似,因此您必须覆盖/实现
接口的所有>30个方法(四次)只要客户端使用即将推出的Java 9
default
方法之一,它就会崩溃。对我来说,这听起来不是一个解决方案。@霍尔格:我不担心实现4x30方法。我知道JDK 9的限制。但这些新方法不会让人大吃一惊。目前,有,我今天已经可以实现了,并且可以委托使用反射(或自己实现)。不过,我同意,这方面可能与未来JDK版本向后不兼容是一个缺点。请注意,这里完全不需要偷偷抛出。您只需使用
catch(Throwable e){/*some action*/throw e;}
Hint~@Lukas Eder:Java 7中引入了异常处理改进,以及
try(…)
语句、多捕获等。这就是为什么变量“有效最终”的概念在Java 8中并不新鲜,它已经是这个Java 7构造所必需的。非常有趣的发现!“因为不能保证客户机将实际调用终端操作”-事实上,但是资源的打开可能会延迟到调用终端操作。对,因为实际用例将通过
StreamSu
// example stream
Stream<String> original=Stream.of("bla").onClose(()->System.out.println("close action"));

// this is the trick
Stream<String> autoClosed=Stream.of(original).flatMap(Function.identity());

//example op
int sum=autoClosed.mapToInt(String::length).sum();
System.out.println(sum);
Stream<String> stream = getAutoCloseableStream();
if(stream.iterator().hasNext()) {
    // do something if stream is non-empty
}
Stream<String> stream = getAutoCloseableStream();
Stream.concat(stream, Stream.of("xyz")).findFirst();