黑马程序员技术交流社区
标题:
集合操作中遇到的小问题?
[打印本页]
作者:
金肖
时间:
2012-5-14 21:58
标题:
集合操作中遇到的小问题?
<P> </P>
复制代码
public class dd {
public static void main(String[] args) throws InterruptedException
{
Collection s = new HashSet();
s.add(32);
s.add("abc");
s.add("aaa");
Iterator it = s.iterator();
while(it.hasNext())
{
if(it.next().equals("abc"))
{
// it.remove(); //为什么这样操作就是ok
// s.remove("abc"); //这样就包异常了呢?
s.add(45); //这样也报异常呢?
}
}
System.out.println(s);
System.out.println(s.size());
}
}
复制代码
作者:
崔陈喜
时间:
2012-5-14 22:08
s.remove("abc"); 在迭代器中你已经定义it调用迭代功能,不能再用集合对象调用构造方法
在局部可以
作者:
wangrenjie
时间:
2012-5-14 22:15
使用迭代器只能遍历集合中的元素,不能对里面的数据进行操作。
作者:
冯建鹏
时间:
2012-5-14 22:18
迭代过程中是不能用集合的方法进行修改的。
比如说集合中有3个元素,迭代过程中他就认为有3个元素,如果你增加或者删除的话,迭代就不知道了。
记住迭代过程中是不能用集合的方法进行修改集合的。
作者:
It's_Zero
时间:
2012-5-14 22:38
本帖最后由 It's_Zero 于 2012-5-14 22:44 编辑
嗯 我看了下 我是想发是 it本身是指针 因为通过JDK的源码发现
Hast 是HashMap的一种特殊形式
public HashSet() {
map = new HashMap<E,Object>();
}
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
所以他是从HashMap中获得Key值 ,也就是指向Value的索引值
所以执行it.remove(); 是删除这个索引
而Iterator中只有void remove();并没有其他的带参数的remove方法 .所以s.remove("abc"); 这个方法的调用是错误的,呵呵呵 这个你完全可以自己覆盖写一个这个方法
public static void main(String[] args) throws InterruptedException {
Collection s = new HashSet();
s.add(32);
s.add("abc");
s.add("aaa");
Iterator it = s.iterator();
while (it.hasNext()) {
if (it.next().equals("abc")) {
// it.remove(); //为什么这样操作就是ok
// s.remove("abc"); //这样就包异常了呢?
System.out.println(it);//输出 java.util.HashMap$KeyIterator@13c5982// HashMap类中的对象KeyIterators(私有内部类) 返回的是key值
s.add(45); // 这样也报异常呢?
}
}
System.out.println(s);
System.out.println(s.size());
}
复制代码
作者:
It's_Zero
时间:
2012-5-14 22:47
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();//获取key索引值
}
}
复制代码
public final K getKey() {
return key;
}
作者:
黑马罗坚
时间:
2012-5-14 23:19
本帖最后由 nailsoul 于 2012-5-15 01:36 编辑
当你用集合的迭代器进行迭代操作时 可以通过迭代器的方法增删集合里德元素这样是没问题的
但是你用迭代器迭代时 在用集合的增删操作集合时就会抛ConcurrentModificationException异常
当你使用迭代器迭代容器时迭代器会创建一张单项索引表或数据结构指向容器的元素 当你通过集合的增删功能时会修改集合的大小 但是迭代器创建的单项索引表即数据结构的大小是不会变的 当你执行迭代器的hasnext方法时 迭代器会调用你容器你的判断方法判断单项索引表的大小是否等于你容器的大小 如果不等则会会抛ConcurrentModificationException异常
集合类中有个int型成员变量modCount 其实迭代器是集合的私有内部内Itr 有个成员变量名itrModCount(源代码中不是这个名字)声明变量时就把modCount的值赋值给intModCount 通过iterator方法创建Itr对象 当获取了迭代器在用集合的操作元素方法操作集合时会让modCount的值自增 再使用迭代器时 迭代器里有一个判断快速失败异常方法其他方法首先会去调用迭代器中的判断快速失败异常方法 该方法就是判断modCount是否等于intModCount 相等则继续往下执行不相等则会发生快速失败抛ConcurrentModificationException异常 这就是出错的原因
通过迭代器和高级迭代器对元素进行增删改操作时底层操作其实是调用的集合的增删改操作
那么为什么通过迭代器的方法操作集合的元素不会抛ConcurrentModificationException异常呢?
那时因为当你用迭代器进行集合中的元素增删改操作时调用完底层的功能后会从新把modCount 赋值给itrModCount 避免快速失败异常 如果是在多线程环境下同时用迭代器操作同一个集合是不安全的 如线程1通过迭代器删除了集合中的元素 没有执行完ItrModCount=modCount;而切换到主线程用迭代器对元素进行操作时会出现itrModCount并不等于modCount 则会抛ConcurrentModificationException异常 所以在多线程中同时用迭代器进行操作同一对象时一定要加入同步块中避免线程并发安全问题。ListIterator也一样。。。。。
List接口中的ListIterator继承迭代器 add 和set方法首先也会调用父类的判断快速失败异常方法 其他方法有的掉有的不掉用。。
所以为了避免快速失败异常 则当你用迭代器操作容器时就不要调用容器的某些方法(不是所以集合方法都自增modCount) 你非要调用的话 那没办法不想抛异常的话就不要再用原先的迭代器或ListIterator中的某些方法(ListIterator里不是所以的方法都判断modCount是否等于itrModeCount ... Iterator的所以方法都判断) 或者从新用集合的iterator或listIterator方法获取一个新的迭代器
在多线程中记得加入同步块。。。。
listIterator(int i); 通过集合调用带int参数的ListIterator时得到的迭代器里所有方法是不会判断itrModCount和modCount是否相等的
作者:
袁錦泰
时间:
2012-5-16 09:11
本帖最后由 袁錦泰 于 2012-5-16 19:26 编辑
具体的原因上面说的很详细了,所以我在这里为你提供针对性的解决方法。
由于迭代器在迭代过程中只能对元素进行获取和删除,无法再用集合操作元素,如果同时对元素进行修改,会导致迭代发生异常,所以需要使用其其接口listIterator,可以通过这个接口获取列表迭代器对象,在迭代过程中对元素进行增删改差,常见方法包括、增加、删除、插入、替换、正反遍历等。
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2