Java 8流先取第一个再调用forEach(...)

6

我有一个CSV文件,第一行包含标题。因此,我认为使用Java 8流是完美的选择。

    try (Stream<String> stream = Files.lines(csv_file) ){
        stream.skip(1).forEach( line -> handleLine(line) );
    } catch ( IOException ioe ){
        handleError(ioe);
    }

能否取出第一个元素,对其进行分析,然后调用forEach方法?类似于:

stream
      .forFirst( line -> handleFirst(line) )
      .skip(1)
      .forEach( line -> handleLine(line) );

此外: 我的CSV文件包含大约1k行,我可以并行处理每一行以加速处理。但第一行除外。我需要第一行来初始化项目中的其他对象:/ 因此,也许快速打开BufferedReader,读取第一行,关闭BufferedReader,然后使用并行流是一个好方法?


你的目标是什么? - shmosel
1
我猜这是一个文件的头部,需要特殊处理,对吗? - NiVeR
findFirst().get() 替换你的伪代码 forFirst,然后使用它进行操作,接着使用 skipforEach - Zircon
3
findFirst()是一个终端操作,使用之后你将无法继续使用该流。 - shmosel
2
有时候,“命令式问题”最好写成命令式代码,而不是滥用看起来像函数式编程的方式。 - Has QUIT--Anony-Mousse
显示剩余2条评论
3个回答

8
一般来说,您可以使用迭代器来完成这个任务:
Stream<Item> stream = ... //initialize your stream
Iterator<Item> i = stream.iterator();
handleFirst(i.next());
i.forEachRemaining(item -> handleRest(item));

在您的程序中,它可能看起来像这样:

try (Stream<String> stream = Files.lines(csv_file)){
    Iterator<String> i = stream.iterator();
    handleFirst(i.next());
    i.forEachRemaining(s -> handleRest(s));
}

如果您只获取到了 1 或者 0 行,建议加入一些错误检查,但是这个方法应该可以实现。


3
使用Files.lines返回的流应该关闭以确保内部的缓冲读取器被关闭。终端操作iterator()不会隐式地关闭流,详见https://dev59.com/zlsX5IYBdhLWcg3wPdU7. - Tunaki

4

一个不错的方法是使用BufferedReader读取文件,例如借助Files.newBufferedReader(path)。然后您可以调用nextLine()一次来检索标题行,并使用lines()获取所有其他行的Stream<String>

try (BufferedReader br = Files.newBufferedReader(csv_file)){
    String header = br.readLine();
    // if header is null, the file was empty, you may want to throw an exception
    br.lines().forEach(line -> handleLine(line));
}

这是因为第一次调用readLine()会导致缓冲读取器读取第一行,因此随后,由于lines()是通过读取行填充的流,它从第二行开始读取。当处理结束时,try-with-resources正确关闭了缓冲读取器。
潜在地,流管道可以并行运行,但对于像这样的I/O绑定任务,我不会期望有任何性能提升,除非每行的处理是较慢的部分。但在这种情况下要小心使用forEach:它将同时运行,因此其代码需要是线程安全的。不清楚handleLine方法做什么,但通常情况下,您不需要forEach,可能更喜欢使用collect进行可变归约,这在并行流中使用是安全的。

1
一个保留顺序的替代方法是 forEachOrdered。但这将进一步限制任何并行性。 - Stuart Marks
那对我来说看起来很完美。只要跳过第一行,我就不必订购它了。 - Highchiller

0

我认为在流管道内没有很好的方法来完成它,但是你可以使用流的迭代器来更精细地控制迭代:

try (Stream<String> stream = Files.lines(csv_file) ){
    Iterator<String> iter = stream.iterator();
    if (iter.hasNext()) {
        handleFirst(iter.next());
        while (iter.hasNext()) {
            handleLine(iter.next());
        }
    }
} catch ( IOException ioe ){
    handleError(ioe);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接