黑马程序员技术交流社区

标题: List的一点小问题???[已解决] [打印本页]

作者: 王金科    时间: 2012-9-1 03:07
标题: List的一点小问题???[已解决]
本帖最后由 王金科 于 2012-9-1 13:19 编辑
  1. List<String> list = new ArrayList<String>();
  2.                
  3.                 list.add("a");
  4.                 list.add("b");
  5.                 list.add("c");
  6.                 list.add("d");
  7.                 list.add("e");
  8.                 list.add("f");
  9.                
  10.                
  11.                List list2 = list.subList(1, 4);
  12.                System.out.println(list2);//放在这里就不会出问题
  13.                
  14.                list.clear();
  15.                
  16.                System.out.println(list);
  17.                
  18.                System.out.println(list2);//这句放这里为什么不行啊,运行就抛异常java.util.ConcurrentModificationException
复制代码

作者: 张飞年    时间: 2012-9-1 03:54
发生这个异常的根本原因是改变列表数据结构而引发ConcurrentModificationException异常。这段代码与下面代码同理,都 会抛出并发修改异常。
  1. List<String> list = new ArrayList<String>();
  2.                 list.add("a");
  3.                 list.add("b");
  4.                 list.add("c");
  5.                 list.add("d");
  6.                 list.add("e");
  7.                 list.add("f");
  8.                 list.clear();
  9.                 System.out.println(list);
  10.                 List list2 = list.subList(1, 4);
  11.                 System.out.println(list2);
  12.                 System.out.println(list2);// 上面的代码等同于这样
复制代码
我查API找到如下说明:
在subList这个方法上有明确的说明(“返回的列表由此列表支持,因此返回列表中的非结构性更改将反映在此列表中,反之亦然。返回的列表支持此列表支持的所有可选列表操作。”),而它操作的是List中的一段。
这是异常抛出的原因:“如果支持列表(即此列表)通过任何其他方式(而不是通过返回的列表)从结构上修改,则此方法返回的列表语义将变为未定义(从结构上修改是指更改列表的大小,或者以其他方式打乱列表,使正在进行的迭代产生错误的结果)”。
这个为异常类的说明 :“此异常不会始终指出对象已经由不同 线程并发修改。如果单线程发出违反对象协定的方法调用序列,则该对象可能抛出此异常。例如,如果线程使用快速失败迭代器在 collection 上迭代时直接修改该 collection,则迭代器将抛出此异常”。


作者: 郑义    时间: 2012-9-1 07:16
很明显,这是并发修改异常
问题就在下边这几条语句:
  1. list.clear();
  2. System.out.println(list);
  3. System.out.println(list2);//这句放这里为什么不行啊,运行就抛异常java.util.ConcurrentModificationException
复制代码
在前边已经定义了List list2 = list.subList(1, 4),而现在对list进行了修改,由于list2是通过list而来的(是list的子集合),
所以当你修改list的时候list2的值就已经改变了。
这就会出现让虚拟机不知道你要打印的list2的值是什么的情况。就会抛出并发修改异常。
主意subList()方法中有这么一句说明:如果支持列表(即此列表)通过任何其他方式(而不是通过返回的列表)从结构上修改,则此方法返回的列表语义将变为未定义(从结构上修改是指更改列表的大小,或者以其他方式打乱列表,使正在进行的迭代产生错误的结果)。
这就是要抛出异常的原因。
作者: 芦曦    时间: 2012-9-1 09:46
List list2 = list.subList(1, 4);
list.clear();
System.out.println(list);
System.out.println(list2);//这里打印list2的时候,因为list已经发生了改变,而list2是list的子集合,有2条语句在操作同一个集合,虚拟机不知道所要打印的list2是什么情况,所以就出现了并发修改异常。
作者: 王金科    时间: 2012-9-1 11:28
芦曦 发表于 2012-9-1 09:46
List list2 = list.subList(1, 4);
list.clear();
System.out.println(list);

我的想法是这样的,
List list2 = list.subList(1, 4);这个返回的是一个视图,只是有一个映射关系,列表list2里面实际上是没有数据
当list被清空时,映射关系就不存在了,所以后面要打印list2时,虚拟机就不知道了
这样的恰当不恰当??
作者: AngieFans85    时间: 2012-9-1 12:51
很简单啊,subList(int fromIndex, int toIndex)方法返回的子列表对象中从fromIndex到toIndex - 1的范围内的元素,与父列表中从fromIndex到toIndex - 1的范围内的元素是相同的元素,是同一块内存中存放的元素,所以对子列表的修改会反映在父列表身上,反之亦然.所以当你调用父列表的clear()方法删除父列表的所有元素后,也会反应在子列表身上,也就是说此时子列表的所有元素都没有了.父列表的所有元素没有了,子列表自然不会再有任何元素了,因为子列表的元素就是引用的父列表中的元素.
作者: 黑马-李勇    时间: 2012-9-1 12:51
list=[a,b,c,d,e,f]
list2是由list的sublist而来的,因此,jvm不会开辟新的空间来存放list2,只会对list存一个引用关系。而ConcurrentModificationException异常:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。也说明了list与list2引用的是一个内容。
作者: AngieFans85    时间: 2012-9-1 12:52
王金科 发表于 2012-9-1 11:28
我的想法是这样的,
List list2 = list.subList(1, 4);这个返回的是一个视图,只是有一个映射关系,列表list2 ...

这样理解确实是说的过去,应该没错.




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2