
本文旨在介绍如何使用 Java Stream 处理文本文件,并修改文件中满足特定条件的行。我们将探讨如何避免 `UnsupportedOperationException` 异常,以及如何利用 `Collectors.toCollection()` 和 `mapMulti()` 方法高效地实现行修改功能。同时,本文也提供针对旧版本 JDK 的兼容方案。
问题背景
在处理文本文件时,经常需要读取文件内容,找到满足特定条件的行,并对其进行修改。例如,需要读取一个包含多行数据的文本文件,找到以特定字符串开头的行,并修改该行中的某个字段。
解决方案
使用 Java Stream 可以优雅地解决这个问题。以下将介绍两种主要的解决方案:
1. 使用 Collectors.toCollection()
Files.lines(Path).toList() 返回的 List 是一个不可修改的列表,尝试修改它会抛出 UnsupportedOperationException 异常。为了获得一个可修改的列表,可以使用 Collectors.toCollection(ArrayList::new):
立即学习“Java免费学习笔记(深入)”;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ModifyFileLine {
public static void main(String[] args) {
String startOfLine = "b";
String filePath = "pathToMyTextFile.txt"; // 替换为你的文件路径
List lines = new ArrayList<>();
try (Stream stream = Files.lines(Paths.get(filePath))) {
lines = stream.collect(Collectors.toCollection(ArrayList::new));
} catch (IOException e) {
e.printStackTrace();
return; // 退出程序,避免后续空指针异常
}
int index = -1; // 初始化 index 为 -1,用于判断是否找到目标行
String modifiedString = "";
for (int i = 0; i < lines.size(); i++) {
String s = lines.get(i);
if (s.startsWith(startOfLine)) {
String[] splitS = s.split("/");
int increment = Integer.parseInt(splitS[2]) + 1;
modifiedString =
splitS[0] + "/" +
splitS[1] + "/" +
increment + "/" +
splitS[3] + "/" +
splitS[4];
index = i; // 记录目标行的索引
break; // 找到目标行后退出循环
}
}
// 只有在找到目标行时才进行修改
if (index != -1) {
lines.set(index, modifiedString);
}
// 打印修改后的列表(可选)
lines.forEach(System.out::println);
}
} 代码解释:
- 首先,使用 Files.lines() 读取文件内容,并使用 Collectors.toCollection(ArrayList::new) 将 Stream 转换为一个 ArrayList。
- 然后,遍历列表,找到以 startOfLine 开头的行。
- 对该行进行修改,并将修改后的字符串赋值给 modifiedString。
- 最后,使用 lines.set(index, modifiedString) 替换列表中的旧值。
注意事项:
- 确保文件路径 pathToMyTextFile.txt 正确。
- 需要处理 IOException 异常。
- 如果文件中没有以 startOfLine 开头的行,则不会进行任何修改。
- 添加了 index 初始化为 -1 和 break 语句,提高了代码的健壮性和效率。
2. 使用 mapMulti() (Java 16+)
对于 Java 16 及以上版本,可以使用 mapMulti() 方法在 Stream 中直接进行修改。
响应式黑色展台设计整站模板,自带内核安装即用,图片文本实现可视化,方便修改,支持多种内容模型及自定义功能,可根据需要自行添加。模板特点: 1、安装即用,自带人人站CMS内核及企业站展示功能(产品,新闻,案例展示等),并可根据需要增加表单 搜索等功能(自带模板) 2、支持响应式 3、前端banner轮播图文本均已进行可视化配置 4、伪静态页面生成 5、支持内容模型、多语言、自定义表单、筛选、多条件搜
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;
public class ModifyFileLineMapMulti {
public static void main(String[] args) {
String startOfLine = "b";
String filePath = "pathToMyTextFile.txt"; // 替换为你的文件路径
List lines = null;
try (Stream stream = Files.lines(Paths.get(filePath))) {
lines = processLines(stream, startOfLine);
} catch (IOException e) {
e.printStackTrace();
return; // 退出程序,避免后续空指针异常
}
// 打印修改后的列表(可选)
if(lines != null) {
lines.forEach(System.out::println);
}
}
public static List processLines(Stream lines, String startOfLine) {
return lines
.mapMulti((line, consumer) -> {
if (!line.startsWith(startOfLine)) consumer.accept(line);
else {
String[] parts = line.split("/");
parts[2] = String.valueOf(Integer.parseInt(parts[2]) + 1);
consumer.accept(String.join("/", parts));
}
})
.toList();
}
} 代码解释:
- processLines 方法接收一个 Stream
和一个 startOfLine 字符串。 - 使用 mapMulti() 方法对 Stream 中的每一行进行处理。
- 如果该行不以 startOfLine 开头,则直接将其传递给 consumer。
- 如果该行以 startOfLine 开头,则将其分割成多个部分,修改指定部分,然后使用 String.join() 重新组合成字符串,并将其传递给 consumer。
- 最后,使用 toList() 方法将 Stream 转换为一个 List。
注意事项:
- mapMulti() 方法是 Java 16 引入的新特性,如果使用较低版本的 JDK,则需要使用其他方法来实现相同的功能。
- 同样需要处理 IOException 异常。
3. 使用 map() (Java
对于 Java 16 之前的版本,可以使用 map() 方法结合一个辅助方法来实现类似的功能。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ModifyFileLineMap {
public static void main(String[] args) {
String startOfLine = "b";
String filePath = "pathToMyTextFile.txt"; // 替换为你的文件路径
List lines = null;
try (Stream stream = Files.lines(Paths.get(filePath))) {
lines = stream.map(line -> processLine(line, startOfLine)).collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
return; // 退出程序,避免后续空指针异常
}
// 打印修改后的列表(可选)
if(lines != null) {
lines.forEach(System.out::println);
}
}
private static String processLine(String line, String startOfLine) {
if (!line.startsWith(startOfLine)) {
return line;
} else {
String[] parts = line.split("/");
parts[2] = String.valueOf(Integer.parseInt(parts[2]) + 1);
return String.join("/", parts);
}
}
} 代码解释:
- processLine 方法接收一行字符串和一个 startOfLine 字符串。
- 如果该行不以 startOfLine 开头,则直接返回该行。
- 如果该行以 startOfLine 开头,则将其分割成多个部分,修改指定部分,然后使用 String.join() 重新组合成字符串并返回。
- 在主程序中,使用 stream.map(line -> processLine(line, startOfLine)) 对 Stream 中的每一行进行处理,并将结果收集到一个 List 中。
注意事项:
- 同样需要处理 IOException 异常。
总结
本文介绍了三种使用 Java Stream 修改文本文件中特定行的方法。Collectors.toCollection() 适用于需要修改列表的情况,mapMulti() 适用于 Java 16 及以上版本,可以直接在 Stream 中进行修改,而 map() 结合辅助方法则适用于 Java 16 之前的版本。选择哪种方法取决于具体的 JDK 版本和需求。在实际应用中,应该根据具体情况选择最合适的方案。









