Java源码之ArrayList(二)

昨天记录了一下怎么往ArrayList容器中放入数据,现在就开始看看ArrayList是怎样修改和删除对应容器数据的。
先从修改数据set方法看起
public E set(int index, E element) {
RangeCheck(index);

E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
    }
从源码中可以看出实际上set方法就是先根据数组和数组下标获得对应值并且把新值覆盖旧值,并且返回旧值;
既然修改方法看过了,下一个就属于删除方法了。
从容器中进行删除数据分两种方式:根据下标或者对象
先从根据下标进行删除开始看起
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
   System.arraycopy(elementData, index+1, elementData, index,
    numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
    }
从这段代码可以看出对应容器删除数据实际上操作的是里面数组,进行把老数组中的数据重新拷贝到数组中。删除数据后进行把数组最后一个数据清空,进行及时释放。并且返回进行删除的老对象
那通过下标删除数据时如此操作的,那通过对象删除有该如何进行的呢?
源码:
public boolean remove(Object o) {
if (o == null) {
            for (int index = 0; index < size; index++)
if (elementData[index] == null) {
   fastRemove(index);
   return true;
}
} else {
   for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
   fastRemove(index);
   return true;
}
        }
return false;
    }
看这个源码可以很清楚的知道实际上其操作机制实际上就是根据对应对象找到数组下标然后根据数组下标方式进行删除(就是通过fastRemove(index)),同时问题也产生了,为什么不直接调用remove(index)方法进行删除呢?
为了明白这个得先看一下fastRemove方法的源码:
private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // Let gc do its work
    }
看到这里就可以明白了。实际上fastRemove与remove方法实际上缺少
RangeCheck(index);方法的调用。该方法是判断传进来的index是否合法。remove(index)本身传来的参数是数组的下标需要进行校验一下否则会出现异常信息,然而remove(obj)是删除的对象,而下标是通过对象进行查询出来的,可以明确知道下标是合法的,因此可以省去判断。
blablablabla
看完ArrayList,想到了老早之前进行删除ArrayList容器中数据出现的问题了。
当时比较土的方法:
for(int i=ll.size()-1;i>=0;i++){
Integer obj = (Integer)ll.get(i);
if(2==obj){
ll.remove(i);//由于进行删除时数组结构会发生变化,因此需要进行从数组最后往前进行遍历
}
也可以通过这种方式进行删除数据:
Iterator iter = ll.iterator();
while(iter.hasNext()){
int num = (Integer)iter.next();
if(num == 2){
iter.remove();
}
第一种方式进行删除就不用说了,但是第二种它是怎么运作的呢?为什么直接操作对应迭代对象就可以移除对应集合中的数据,并且集合数组大小都发生变化了还能够正常运行?
现在就一览它工作的过程
在jdk1.6中进行ll.iterator()方法是在AbstractList类中进行实现了改该方法(针对1.7中出现在ArrayList中重写了该方法)
具体源码如下:
public Iterator<E> iterator() {
return new Itr();
    }
本身返回的是一个内部对象Itr()改对象又是Iterator接口的实现类,因此直接看对应内部类Itr中方法即可
先看看iter.hasNext()方法
public boolean hasNext() {
            return cursor != size();
}
本身是判断是否还有值。从源代码中可以看出。进行通过当前位置与数组的最大位置进行相对照,如果不相等就代表还有值,从这个代码就可以猜测出对应next()实现代码了。必定会有一个cursor属性自增,和从数组中进行返回一个指定下标的对象。
public E next() {
            checkForComodification();
   try {
E next = get(cursor);
lastRet = cursor++;
return next;
   } catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
   }
}
貌似刚猜测的不错主要就是这两个步骤,但是该方法增加了一下异常情况下的处理。既然获得对象可以知道了,那remove又是如何工作的呢?为什么进行删除时不像for循环一样冲后向前进行操作呢?
public void remove() {
   if (lastRet == -1)
throw new IllegalStateException();
            checkForComodification();

   try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
   cursor--;
lastRet = -1;
expectedModCount = modCount;
   } catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
   }
}
看到源码就真相大白了,原来它的内部调用了ArrayList的remove方法进行跟进index进行删除时间,但是其后进行cursor--操作表示查询下次进行查询时下标位置是不变的,因此虽然数组大小改变了,但是对应cursor值也得到了应有的改变。就不会导致错误了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值