1.理解增强for循环的本质
java
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User("Tom", 12));
userList.add(new User("Bob", 13));
for(User u: userList){
System.out.println(u);
}
}
为什么userList
可以可以使用增强for循环,而我们自定义的User对象
不能使用增强for 循环。要理解其中发生了什么,我们可以查看编译后的字节码:
发现其实增强for循环等价于以下写法:
java
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User("Tom", 12));
userList.add(new User("Bob", 13));
Iterator<User> iterator = userList.iterator();
while (iterator.hasNext()){
User u = iterator.next();
System.out.println(u);
}
}
发现增强for循环的背后是通过生成iterator对象,从而利用iterator提供的方法进行遍历。
2. java中的Iterator类
接下来我们可以查看List背后的源码,观察其是如何实现迭代器模式的。
一层层点击,发现实质上List
实现了Iterable
的接口并重写了iterator()
才具备迭代功能。
因此如果我们想要使用增强for循环,就必须使遍历的对象实现Iterable
的接口。
3. 探索ArrayList背后的迭代器模式
ArrayList
实现了Iterable
(可迭代对象)的接口并重写了iterator()
,返回一个迭代器(Iterator
)对象
其中迭代器对象的hasNext
主要根据当前的游标和数组的尺寸大小进行判断。next()
首先进行了一系列判断,然后获取当前游标的值并使得游标加一。
那我们是否能在迭代的过程中增加或删除数据呢?通过以下代码进行验证
java
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User("Tom", 12));
userList.add(new User("Bob", 13));
Iterator<User> iterator = userList.iterator();
while (iterator.hasNext()){
User u = iterator.next();
if(u.getAge()==12){
userList.add(new User("txt",13));
}
}
}

发现抛出异常,点进去发现原来是在 checkForComodification();
抛出异常。
因此,在 Java 中,直接通过 for-each 循环(或普通 for 循环结合 Iterator)遍历集合时,直接调用 List 的 add()、remove()
等方法修改集合会触发 ConcurrentModificationException
。这是因为集合的 modCount(修改计数器)和迭代器的 expectedModCount(预期修改计数器)不一致,导致迭代器的并发修改检查失败。正确的做法是:显式使用 Iterator 的 remove()
方法,通过Iterator
遍历并调用其自身(iterator)的 remove()
方法删除元素,此时 Iterator 会同步更新 expectedModCount,避免异常。
4. 案例:读取文件,避免OM问题
改造以下原始代码,利用迭代器模式,读取问题,避免内存溢出问题。
java
public static void main(String[] args) throws IOException {
List<User> userList=new ArrayList<>();
String filePath = FileRead.class.getClassLoader().getResource("User.user").getPath();
List<String> lines = Files.readAllLines(new File(filePath).toPath());
for(String l: lines){
String substring = l.substring(1, l.length() - 1);
String[] split = substring.split(",");
userList.add(new User(split[0],Integer.parseInt(split[1])));
}
}
利用 BufferedReader 的迭代器模式
逐行读取文件内容,并避免内存溢出问题
java
public class ReadFile {
public static void main(String[] args) throws IOException {
String filePath = ReadFile.class.getClassLoader().getResource("User.user").getPath();
// 使用 BufferedReader 逐行读取文件内容
try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath))) {
Iterator<String> lineIterator = new LineIterator(reader);
while (lineIterator.hasNext()) {
String line = lineIterator.next();
// 去掉首尾的括号并分割字段
String substring = line.substring(1, line.length() - 1);
String[] split = substring.split(",");
// 创建 User 对象并处理(此处仅打印作为示例)
User user = new User(split[0], Integer.parseInt(split[1]));
System.out.println(user);
}
}
}
// 自定义 LineIterator 实现迭代器模式
static class LineIterator implements Iterator<String> {
private final BufferedReader reader;
private String nextLine;
public LineIterator(BufferedReader reader) throws IOException {
this.reader = reader;
this.nextLine = reader.readLine(); // 初始化第一行
}
@Override
public boolean hasNext() {
return nextLine != null;
}
@Override
public String next() {
String currentLine = nextLine;
try {
nextLine = reader.readLine(); // 读取下一行
} catch (IOException e) {
throw new RuntimeException("Error reading next line", e);
}
return currentLine;
}
}
// User 类定义
static class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
}