XML反序列化后访问Sublist抛出ConcurrentModificationException问题
XStream序列化ArrayList子列表:Java6 vs Java7差异解析
你遇到的问题核心在于Java6和Java7中ArrayList.subList()的实现差异,以及XStream对不同版本子列表类的序列化行为不同。先回顾下你的场景:使用XStream 1.4.4序列化ArrayList的子列表,Java7反序列化后迭代抛出ConcurrentModificationException,但Java6正常,最终通过将子列表转为新ArrayList解决了问题。
你的测试代码
import java.util.ArrayList; import java.util.List; import com.thoughtworks.xstream.XStream; public class TestClass { public static void main(String[] args) { List<String> someList = new ArrayList<String>(); someList.add("String1"); someList.add("String2"); someList.add("String3"); List<String> someSubList = someList.subList(0, 2); for (String string : someSubList) { System.out.println(string); } System.out.println("====================================="); XStream xstr = new XStream(); String subListXML = xstr.toXML(someSubList); System.out.println(subListXML); System.out.println("====================================="); @SuppressWarnings("unchecked") List<String> subList = (List<String>) xstr.fromXML(subListXML); for (String string : subList) { // 这里Java7抛出异常 System.out.println(string); } } }
Java7抛出的异常
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1169) at java.util.ArrayList$SubList.listIterator(ArrayList.java:1049) at java.util.AbstractList.listIterator(AbstractList.java:299) at java.util.ArrayList$SubList.iterator(ArrayList.java:1045) at com.test.TestClass.main(TestClass.java:36)
不同Java版本生成的XML结构
Java7生成的XML
<java.util.ArrayList_-SubList> <parent class="list"> <string>String1</string> <string>String2</string> <string>String3</string> </parent> <parentOffset>0</parentOffset> <offset>0</offset> <size>2</size> <outer-class reference="../parent"/> </java.util.ArrayList_-SubList>
Java6生成的XML
<java.util.RandomAccessSubList> <l class="list"> <string>String1</string> <string>String2</string> <string>String3</string> </l> <offset>0</offset> <size>2</size> <expectedModCount>3</expectedModCount> </java.util.RandomAccessSubList>
问题解答
1. 为什么Java6中反序列化子列表后迭代不会触发异常?
这不是“创建子列表被视为结构性修改”的问题,而是Java6和Java7的子列表实现类不同,XStream的序列化行为也有差异:
- Java6中,
ArrayList.subList()返回的是java.util.RandomAccessSubList,这个类自己维护了一个expectedModCount字段,用来跟踪原列表的修改次数。 - XStream序列化
RandomAccessSubList时,会把expectedModCount字段写入XML(你提供的Java6 XML里能看到这个字段值为3,对应原列表添加3个元素后的修改次数)。 - 反序列化时,XStream会重建原列表(调用
add方法添加三个元素),此时原列表的modCount会递增到3;同时RandomAccessSubList的expectedModCount被还原为3。当你迭代子列表时,checkForComodification()检查原列表的modCount和子列表的expectedModCount相等,所以不会抛出异常。
2. 为什么Java7中的异常在XML反序列化后而非之前抛出?
ConcurrentModificationException是在迭代子列表时触发的修改检查,而非反序列化过程:
- Java7中,
ArrayList.subList()返回的是ArrayList$SubList内部类,它的checkForComodification()方法会比较原列表的modCount和自己的modCount(这个值在子列表创建时从原列表复制)。 - 反序列化过程只是创建
ArrayList$SubList对象并设置它的字段(比如parent、offset等),但不会触发修改检查——因为此时还没有遍历列表。 - 当你尝试迭代反序列化后的子列表时,调用
iterator()方法会触发listIterator(),进而调用checkForComodification()。此时反序列化出来的原列表modCount是3(XStream添加元素时递增的),但SubList自己的modCount是默认初始值0(XStream没有序列化这个字段,因为它不是公开字段),两者不匹配,所以抛出异常。
你的解决方案为什么有效?
当你用new ArrayList<String>(someList.subList(0, 2))创建新列表时,本质是把原列表的子列表元素复制到一个全新的ArrayList中,不再和原列表有引用关联。XStream序列化这个新列表时,只会保存元素内容,反序列化后得到的是独立的ArrayList,自然不会有修改检查的问题,生成的XML也更简洁。
内容的提问来源于stack exchange,提问作者Heena Jain
相关产品推荐
相关产品推荐

