ArryList源码

2020-09-02  本文已影响0人  我是许仙

ArryList适合查询多 不适合大量新增与删除的操作

属性
/**
 * 默认的数组长度
 */
private static final int DEFAULT_CAPACITY = 10;

/**
  空数组
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 不会被序列化的一个属性,list的核心是数组,这个属性存储了数组中的值
 */
transient Object[] elementData; // non-private to simplify nested class access

//这里并不是数组的长度,而是数组中存储数组的大小。
private int size;
初始化
/**
 * 内部构造一个null的数组
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        //新建一个内部的数组对象,长度是 initialCapacity
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

ArryList就是操作数组

add(Object o)

数组的下标是连续的,每次按照elementData[size++]新增数据。如果用默认构造函数new一个Arrylist对象,add的时候会创建一个长度为10的数组。当第11次新增数据的时候,数组的长度超过了10,这个时候触发扩容,每次扩容上一次容量的1.5倍。 int newCapacity = oldCapacity + (oldCapacity >> 1); 由此可见当触发扩容的时候会新新建数组并复制,会有新能开销,所以数组的长度尽量设置合理。

public boolean add(E e) {
    //计算容量的大小
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //添加数组中元素的值
    elementData[size++] = e;
    return true;
}
//确保内部容量
private void ensureCapacityInternal(int minCapacity) {
    //如果内部数组为{}对象 进行容量初始化
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 对比 10 与 minCapacity 中最大的一个数。
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    //判断需要的容量 是否比实际的容量大
    if (minCapacity - elementData.length > 0)
        //进行扩容
        grow(minCapacity);
}
//动态扩容核心代码
private void grow(int minCapacity) {
    //
    int oldCapacity = elementData.length;
    //右移 每次扩容上一次的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //复制到新的数组中
    elementData = Arrays.copyOf(elementData, newCapacity);
}
addAll(List list)

在原数组的后面添加新的数组。

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    //进行扩容
    ensureCapacityInternal(size + numNew);  // Increments modCount
    //复制原数组的0-0+numNew下标位置数据,复制到目标数组的size位置。
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}
add(inde,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++;
}
get(index)

通过下标获取list中的内部数组的下标对应的值,因为是下标寻址效率比较高。

public E get(int index) {
    //就是数组按照下标获取值  
    return elementData(index);
}
remove(int index)
public E remove(int index) {
    //操作次数自增
    modCount++;
   //通过下标获取老数据
    E oldValue = elementData(index);
    //需要移动的元素个数 
    int numMoved = size - index - 1;
    if (numMoved > 0)
        //进行数组的copy
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //先运算在赋值,把数组的最后一位设置为null
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}
remove(object o)

删除对象先循环内部数组,判断需要删除的值数组内元素是否匹配并找到下标,然后执行删除(数组的复制)

效率低下既要循环找到数据又要进行数组复制。

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;
}
toArry()

获取集合中的数组。

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

public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());

}

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

正确实例

ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");

Object[] objects = list.toArray();
for (Object o : objects){
    System.out.println(o.toString());
}

错误实例

ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");

String[] stringS= (String[]) list.toArray();
for (Object o : stringS){
    System.out.println(o.toString());
}
image-20200902201510148.png

因为无参数的方法默认返回的是object[]数组。强转会报错

toArry(T[] )

正确的写法,传入一个指定数组

String[] strings = list.toArray(new String[0]);
for (String i : strings) {
    System.out.println(i);
}
上一篇下一篇

猜你喜欢

热点阅读