arraylist长度_深入理解 ArrayList

13836b088b62b6d6cfd15909a685633b.png

简介

ArrayList 是基于数组实现的,支持动态扩容。

查询效率高,增删效率低。

线程不安全,在多线程环境下,可以使用 Vector(数组容器)或者可以通过 Collections.synchronizedList 将普通的 ArrayList 包装成一个线程安全的数组容器,原理同 Vector 类似, 将所有的方法套上 synchronized

源码

继承类, 实现接口

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable,java.io.Serializable{
    // 默认初始化容量
    private static final int DEFAULT_CAPACITY = 10;
​
    /*Shared empty array instance used for empty instances.*/   
    /*调用有参构造器传入 0 是,把该空数组赋值给 elementData*/
    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.
     */
    /* 使用无参构成函数时,把这个空数组赋值 elementData ,当第一次调用 add() 才分配默认的初始化值 DEFAULT_CAPACITY */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
​
    // ArrayList 底层存储元素的数组  
    transient Object[] elementData;
​
    // ArrayList中元素的数量,size() 返回的是这个变量。 
    private int size;
}

ArrayList 继承 AbstractList 抽象类,同时实现了 RandomAccess(支持随机访问), Cloneable(支持克隆), Serializable(支持序列化)

构造器

有参构造器

根据传入的参数初始化底层 elementData

/**
 * Constructs an empty list with the specified initial capacity.
 * @param  initialCapacity  the initial capacity of the list
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
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);
    }
}

无参构造函数

调用无参构造函数时,elementData 赋值一个空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,当第一次调用 add() 才初始化化为默认的大小 DEFAULT_CAPACITY

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

在 1.7 和 1.8 的初始化版本的区别

1.7版本以前无参构造函数会调用 this(10) ,将容量初始化为10, 1.7以后(包括1.7)默认将 底层数组elementData 赋值为空数组,只有第一次add的时候容量会变成10。

add 方法

直接添加的方式。

public boolean add(E e) {
    // 判断底层数组长度,如果长度不够,需要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

指定 index 添加

public void add(int index, E element) {
    // 检验 index 的合法
    rangeCheckForAdd(index);
    // 判断长度,如果长度不够,需要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 将原先数组 index 所指的位置的元素及 index 后面的元素往后搬移一位。 
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    // 将新的元素添加到 index 的位置
    elementData[index] = element;
    size++;
}

ensureCapacityInternal() :判断数组的长度是否足够,不足则需要扩容。

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 判断此时 elementData 是否是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    // 是就返回 DEFAULT_CAPACITY
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {     
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
​
private void ensureExplicitCapacity(int minCapacity) {
    // 记录list改动的次数(增加,删除)
    modCount++;
​
    // overflow-conscious code
    // 判断 minCapacity 是否底层数组 elementData 的长度,大于则扩容
    if (minCapacity - elementData.length > 0)
        // 数组扩容的方法
        grow(minCapacity);
}

数组扩容的方式 grow()

1.8 版本采用的是 位运算,向右移动一位,相当于除于2,效率更高了。把长度扩大 1.5 倍了。

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // 采用移位运算,移位运算的效率比除数高
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // newCapacity < minCapacity 的情况 
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // newCapacity 大于 Java 允许的数组的最大长度的情况
    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);
}
​
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}
​

remove()

删除指定 index, 通过计算需要移动的元素,调用 System.arraycopy 往前移动 size-index-1 个位置。大量的搬移操作使得 ArrayList 的删除效率不高。

public E remove(int index) {
    // 检查 index 是否大于 size
    rangeCheck(index);
    // 
    modCount++;
    E oldValue = elementData(index);
    // 计算需要移动的元素的数量
    int numMoved = size - index - 1;
    // 将元素往前移动 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;
}

ArrayList(int initialCapacity)会不会初始化数组大小?

会初始化数组,但 list 的大小不变,list.size() 是返回size。

一个典型的 bug

public static void main(String[]args){
    ArrayList<Integer> list = new ArrayList<>(10);
    System.out.println(list.size());
    list.set(5, 1);
}
//  System.out.println(list.size()); 打印0,
// 然后 list.set() 会报错,数组越界

通过查看源码可以看出,new ArrayList<>(10) 会初始化底层数组的大小,但里面size=0,即没有添加元素,在调用 set 的时候,set 方法里面的 rangeCheck(index); 会将 index 与 size 比较,index 大于 size 就会抛出越界异常。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值