java的ArrayList源码解读

谈到ArrayList,先说说优缺点:

因为ArrayList底层使用数组实现,所以优缺点与数组类似。

优点:

1、根据下标遍历元素效率较高。

2、根据下标访问元素效率较高。

3、在数组的基础上封装了对元素操作的方法。

4、可以自动扩容。

缺点:

1、插入和删除的效率比较低。

2、根据内容查找元素的效率较低。

扩容规则:每次扩容现有容量的50%。

1. 先看看ArrayList的成员变量:

   
    //默认初始容量:
     private static final int DEFAULT_CAPACITY = 10;

    //用于空实例的共享空数组实例。
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //用于默认大小的空实例的共享空数组实例。我们将其与EMPTY_ELEMENTDATA区分开来,从而
    //知道什么时候添加了第一个元素
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    
     //ArrayList存储数据的数组就是这个了:
    transient Object[] elementData; // non-private to simplify nested class access

    //这个不多说了,当前容量大小
    private int size;

大家都知道Arraylist用迭代器或者foreach的时候不能对元素进行修改,为啥呢?

原因这里:

protected transient int modCount = 0;

官方给的解释,这个成员变量用来监控Arraylist被操作的次数(例如增删查改);大致的意思是:modCount这个参数记录了 某个List改变大小的次数,如果modCount改变的不符合预期,那么就会抛出异常。

那么记录了有什么用呢?

拿迭代器来说,ArrayList的迭代器有这一操作:

int expectedModCount = modCount;

简单的说:迭代前让expectedModCount初始化为modCont,迭代过程中要是我们做了修改数据的操作,modCount++;

与预期不一样则就会抛出异常;你就不想问我modCount++什么时候执行的?继续看

 

2. 说说常用的add (Object o)方法,先看看源码:



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

直接看ensureCapacityInternal(size + 1);这个方法,后面那两句大家都明白;


//这里面用到了calculateCapacity,ensureExplicitCapacity两个方法,下面列出来了
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }


//elementData就是当前的集合数组,前面说到的,这里判断是否为空,返回当前适合的数组容量大小
//为空返回默认大小10; 不为空返回size+1;

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }


//!!!!!!!!!!!!这里就进行了modCount++; 注意注意!
//这里就需要注意了,当前需要的容量size+1 若大于当前数组长度,那么就意味着我们得先扩容
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;      

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

哎,看来add方法的第一句就涉及这么多,还没讲完,这里看到了容量大小不够就得调用grow(minCapacity);

那就继续看看grow(minCapacity);既然走到grow这一步,这里就得注意minCapacity可以认为就是size+n;

private void grow(int minCapacity) {
        //先保存当前elementData的长度大小;
        int oldCapacity = elementData.length;

        
        //这里注意oldCapacity + (oldCapacity >> 1),前面说过每次扩容扩容当前的一半
        //这里用了位运算: 例如 100 >>1  =10 也就是2进制向右移了一位 
        int newCapacity = oldCapacity + (oldCapacity >> 1);
         
        //这里判断扩增了一半后,是否还是小于当前需求,假如还是不满足干脆就让当前扩容大小等于它    
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
         
        //这里就是新的数组容量是否大于MAX_ARRAY_SIZE,        
        //ArrayList定义的MAX_ARRAY_SIZE = 整型数据的最大值-8,也就是说还会扩容,这里就不深究了
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        
        //这里实际上就是拷贝了,新的数组大小确定了,那就得把旧数组的数据放到新数组里来
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

到这里Add方法就结束了;

再看看他的同类:Add(index, object) 这就是常用的向指定位置添加元素;

public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

这里只需要看rangeCheckForAdd(index); 和System.arraycopy(elementData, index, elementData, index + 1, size - index);

//这里不多说就是判断有没有越界
private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
//这是System类的里的方法,可以看出来参数就是将当前插入的index位置后面的元素后移一位
public static native void arraycopy
(Object src,  int  srcPos,Object dest, int destPos, int length);

就先写这么多吧,记录一下

 

 

 

 

 

你可能感兴趣的:(java面试基础)