如何使用Java 8流

时间:2015-12-29 21:59:59

标签: java java-8 java-stream nio2

我想读取一个由“块”组成的ASCII文件,这些块由开始和结束标记分隔。

我从未使用过Java 8流,我想在这个文件阅读器上测试它们,但我真的不知道怎么做。

为简单起见,我们考虑以下文件格式(实际文件格式can be found here):

$Node
6
1 1.0 0.0 0.0
2 -1.0 0.0 0.0
3 0.0 1.0 0.0
4 0.0 -1.0 0.0
5 0.0 0.0 1.0
6 0.0 0.0 -1.0
$EndNode
$Elements
3
1 10 1 2 3
2 10 4 5 6
3 10 1 5 3
$EndElements

每个块的第一行是块中元素的数量。然后每个块都是以空格分隔的值列表。值和类型的数量取决于块。

在现实生活中,文件可能会变得非常大(几百Mb,可能高达几Gb),因此性能至关重要。

使用Java NIO 2(没有Java 8流),我会做这样的事情:

BufferedReader reader = Files.newBufferedReader(filePath, Charset.defaultCharset());
String line = null;
Parser currentParser = defaultParser;
while ((line = reader.readLine()) != null) {
    if (line.startsWith("$")) {
        currentParser = getParser(line);
        continue;
    }
    currentParser.parseLine(line);
}

使用一个足够聪明的线解析器来处理块的第一行与其余部分不同(不必为每一行测试一个isFirstLineOfBlock布尔值)......不知道如何做到这一点顺便说一句。

无论如何,我很感激为这个文件阅读器使用Java 8流提供了一些帮助。

最后一个问题,在这样的应用程序中使用Java流有什么好处:它只是一个可读性问题,还是我期望改进性能?

2 个答案:

答案 0 :(得分:1)

有几种方法可以使用Java 8流。例如,

try (BufferedReader br = Files.newBufferedReader(Paths.get(filePath, Charset.defaultCharset())) {
    br.lines()
        .filter(line -> !line.startsWith("$"))
        .forEachOrdered(currentParser::parseLine);
} catch (IOException ex) {
    throw new Error(ex);
}

.lines()方法说明包含

The Stream is lazily populated, i.e., 
read only occurs during the terminal stream operation.

在此示例中,终端操作为forEachOrdered

另一个

try (Stream<String> stream = Files.lines(Paths.get(filePath, Charset.defaultCharset())) {
    stream
        .filter(line -> !line.startsWith("$"))
        .forEachOrdered(currentParser::parseLine);
} catch (IOException ex) {
    throw new Error(ex);
}

答案 1 :(得分:1)

可以在我的免费StreamEx库的帮助下解析此类构造,该库增强了标准Stream API:

StreamEx.ofLines(filePath, Charset.defaultCharset())
        .groupRuns((a, b) -> !b.startsWith("$"))
        .forEachOrdered(list -> 
            list.subList(1, list.size()).forEach(getParser(list.get(0))::parseLine));

这里我们使用groupRuns方法将单个文件条目组合到列表中。传递给groupRuns的参数是BiPredicate,它适用于相邻的输入元素对,如果必须对元素进行分组,则返回true。在这里,我们对元素进行分组,除非下一个元素以"$"开头。之后我们已经懒惰填充Stream<List<String>>并解析每个组使用第一行创建解析器并为所有后续行调用parseLine