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
本身,从而使整个值无效我在这里尝试做的事情的重点。
有什么解决办法吗?
但我找不到一种方法来阻止用户只返回
本身,从而抵消了我在这里尝试做的整个事情。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 作斗争。您可以通过代码审查等方式来防止此类尝试。