ArrayList源码分析

ArrayList源码分析

构造器

从源码的3个构造器中可以看出,都只是为了初始化数组elementData的大小

//当第一个元素被添加时的默认拓展的容量
private static final int DEFAULT_CAPACITY = 10;
//数组elementData设置为空的实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//数组elementData的初始化实例,与DEFAULT_CAPACITY相关联
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存放ArrayList元素的数组
transient Object[] elementData;
//元素的数量,不是elementData的大小
private int size;

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//构造容量为initialCapacity的数组
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}
 //用Collection构造ArrayList,可猜测所有Collection的实现类之间的互相转换可以用这个类似的构造器实现转换。
 public ArrayList(Collection<? extends E> c) {
     elementData = c.toArray();
     if ((size = elementData.length) != 0) {
         // c.toArray might (incorrectly) not return Object[] (see 6260652)
         // 比如c.toArray返回的是String[]
         if (elementData.getClass() != Object[].class)
             elementData = Arrays.copyOf(elementData, size, Object[].class);
     } else {
         // replace with empty array.
         this.elementData = EMPTY_ELEMENTDATA;
     }
 }

增删改查API

从增删改查API了解ArrayList原理, 通过数组的copy操作进行自动拓容。

增 :add(E e)

new ArrayList<String>().add("xxx")来说明添加元素的过程,首先是无参构造,然后调用add方法新增元素“xxx”。add方法代码如下:

//往数组末尾添加元素e
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  
    elementData[size++] = e;
    return true;
}

其中ensureCapacityInternal()方法是通过自动拓容来确保足够的容量来添加元素。逻辑:

a . 先计算最小容量

b. 如果比原来的elementData长度大,则进行拓容,使用Arrays.copyOf(elementData, newCapacity);

private void ensureCapacityInternal(int minCapacity) {
	ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

//根据最小容量来自动拓容
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

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

//计算最小的容量,如果ArrayList是无参构造的,返回DEFAULT_CAPACITY和minCapacity中大的一个
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

//对elementData进行拓容,通过复制生成一个包含原数组元素和新容量大小的数组
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);//相当于1.5倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        //超过最大值则等于Integer.MAX_VALUE : MAX_ARRAY_SIZE;
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

改:set(int index, E element)

set是将elementData中下标为index的元素替换为element, 并返回该下标的旧值

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

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

E elementData(int index) {
    return (E) elementData[index];
}

查:get(int index)

返回数组elementData中下标为index的元素

public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}

删: remove(int index)

删除下标为index的元素,返回旧值。

 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;
 }

从上面的代码通过System.arraycopy(....)数组复制来实现index后面的所有元素向左移动一位,来举例说明一下arraycopy的逻辑

//假如要删除元素b
String[] array = {"a","b","c","d"};
int b_index = 1;
int size =array.length();
int numMoved = size - b_index - 1;//要移动的元素数量4-1-1

/**
* 源数组array中b_index+1后的2个元素是 [c,d]; 
* 将[c,d] 在目标数组array的b_index位置开始替换
* 复制后的数组就变为了 {"a","c","d","d"},然后将最后一个元素设为null。
* 这里的源数组和目标数组都是array
*/
System.arraycopy(array, b_index+1, array, b_index, numMoved);

//最终 array 就是 {"a","c","d",null}
array[--size] = null;

另外add(int index, E element), addAll(Collection<? extends E> c)也是使用System.arraycopy实现的。

序列化

ArrayList实现了接口Serializable,但是它的属性transient Object[] elementData;使用了关键字transient,会在序列化的时候忽略elementData吗?如果真的忽略它,那反序列化的时候实际数据就没了,所以肯定是不行的。ArrayList类里有2个私有方法实现了序列化。

private void writeObject(java.io.ObjectOutputStream s){
    // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }
}

 private void readObject(java.io.ObjectInputStream s){...}

拿为什么要这样做,而不直接使用elementData来序列化呢?

writeObject的for循环中可以看出,用size作为最大值,而不是elementData.length(). 因为size是实际的元素大小,而elementData.length()有可能大于size的。 从add(E e)方法可以看出,当添加第一个元素时,elementData的长度是10,而size是1。

所以这样子实现序列化就会减少不必要的网络开销。

常用API DEMO

public class ArrayListDemo {

    public static void main(String[] args) {
        //Arrays.asList返回的ArrayList是Arrays的内部类,并不是java.util.ArrayList
        List<String> stringArray =Arrays.asList(new String[]{"a","b","c","d"});

        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.addAll(stringArray);
        //add arrayList: a,b,c,d
        System.out.println("add arrayList: "+toArrayString(arrayList));

        arrayList.addAll(2,stringArray);
        //add arrayList: a,b,a,b,c,d,c,d
        System.out.println("add arrayList: "+toArrayString(arrayList));

        arrayList.sort((e1,e2) -> {
            return e2.compareTo(e1);
        });
        //sort arrayList: d,d,c,c,b,b,a,a
        System.out.println("sort arrayList: "+toArrayString(arrayList));

        arrayList.replaceAll(e -> {return e+"1";});
        //replace All : d1,d1,c1,c1,b1,b1,a1,a1
        System.out.println("replace All : "+toArrayString(arrayList));

        arrayList.removeAll(Arrays.asList(new String[]{"d1"}));
        //remove All d1: c1,c1,b1,b1,a1,a1
        System.out.println("remove All d1: "+toArrayString(arrayList));

        arrayList.retainAll(Arrays.asList(new String[]{"a1","b1"}));
        //retain All a1,b1: b1,b1,a1,a1
        System.out.println("retain All a1,b1: "+toArrayString(arrayList));

        List<String> subArray = arrayList.subList(1,3);
        //subList : b1,a1
        System.out.println("subList : "+toArrayString(subArray));
		
        //[b1,a1]
        String[] toArray = subArray.toArray(new String[0]);
    }

    private static String toArrayString(List<String> arrayList){
        StringBuilder sb = new StringBuilder();
        arrayList.forEach(e ->{
            sb.append(e).append(",");
        });
        sb.deleteCharAt(sb.lastIndexOf(","));
        return sb.toString();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值