如何使用Java Stream API读取文件首尾行并生成指定Map
使用Java Stream API单次流水线获取文件首末行并生成Map
你的文本文件内容如下:
Column_A Column_B
Cell_1 Cell_2
Cell_3 Cell_4
需求是在单个Stream流水线中获取有效首行和末行,最终生成如下格式的Map:
{ "Column_A": "Cell_3", "Column_B": "Cell_4" }
需要注意:你原先两次调用br.lines()的写法存在问题——BufferedReader对应的Stream只能被消费一次,第二次调用会直接抛出IllegalStateException。下面提供两种单流水线的可行方案:
方案一:自定义收集器(支持并行流)
先定义一个容器类存储首行和末行,再实现对应的收集器:
// 用于存储首行和末行的容器类 class LinePair { private final String first; private String last; private LinePair(String first) { this.first = first; this.last = first; } public void updateLast(String line) { this.last = line; } public String getFirst() { return first; } public String getLast() { return last; } // 自定义收集器:收集有效行的首行和末行 public static Collector<String, ?, LinePair> toLinePair() { return Collector.of( () -> null, (pair, line) -> { if (pair == null) { return line.isBlank() ? null : new LinePair(line); } else { if (!line.isBlank()) { pair.updateLast(line); } return pair; } }, (pair1, pair2) -> { if (pair1 == null) return pair2; if (pair2 == null) return pair1; pair1.updateLast(pair2.getLast()); return pair1; }, Collector.Characteristics.UNORDERED ); } }
使用收集器完成处理:
try (BufferedReader br = new BufferedReader(new FileReader("your-file.txt"))) { LinePair linePair = br.lines() .filter(line -> !line.isBlank()) .collect(LinePair.toLinePair()); if (linePair != null) { String[] keys = linePair.getFirst().split("\\s+"); // 适配多空格分隔 String[] values = linePair.getLast().split("\\s+"); Map<String, String> result = IntStream.range(0, keys.length) .boxed() .collect(Collectors.toMap( i -> keys[i], i -> values[i], (oldVal, newVal) -> oldVal, HashMap::new )); System.out.println(result); } } catch (IOException e) { e.printStackTrace(); }
方案二:AtomicReference记录首行(简洁高效,适合顺序流)
利用peek操作在遍历过程中记录首行,同时用reduce获取末行,全程单流水线:
try (BufferedReader br = new BufferedReader(new FileReader("your-file.txt"))) { AtomicReference<String> firstLine = new AtomicReference<>(); String lastLine = br.lines() .filter(line -> !line.isBlank()) .peek(line -> firstLine.compareAndSet(null, line)) // 仅首次设置首行 .reduce((a, b) -> b) .orElseThrow(() -> new IllegalStateException("文件无有效内容")); String[] keys = firstLine.get().split("\\s+"); String[] values = lastLine.split("\\s+"); Map<String, String> result = IntStream.range(0, Math.min(keys.length, values.length)) .boxed() .collect(Collectors.toMap( i -> keys[i], i -> values[i], (o, n) -> o, HashMap::new )); System.out.println(result); } catch (IOException e) { e.printStackTrace(); }
关键说明
- 两个方案都只遍历文件一次,避免了Stream重复消费的问题
- 使用
\\s+分割字符串,适配列之间多个空格的场景 - 过滤空行,确保首末行都是有效数据行
- 方案二更适合普通的顺序文件流,代码更简洁;方案一支持并行流场景
内容的提问来源于stack exchange,提问作者Alexey Dunaitsev
相关产品推荐
相关产品推荐

