Java之容器学习(探究:循环删除容器中元素的正确姿势)------Java学习之路(20)

前言------

上课时老师稍微讲了下循环删除元素只有一种方式可以,其他会错,但是也就是这么说一下。。好奇的我,趁这篇博文,整理了3种方式,并分析了为啥可以和为啥不可以,整理得很用心,希望自己理解的同时也能帮到路过的你。

如同我上篇博文提到,循环变量容器可以用for(),foreach, 迭代器遍历这三种,那么是否意味着循环删除List中的元素也可以有这三种方式呢?想起来貌似可以,毕竟删除离不开循环鸭。我们就三种方式都来试试

目录

一.for循环删除List中的元素

二.迭代器删除List中的元素

三.foreach循环删除List中的元素

4.干货总结


一.for循环删除List中的元素

package Test;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

import Charactor.String;

public class TestDeleteOfContainer {

	List <String> strings = new ArrayList<>();
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		TestDeleteOfContainer myTest = new TestDeleteOfContainer();
		myTest.forDelete();

	}
	public void forDelete() {
		
		String temp ;
		//添加100个String对象
		for(int i = 0 ;i < 100 ;i++) {
			
			strings.add(new String("s" + i));
		}
		
		for(int i = 0 ;i <strings.size();i++) {
			
				strings.remove(i);//满足则删除
	
			
		}
		
		System.out.println("现在,strings的大小为:" + strings.size());
	}

运行结果:现在,strings的大小为:50  

怎么回事呢,为啥总共有100个String对象,删除了一遍后,却还剩50个呢?应该要只剩下0个而已啊!!

原因:在for循环删除的过程中,List表的size是随着删除不断改变的。

假如原先List中有1-8个元素,现在remove了箭头所指的第一个元素,因为第一个元素被删了,后面的元素就会一同往前挪一位,原先的2到达了1的位置,原先的3到达了2位置,i++导致箭头往后移,指向了元素3,其实2才是本应该被删除的元素,但是却被遗漏了,这也就是为什么还剩下50个元素没有被删除的原因。所以for循环方式不能正确删除List中的元素

二.迭代器删除List中的元素

package Test;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

import Charactor.String;

public class TestDeleteOfContainer {

	List <String> strings = new ArrayList<>();
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		TestDeleteOfContainer myTest = new TestDeleteOfContainer();
		myTest.iteratorDelete() ;

	}
	
	public void iteratorDelete() {
		
		String temp;
		//添加100个对象
		for(int i = 0 ;i < 100 ;i++) {
			
			strings.add(new String("S"+ i));
		}
		
		Iterator it = strings.iterator();
		while(it.hasNext()) {
			
			temp = (String)it.next();
			if(temp.equals("S8"){
			  it.remove();
                          } 
			
		}
		System.out.println("现在,strings的大小为:" + strings.size());
	}
}

运行结果:现在,strings的大小为:99。

上述运行结果说明成功删除。成功原因下面分析,暂且不提。

试想:写成下面这种形式呢?

package Test;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

import Charactor.String;

public class TestDeleteOfContainer {

	List <String> strings = new ArrayList<>();
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		TestDeleteOfContainer myTest = new TestDeleteOfContainer();
		myTest.iteratorDelete() ;

	}
	
	public void iteratorDelete() {
		
		String temp;
		//添加100个对象
		for(int i = 0 ;i < 100 ;i++) {
			
			strings.add(new String("S"+ i));
		}
		

		for(Iterator it = strings.iterator(); it.hasNext();) {
			
			
			temp = (String)it.next();
			if(temp.equals("S8")) {
			   strings.remove(temp);
			}
		}
		System.out.println("现在,strings的大小为:" + strings.size());
	}
}

运行结果:出现了java.util.ConcurrentModificationException  异常,这是什么原因呢?这如果要解释的话,必须要涉及Java中List的源码了。不过我先给大家讲一讲大致的原理:

List中有一个modCount的变量,是一个计数变量,计数迭代循环过程中调用了多少次List.add()和List.remove()。

但是List中还有一个expectedModCount的变量,这个变量初始值和modCount一致。每次迭代调用it.next()和it.remove()时都会先判断二者是否相同,如果不相同就会抛出并行修改的异常。为了在修改中不抛出异常,迭代器中有个remove()方法,这个方法本质上也是调用List的remove()方法,虽然每次remove后,modCount的值会+ 1,但是我无只要同步修改expectedModCount 的值就可以了,使得二者永远等同就 可以啦。

因此改成iI.remove就可以啦!!!

说了这么多,是不是没看懂?我放源码啦,自己领悟下

AbsrtactList中iteraor方法,它返回一个内部类,这个类实现了iterator接口,代码如下:

public Iterator<E> iterator() {
    return new Itr();
}
private class Itr implements Iterator<E> {
    int cursor = 0;
 
    int lastRet = -1;
 
    int expectedModCount = modCount;
 
    public boolean hasNext() {
        return cursor != size();
    }
 
    public E next() {
        checkForComodification();
        try {
            E next = get(cursor);
            lastRet = cursor++;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }
 
    public void remove() {
        if (lastRet == -1)
            throw new IllegalStateException();
        checkForComodification();
 
        try {
            AbstractList.this.remove(lastRet);
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            // 同步修改expectedModCount 的值
            expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
        }
    }
 
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
    }


是不是还没看明白呢?那就总结个干货,直接记起来,迭代循环删除元素中,只能调用迭代器的remove方法,不能调用List的

remove方法,对上面例子来说,就是只能用it.remove(temp),不能用string.remove(temp)~~~~~

三.foreach循环删除List中的元素

package Test;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

import Charactor.String;

public class TestDeleteOfContainer {

	List <String> strings = new ArrayList<>();
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		TestDeleteOfContainer myTest = new TestDeleteOfContainer();
		myTest.foreachDelete();

	}
	
	public void foreachDelete() {
		

		//添加100个String对象
		for(int i = 0 ;i < 100 ;i++) {
			
			strings.add(new String("s" + i));
		}
		
		for(String temp: strings) {
			
			if(temp.equals("S8")) {
			   strings.remove(temp);
			}
		}
		
		System.out.println("现在,strings的大小为:" + strings.size());
		
	}
}

运行结果:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at Test.TestDeleteOfContainer.foreachDelete(TestDeleteOfContainer.java:29)
    at Test.TestDeleteOfContainer.main(TestDeleteOfContainer.java:16)
 

也出现了java.util.ConcurrentModificationException  异常,这有是什么原因呢?其实这个原因和上面的一样,都是调用了List中的remove方法了,造成了并行修改的错误。因此foreach修改也不行。

四.干货总结

只能用迭代循环,且过程中只能调用迭代器的remove方法。

猜你喜欢

转载自blog.csdn.net/CCSGTC/article/details/83720005