如何使用Java Streams及java.nio.file.Files逐行合并两个大文本文件
使用Java Streams实现文件对应行拼接
可以用Java Streams结合java.nio.file.Files实现需求,核心是实现流的配对(zip)操作——Java标准库没有内置流zip方法,我们可以通过Spliterator同步遍历两个文件的行流,处理文件长度不一致的情况。
你之前尝试的Stream.of(Files.lines(...), Files.lines(...)).flatMap(...)思路存在误区:flatMap是将多个流的元素平铺为单个流,而我们需要把两个流的对应元素配对(第一行配第一行、第二行配第二行),因此需要自定义zip逻辑。
完整流实现代码
package com.example.codingTest; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.BinaryOperator; import java.util.stream.Stream; import java.util.stream.StreamSupport; public class FileMergerImpl { public void merge(Path sourceFilename1, Path sourceFilename2, Path targetFilename, BinaryOperator<String> lineCombiner) { // try-with-resources保证流自动关闭,避免资源泄漏 try (Stream<String> lines1 = Files.lines(sourceFilename1); Stream<String> lines2 = Files.lines(sourceFilename2)) { // 生成配对流:每个元素为两个文件的对应行,某一文件耗尽时对应位置为null Stream<Pair<String, String>> pairedLines = zip(lines1, lines2); // 按照需求处理配对行,生成最终内容 Stream<String> mergedLines = pairedLines.map(pair -> { String line1 = pair.left; String line2 = pair.right; if (line1 != null && line2 != null) { return lineCombiner.apply(line1, line2); } else if (line1 != null) { return line1; } else { return line2; } }); // 将合并后的行写入目标文件,迭代器方式避免加载全部内容到内存 Files.write(targetFilename, (Iterable<String>) mergedLines::iterator); } catch (IOException e) { throw new RuntimeException(e); } } // 自定义流zip方法,同步遍历两个流直到全部耗尽 private <T, U> Stream<Pair<T, U>> zip(Stream<T> first, Stream<U> second) { Iterator<T> iterator1 = first.iterator(); Iterator<U> iterator2 = second.iterator(); Spliterator<Pair<T, U>> spliterator = Spliterators.spliteratorUnknownSize( new Iterator<>() { @Override public boolean hasNext() { return iterator1.hasNext() || iterator2.hasNext(); } @Override public Pair<T, U> next() { T t = iterator1.hasNext() ? iterator1.next() : null; U u = iterator2.hasNext() ? iterator2.next() : null; return new Pair<>(t, u); } }, Spliterator.ORDERED ); return StreamSupport.stream(spliterator, false); } // 内部配对类,用于存储两个流的对应元素 private static class Pair<L, R> { final L left; final R right; Pair(L left, R right) { this.left = left; this.right = right; } } }
代码关键点说明
- zip方法:通过迭代器同步遍历两个流,当其中一个流耗尽时返回null,确保两个文件的所有行都被处理,适配长度不一致的场景。
- 合并逻辑:和你原有的非流实现完全一致,保证行为相同,可直接通过原单元测试。
- 内存友好:流的惰性求值特性+
Files.write的迭代器写入方式,不会一次性加载所有文件内容到内存,适合处理大型文本文件。
内容的提问来源于stack exchange,提问作者Lucas T
相关产品推荐
相关产品推荐

