ArrayList的remove、序列化(一)

ArrayList作为常用的集合,经常使用,这里有两个需要注意一下的地方,分别是remove方法和序列化操作。

remove

先举个小示例:

public class t{
	public static void main(String[] args){
		ArrayList<String> arr=new ArrayList<String>();
		arr.add("a");
		arr.add("b");
		arr.add("c");
		for(int i=0;i<arr.size();i++){
			if(arr.get(i).equals("a")||arr.get(i).equals("b")){
				arr.remove(i);
			}
		}
		System.out.println(arr.toString());
	}
}
//输出结果:[b,c]
原因很简单,大家都知道是因为元素虽然被删除,但是访问下标依然在递增。避免方式就是改为由后向前遍历查询删除即可。

另一种出错方式:

public class t{
	public static void main(String[] args){
		ArrayList<String> arr=new ArrayList<String>();
		arr.add("a");
		arr.add("b");
		arr.add("c");
		for(String s:arr){
			if(s.equals("a")||s.equals("b")){
				System.out.println("before:"+s);
				arr.remove(s);
				System.out.println("after:"+s);
			}
		}
		System.out.println(arr.toString());
	}
}//抛出异常

在第一个删除元素操作之后,如果存在下一个待检查元素,则会抛出著名的ConcurrentModificationException,并且此处仍然没有避免上一个示例的错误区,使用for-each方式即使没有抛出异常,也会忽略掉已删除元素的下一个元素,例如:

public class t{
	public static void main(String[] args){
		ArrayList<String> arr=new ArrayList<String>();
		arr.add("a");
		arr.add("b");
		arr.add("c");
		for(String s:arr){
			if(s.equals("c")||s.equals("b")){
				System.out.println("before:"+s);
				arr.remove(s);
				System.out.println("after:"+s);
			}
		}
		System.out.println(arr.toString());
	}
}
//最后输出为:[a,c]

避免忽略检查元素很简单,倒序判断即可,此处重点观察ConcurrentModificationException异常。


使用for-each方式遍历元素,即为使用Iterator迭代查询,在说Iterator之前,先看一下ArrayList中的remove方法。

ArrayList中有两个remove方法:

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

        modCount++;//修改次数 +1
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);  //System类中的一个本地方法复制
        elementData[--size] = null; // 删除数组最后一个元素并修改大小

        return oldValue;
    }
public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);  //调用fastRemove修改
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);   //调用fastRemove修改
                    return true;
                }
        }
        return false;
    }
使用下标删除元素,modCount++修改次数加一,使用查找对象删除,调用fastRemove方法

private void fastRemove(int index) {
        modCount++;  //修改计数+1
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);//System类中的一个本地方法复制
        elementData[--size] = null; // 删除数组最后一个元素并修改大小
    }
此时比较remove(int index)与remove(Object o)+fastRemove(int index)

可以很明显看出,前后两种方式差别只有两处:

1.后者的删除多了一个查找定位index

2.将返回类型修改为boolean类型

差别神马的我们不关心,只关心共同点:

都有:modCount++


ArrayList中for-each循环下的remove操作使得modCount发生了变化,for-each是一个Iterator迭代,Iterator接口中只有三个方法:

public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
ArrayList中使用迭代的方式为一个实现Iterator接口的内部类:

public Iterator<E> iterator() {
        return new Itr();
    }
private class Itr implements Iterator<E> {
        public boolean hasNext() {
            return cursor != size;
        }
        public E next() {
            checkForComodification();
            ......................
        }
        public void remove() {
             ...............
             checkForComodification();
             ...............
        }
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
}
Itr类中增加了一个checkForComodification方法,比较modCount与expectedModCount的值,使用ArrayList的两个remove方法都只是修改了modCount,所以在迭代hasNext返回true时,next方法会抛出异常。修改方式很简单,使用Iterator的remove方法即可,remove函数为:

public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;//修改expectedModCount值
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
使用示例:

public class t{
	public static void main(String[] args){
		ArrayList<String> arr=new ArrayList<String>();
		arr.add("a");
		arr.add("b");
		arr.add("c");
		
		Iterator<String> it=arr.iterator();
		while(it.hasNext()){
			String s=it.next();
			if(s.equals("c")||s.equals("b")){
				System.out.println("before:"+s);
				it.remove();
				System.out.println("after:"+s);
			}
		}
		System.out.println(arr.toString());
	}
}
//最后输出为:[a]

最后多说一句,此处ArrayList的元素类型为String,如果是Integer类,可能存在一个小的情况:

public class t{
	public static void main(String[] args){
		ArrayList<Integer> arr=new ArrayList<Integer>();
		arr.add(2);
		arr.add(1);
		arr.add(3);
		arr.remove(2);//基本类型使用  remove(int index);  删除元素:3
		arr.remove((Integer)2);//使用remove(Object o);删除元素:2
	}
}

总结:

如果遍历中用到ArrayList的remove时,使用倒序查询删除即可。使用for-each图操作简化的话,就别有remove行为了,最简单还是直接使用Iterator的删除操作,免得写个遍历还要顾虑许多。






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值