List接口实现类ArrayList之浅谈

来源:互联网 发布:python调用caffemodel 编辑:程序博客网 时间:2024/05/07 12:15

ArrayList和HashMap一样,在大家的平时编程中,经常被使用,算是使用率最高的容器了,ArrayList其实内在就是一个数组,现在我来简要介绍一下ArrayList其内部的原理和核心技术。


      首先ArrayList内部使用的是一个数组来存储数据,然后还有一个size成员变量来记录该List中已经存放的数据个数。

      我们知道,数组是固定长度的,在定义之初就定义好的,那为什么ArrayList在使用的时候,不用考虑这个数组越界等问题呢?其实在ArrayList里面有一个方法,就是

ensureCapacity,具体实现定义如下

public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
   Object oldData[] = elementData;
   int newCapacity = (oldCapacity * 3)/2 + 1;
       if (newCapacity < minCapacity)
newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
}
    }

这个方法是用于在队列长度不够的时候,自动扩充队列长度的,每次扩充的比例是老长度*3/2 + 1,也就是说将老的长度扩大1.5倍,并且再加1,这个为什么以这个比例来扩展,背景和原因不太清楚。


接着,我们来看下里面的具体方法的实现

trimToSize

public void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (size < oldCapacity) {
            elementData = Arrays.copyOf(elementData, size);
}
    }
这个方法的目的就是将这个List内部的数组后面空的对象直接移除掉,类似String的trim方法,可以节省空间。比如这个List内部的数组已经定义为了32的长度,但是实际内部只放了12个数据,那么这个时候,调用了这个方法之后,会重新申请一个12长度的数组,将原来数组中的12个数据拷贝过来,然后将原来的32长度的数组destroy。


接着,我们看下indexOf方法的实现

public int indexOf(Object o) {
if (o == null) {
   for (int i = 0; i < size; i++)
if (elementData[i]==null)
   return i;
} else {
   for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
   return i;
}
return -1;
    }

这个方法,其实就是遍历数组,然后找到内容是一样的对象出来,不过这里需要注意的一点就是,ArrayList的元素是可以重复的,所以这里找到的也是第一个匹配的记录。


既然有indexOf,而且元素又是可以重复的,所以必然也就有一个lastIndexOf

public int lastIndexOf(Object o) {
if (o == null) {
   for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
   return i;
} else {
   for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
   return i;
}
return -1;
    }

这个的实现思路和上面的方法类似,只是从数组的最后面查询起。


接下来需要关注的是几个toArray的方法,这里,通过看内部的实现可以知道,其实返回的是一个新的数组,并且是通过内存拷贝的方式,具体的方法有

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }


后面的add方法和set方法如下

public E set(int index, E element) {
RangeCheck(index);


E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
    }


    public boolean add(E e) {
ensureCapacity(size + 1);  // Increments modCount!!
elementData[size++] = e;
return true;
    }

这两个方法也没啥技术含量,对于set方法来说,就是先检测一下这个index是否合法,如果不合法,就抛出异常,否则就将这个位置的数据替换掉,同时返回老数据。

对于add方法,则是先对长度+1做一次ensureCapacity,可能有概率会扩充这个数组长度,然后将数据追加到数组的有数据的最后一个位置的后面。


后面的remove等方法就不继续介绍了,只要知道了ArrayList其实内部就是一数组,并且会自动扩展长度,最后对外呈现就是一个不需要关心其长度的List,其他的一些方法就逗比较好理解了。


最后有一个问题需要特别说明,就是如果在new的时候不指定初始化长度,那么默认给的长度就是10,所以一般推荐大家在new一个ArrayList的时候,一定要指定一下初始化长度,减少ArrayList自己去扩充长度照成的内存、性能的浪费。