在此示例中如何避免使用 try-with-resources 或 close() ?

问题描述 投票:0回答:1

我正在尝试使用

java.util.stream.Stream
从互联网传输数据。我有一个有效的实现。下面是它。

final URL url =
   new
      URI
      (
         "INSERT YOUR HYPERLINK HERE. Mine is this 5.2 gb file --> https://raw.githubusercontent.com/nytimes/covid-19-data/master/us.csv"
      )
      .toURL()
      ;

try
(
   final InputStream inputStream = url.openStream();
   final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
   final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
)
{
   final Stream<String> lines = bufferedReader.lines();
   //do what you want with the stream here.    
}
catch (final Exception exception)
{
   throw new RuntimeException(exception);
}

因为它是一个

Stream
,所以执行是惰性的。它仅从上游来源(互联网)获取最少的必要内容。这是理想的,因为我可以短路并避免从互联网上下载超出需要的内容。

问题在于所有的资源尝试。我并不反对编写它们,但我计划将这个实现作为团队的一部分使用。所以,很多人都会使用它。为了避免错误,我想消除至少一个特定的绊倒危险——需要尝试资源。

我想避免遇到内存/资源问题,因为有人忘记进行资源尝试。或者一个

close()
方法,因为这会是同样的问题,但更容易忘记。

大家会如何解决这个问题?


这是我迄今为止的进展。

我看到了 Venkat Subremaniam 的演讲,他在其中描述了“执行周围方法模式” --> https://www.youtube.com/watch?v=yTuwi--LFsM&t=7920s

长话短说,“围绕方法执行模式”允许您通过不让调用者拥有资源来避免资源误用的担忧。相反,您让调用者传入一个

java.util.function.Consumer
,其参数化类型是您不让他们拥有的资源,然后简单地让他们在
Consumer
中指定他们将使用该资源调用的函数,如果您会让他们拥有一个。

一方面,这种模式对于我的问题来说完美

Stream
执行
Consumer
告诉它的所有事情,然后在
Consumer
完成后自行关闭。这是我制作的一个完整的、可运行的实现。

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.util.Comparator;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;

public class StreamDataFromTheInternet
{
   public static void main(final String[] args) throws Exception
   {
      StreamDataFromTheInternet
         .executeAroundMethodPattern
         (
            "https://raw.githubusercontent.com/nytimes/covid-19-data/master/us.csv" //THIS IS A 5 GIGABYTE FILE
            ,
            lines ->
               lines
                  .filter(line -> line.length() > 20) //filter down to lines that are >20 characters
                  .limit(10)                          //of those lines, only grab the first 10
                  .forEach(System.out::println)       //print them out to console
         )
         ;
      //Doing it this way allowed us to avoid downloading the entire 5GB file.
      //Instead, we downloaded only a couple of kb.
      //Basically, we only downloaded as many lines as we needed to in order
      //to complete the Stream.
   }

   private static void executeAroundMethodPattern(final String hyperlink, Consumer<Stream<String>> functionToExecute) throws Exception
   {
      final URL url =
         new
            URI
            (
               hyperlink
            )
            .toURL()
            ;
   
      try
      (
         final InputStream inputStream = url.openStream();
         final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
         final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
      )
      {
         final Stream<String> lines = bufferedReader.lines();
         functionToExecute.accept(lines);
      }
      catch (final Exception exception)
      {
         throw new RuntimeException(exception);
      }
   }
}

所以,这看起来正是我正在寻找的......假设我只做涉及副作用的代码。

但实际上,

Stream
的大多数实例都应该产生一个值。产生副作用实际上是
Stream
的次要用例。我想提供主要用例,但我要提供次要用例的所有安全性。

我会省去你这个长长的兔子洞,基本上可以归结为——返回一个值很容易,但我找不到一种方法来阻止用户只返回

Stream
本身,从而使整个值无效我在这里尝试做的事情的重点。

有什么解决办法吗?

java java-stream lazy-evaluation try-with-resources resource-leak
1个回答
0
投票

但我找不到一种方法来阻止用户只返回

Stream
本身,从而抵消了我在这里尝试做的整个事情。

如果

Stream
本身被退回,请不要担心。只需确保在执行方法返回之前关闭
Stream
即可。例如:

/**
 * Processes a stream of lines from a URL and returns a result. The characters
 * are decoded using the default charset. The {@code Stream} will be closed
 * before this method returns.
 *
 * @param url the URL to stream lines from
 * @param function the function that processes the stream
 * @return the value returned by {@code function}
 * @param <R> The result type.
 * @throws IllegalArgumentException if {@code url} is not a valid URL
 * @throws UncheckedIOException if an I/O error occurs
 */
public static <R> R streamLines(String url, Function<Stream<String>, R> function) {
    try (var input = URI.create(url).toURL().openStream();
         var stream = new BufferedReader(new InputStreamReader(input)).lines()) {
        return function.apply(stream);
    } catch (IOException ex) {
        throw new UncheckedIOException(ex);
    }
}

注意:您可能想添加一种方法来指定阅读器使用的字符集。

即使像这样调用此方法:

Stream<String> stream = streamLines("...", Function.identity());

没关系。

Stream
在被
streamLines
返回之前保证被关闭,这意味着它不可用。

上述方法的设计鼓励您想要的使用。文档解释了流将自动关闭。任何想要以可用方式泄露流的人都必须与 API 作斗争。您可以通过代码审查等方式来防止此类尝试。

© www.soinside.com 2019 - 2024. All rights reserved.