Java 8 Streams 并尝试使用资源
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25796118/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8 Streams and try with resources
提问by fan
I thought that the stream API was here to make the code easier to read.
I found something quite annoying. The Stream
interface extends the java.lang.AutoCloseable
interface.
我认为流 API 是为了使代码更易于阅读。我发现一些很烦人的事情。该Stream
接口扩展java.lang.AutoCloseable
接口。
So if you want to correctly close your streams, you have to use try with resources.
所以如果你想正确地关闭你的流,你必须使用 try with resources。
Listing 1. Not very nice, streams are not closed.
清单 1。不是很好,流没有关闭。
public void noTryWithResource() {
Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));
@SuppressWarnings("resource") List<ImageView> collect = photos.stream()
.map(photo -> new ImageView(new Image(String.valueOf(photo))))
.collect(Collectors.<ImageView>toList());
}
Listing 2. With 2 nested try
清单 2。使用 2 次嵌套尝试
public void tryWithResource() {
Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));
try (Stream<Integer> stream = photos.stream()) {
try (Stream<ImageView> map = stream
.map(photo -> new ImageView(new Image(String.valueOf(photo)))))
{
List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
}
}
}
Listing 3. As map
returns a stream, both the stream()
and the map()
functions have to be closed.
清单 3。当map
返回一个流时,stream()
和map()
函数都必须关闭。
public void tryWithResource2() {
Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));
try (Stream<Integer> stream = photos.stream(); Stream<ImageView> map = stream.map(photo -> new ImageView(new Image(String.valueOf(photo)))))
{
List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
}
}
The example I give does not make any sense. I replaced Path
to jpg images with Integer
, for the sake of the example. But don't let you distract by these details.
我举的例子没有任何意义。为了这个例子,我用 替换Path
为 jpg 图像Integer
。但是不要让你被这些细节分散注意力。
What is the best way to go around with those auto closable streams. I have to say I'm not satisfied with any of the 3 options I showed. What do you think? Are there yet other more elegant solutions?
处理这些可自动关闭的流的最佳方法是什么。我不得不说我对我展示的 3 个选项中的任何一个都不满意。你怎么认为?还有其他更优雅的解决方案吗?
采纳答案by Stuart Marks
You're using @SuppressWarnings("resource")
which presumably suppresses a warning about an unclosed resource. This isn't one of the warnings emitted by javac
. Web searches seem to indicate that Eclipse issues warnings if an AutoCloseable
is left unclosed.
您正在使用@SuppressWarnings("resource")
它可能会抑制有关未关闭资源的警告。这不是javac
. Web 搜索似乎表明,如果 EclipseAutoCloseable
未关闭,则会发出警告。
This is a reasonable warning according to the Java 7 specificationthat introduced AutoCloseable
:
根据引入的Java 7 规范,这是一个合理的警告AutoCloseable
:
A resource that must be closed when it is no longer needed.
不再需要时必须关闭的资源。
However, the Java 8 specificationfor AutoCloseable
was relaxed to remove the "must be closed" clause. It now says, in part,
然而,Java的8规范的AutoCloseable
很轻松删除“必须关闭”的条款。它现在部分说,
An object that may hold resources ... until it is closed.
It is possible, and in fact common, for a base class to implement AutoCloseable even though not all of its subclasses or instances will hold releasable resources. For code that must operate in complete generality, or when it is known that the AutoCloseable instance requires resource release, it is recommended to use try-with-resources constructions. However, when using facilities such as Stream that support both I/O-based and non-I/O-based forms, try-with-resources blocks are in general unnecessary when using non-I/O-based forms.
一个可能持有资源的对象......直到它被关闭。
即使不是所有的子类或实例都拥有可释放的资源,基类也有可能实现 AutoCloseable,并且实际上很常见。对于必须完全通用的代码,或者当知道 AutoCloseable 实例需要资源释放时,建议使用 try-with-resources 构造。但是,当使用 Stream 等同时支持基于 I/O 和非基于 I/O 的形式时,在使用非基于 I/O 的形式时,try-with-resources 块通常是不必要的。
This issue was discussed extensively within the Lambda expert group; this messagesummarizes the decision. Among other things it mentions changes to the AutoCloseable
specification (cited above) and the BaseStream
specification (cited by other answers). It also mentions the possible need to adjust the Eclipse code inspector for the changed semantics, presumably not to emit warnings unconditionally for AutoCloseable
objects. Apparently this message didn't get to the Eclipse folks or they haven't changed it yet.
这个问题在 Lambda 专家组内进行了广泛讨论;该消息总结了该决定。除其他外,它提到了对AutoCloseable
规范(上面引用的)和BaseStream
规范(由其他答案引用)的更改。它还提到可能需要针对已更改的语义调整 Eclipse 代码检查器,大概不会无条件地为AutoCloseable
对象发出警告。显然这条消息没有传达给 Eclipse 人员,或者他们还没有改变它。
In summary, if Eclipse warnings are leading you into thinking that you need to close all AutoCloseable
objects, that's incorrect. Only certain specific AutoCloseable
objects need to be closed. Eclipse needs to be fixed (if it hasn't already) not to emit warnings for all AutoCloseable
objects.
总之,如果 Eclipse 警告使您认为您需要关闭所有AutoCloseable
对象,那是不正确的。只AutoCloseable
需要关闭某些特定对象。Eclipse 需要被修复(如果它还没有)不为所有AutoCloseable
对象发出警告。
回答by dkatzel
You only need to close Streams if the stream needs to do any cleanup of itself, usually I/O. Your example uses an HashSet so it doesn't need to be closed.
如果流需要对自身进行任何清理(通常是 I/O),您只需关闭 Streams。您的示例使用 HashSet,因此不需要关闭它。
from the Streamjavadoc:
来自Streamjavadoc:
Generally, only streams whose source is an IO channel (such as those returned by Files.lines(Path, Charset)) will require closing. Most streams are backed by collections, arrays, or generating functions, which require no special resource management.
通常,只有源是 IO 通道的流(例如 Files.lines(Path, Charset) 返回的流)才需要关闭。大多数流由集合、数组或生成函数支持,不需要特殊的资源管理。
So in your example this should work without issue
所以在你的例子中,这应该没有问题
List<ImageView> collect = photos.stream()
.map(photo -> ...)
.collect(toList());
EDIT
编辑
Even if you need to clean up resources, you should be able to use just one try-with-resource. Let's pretend you are reading a file where each line in the file is a path to an image:
即使您需要清理资源,您也应该只能使用一个 try-with-resource。假设您正在读取一个文件,其中文件中的每一行都是图像的路径:
try(Stream<String> lines = Files.lines(file)){
List<ImageView> collect = lines
.map(line -> new ImageView( ImageIO.read(new File(line)))
.collect(toList());
}
回答by Holger
“Closeable” means “can be closed”, not “must be closed”.
“可关闭”的意思是“可以关闭”,而不是“必须关闭”。
That was true in the past, e.g. see ByteArrayOutputStream
:
过去确实如此,例如,请参阅ByteArrayOutputStream
:
Closing a
ByteArrayOutputStream
has no effect.
关闭 a
ByteArrayOutputStream
没有效果。
And that is true now for Stream
s where the documentation makes clear:
现在对于Stream
s 来说,文档清楚地说明了这一点:
Streams have a
BaseStream.close()
method and implementAutoCloseable
, but nearly all stream instances do not actually need to be closed after use. Generally, only streams whose source is an IO channel (such as those returned byFiles.lines(Path, Charset)
) will require closing.
流有一个
BaseStream.close()
方法和实现AutoCloseable
,但几乎所有的流实例实际上不需要在使用后关闭。通常,只有源为 IO 通道的流(例如由 返回的流Files.lines(Path, Charset)
)才需要关闭。
So if an audit tool generates false warnings, it's a problem of the audit tool, not of the API.
因此,如果审计工具生成错误警告,则是审计工具的问题,而不是 API 的问题。
Note that even if you want to add resource management, there is no need to nest try
statements. While the following is sufficient:
注意,即使要添加资源管理,也不需要嵌套try
语句。虽然以下就足够了:
final Path p = Paths.get(System.getProperty("java.home"), "COPYRIGHT");
try(Stream<String> stream=Files.lines(p, StandardCharsets.ISO_8859_1)) {
System.out.println(stream.filter(s->s.contains("Oracle")).count());
}
you may also add the secondary Stream
to the resource management without an additional try
:
您还可以将辅助添加Stream
到资源管理中,而无需额外的try
:
final Path p = Paths.get(System.getProperty("java.home"), "COPYRIGHT");
try(Stream<String> stream=Files.lines(p, StandardCharsets.ISO_8859_1);
Stream<String> filtered=stream.filter(s->s.contains("Oracle"))) {
System.out.println(filtered.count());
}
回答by Lii
It is possible to create a utility method that reliably closes streams with a try-with-resource-statement.
可以创建一个实用方法,使用 try-with-resource-statement 可靠地关闭流。
It is a bit like a try-finally that is an expression(something that is the case in e.g. Scala).
它有点像 try-finally 是一个表达式(例如 Scala 中的情况)。
/**
* Applies a function to a resource and closes it afterwards.
* @param sup Supplier of the resource that should be closed
* @param op operation that should be performed on the resource before it is closed
* @return The result of calling op.apply on the resource
*/
private static <A extends AutoCloseable, B> B applyAndClose(Callable<A> sup, Function<A, B> op) {
try (A res = sup.call()) {
return op.apply(res);
} catch (RuntimeException exc) {
throw exc;
} catch (Exception exc) {
throw new RuntimeException("Wrapped in applyAndClose", exc);
}
}
(Since resources that need to be closed often also throw exceptions when they are allocated non-runtime exceptions are wrapped in runtime exceptions, avoiding the need for a separate method that does that.)
(因为需要关闭的资源在被分配时通常也会抛出异常,非运行时异常被包装在运行时异常中,避免了需要一个单独的方法来做到这一点。)
With this method the example from the question looks like this:
使用此方法,问题中的示例如下所示:
Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));
List<ImageView> collect = applyAndClose(photos::stream, s -> s
.map(photo -> new ImageView(new Image(String.valueOf(photo))))
.collect(Collectors.toList()));
This is useful in situations when closing the stream is required, such as when using Files.lines
. It also helps when you have to do a "double close", as in your example in Listing 3.
这在需要关闭流的情况下很有用,例如使用Files.lines
. 当您必须执行“双重关闭”时,它也有帮助,如清单 3中的示例所示。
This answer is an adaptation of an old answerto a similar question.
这个答案是对类似问题的旧答案的改编。