问题还原
import java.io.*;
import java.util.ArrayList;
import java.util.List;
class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
// Getter和Setter方法(可选)
// ...
// 重写toString方法,用于输出学生信息
@Override
public String toString() {
return "Name: " + name + ", Age: " + age + ", Score: " + score;
}
}
public class SerializationDemo {
public static void main(String[] args) {
List<Student> loadedStudents = loadStudentsFromFile("students.ser");
if (loadedStudents != null) {
try {
// 在其他方法中尝试移除元素
removeFromList(loadedStudents);
// 尝试序列化已修改的集合
saveStudentsToFile(loadedStudents, "modified_students.ser");
} catch (ConcurrentModificationException e) {
System.out.println("ConcurrentModificationException: " + e.getMessage());
}
}
}
// 从文件中读取学生对象并返回学生列表
private static List<Student> loadStudentsFromFile(String fileName) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName))) {
List<Student> loadedStudents = (List<Student>) ois.readObject();
System.out.println("Students loaded from file: " + fileName);
return loadedStudents;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
// 将学生对象保存到文件中(序列化)
private static void saveStudentsToFile(List<Student> students, String fileName) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
oos.writeObject(students);
System.out.println("Students saved to file: " + fileName);
} catch (IOException e) {
e.printStackTrace();
}
}
// 在其他方法中尝试修改集合
private static void removeFromList(List<Student> students) {
// 移除集合中的第一个元素
students.remove(0);
}
}
在上述示例中,
removeFromList
方法尝试移除集合中的第一个元素,但这个操作会导致集合结构的变化。如果之后尝试将这个修改后的集合再次序列化,可能会触发ConcurrentModificationException
异常,因为序列化和之前的序列化版本不一致。
为了避免这种情况,最好在对集合进行序列化和反序列化操作时,确保集合不会被其他方法并发地修改。如果需要对集合进行修改,可以在序列化和反序列化操作之后执行。
问题解释
当反序列化的集合对象被其他方法调用并移除其中的元素时,如果在序列化和反序列化的过程中,对集合进行了不同版本的更改,可能会导致
ConcurrentModificationException
异常。这是因为在序列化时,可能存储了集合的一些状态信息,而在反序列化后,如果对集合进行了结构性修改(添加、删除元素等),就会导致集合的版本不一致,进而引发异常。
ConcurrentModificationException
是Java集合框架中的一个异常类,它通常在多线程环境下(或序列化与反序列化过程中)出现,提示集合被并发地修改。
解决办法
如果我想要做移除操作应该怎么办呢?
-
方法一:使用迭代器进行移除
迭代器是一种安全的方式来遍历集合并在遍历的同时进行修改。您可以使用迭代器的
remove
方法来移除元素,这不会引发ConcurrentModificationException
异常。Iterator<Student> iterator = loadedStudents.iterator(); while (iterator.hasNext()) { Student student = iterator.next(); if (/* 判断是否要移除这个学生 */) { iterator.remove(); } }
-
方法二:将移除操作延迟到遍历之后\
如果您可以在遍历集合之后进行移除操作,可以先将要移除的元素的索引记录下来,然后遍历完集合后再进行实际的移除。
List<Integer> indexesToRemove = new ArrayList<>(); for (int i = 0; i < loadedStudents.size(); i++) { Student student = loadedStudents.get(i); if (/* 判断是否要移除这个学生 */) { indexesToRemove.add(i); } } for (int index : indexesToRemove) { loadedStudents.remove(index); }
-
方法三:使用线程安全的集合类
Java提供了一些线程安全的集合类,如
CopyOnWriteArrayList
。这些集合类允许在迭代时进行修改,但会创建一个新的副本来保存修改,从而避免了ConcurrentModificationException
异常。CopyOnWriteArrayList<Student> threadSafeList = new CopyOnWriteArrayList<>(loadedStudents); for (Student student : threadSafeList) { if (/* 判断是否要移除这个学生 */) { threadSafeList.remove(student); } }
发表评论