Java ArrayList元素删除问题

分析Demo

List list = new ArrayList();
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("d");
list.add("e");

场景一

        for (int i = 0; i < list.size();i++){
            if(list.get(i).equals("b")){
                list.remove(i);
            }
        }

执行后的输出结果为:[a, b, c, d, e]

原因分析,查看源码中的remove方法:

public E remove(int index) {
        rangeCheck(index);

        modCount++; //记录集合修改次数
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            //移动复制数组内容,就是把删除后面的元素向前顶一位
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }


所以场景一中删除第一个b后,第二个b顶替了删除的位置但是集合的遍历依旧继续于是就导致第二个删除没有成功。

解决办法就是倒序删除

场景二

        for(String s:list){
            if(s.equals("b"))
            {
                list.remove(s);
            }
        }

结果是:java.util.ConcurrentModificationException 并发异常

public E next() {
        checkForComodification();
        try {
            E next = get(cursor);
            lastRet = cursor++;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
}
final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
}

做迭代器内部修改次数检查,因为上面的remove(Object)方法修改了modCount的值,所以才会报出并发修改异常。要避免这种情况的出现则在使用迭代器迭代时不要使用ArrayList的remove,改为用Iterator的remove即可

public static void remove(ArrayList list) 
    {
        Iterator it = list.iterator();
        while (it.hasNext()) 
        {
            String s = it.next();
            if (s.equals("b")) 
            {
                it.remove();
            }
        }
}

扩展:

public static void main(String[] args){
	//List list = new ArrayList<>();
	//List list = Collections.synchronizedList(new ArrayList<>());
    List list = new CopyOnWriteArrayList<>();
    for(int i=1;i<=3;i++){
        new Thread(()->{
            list.add(UUID.randomUUID().toString().substring(0,8));
            System.out.println(list);
        },String.valueOf(i)).start();
    }
	
	/**
	*
	* 1 故障现象
	* 多线程经常出现问题java.util.ConcurrentModificationException
	*
	* 2 导致原因
	*	并发争抢修改导致 参考我们的花名册签名情况
	*	一个人正在写入 另外一个同学过来争抢 导致数据不一致情况 并发修改异常
	*
	* 3 解决办法
	* 	3.1 new Vector();
	*   3.2 Collections.synchronizedList(new ArrayList(<>());
	*   3.3 new CopyOnWriteArrayList();
	*		写时复制
    *       CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器 
    *       Object[]添加,
    *       而是先将当前object[]进行Copy,复制出一个新的容器Object[] newElements,然后新的容* 
    *       器Object[] newElements
    *       里添加元素,添加完元素之后,再将原容器的引用指向新的容器setArray(newElements);这 
    *       样做的好处是可以对
    *       copyonwrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以 
    *       copyonwrite容器也是一种
    *       读写分离的思想,读和写不同的容器。
	*
	**/

 

 

 

 

 

 

 

你可能感兴趣的:(JAVA,arraylist)