ArrayList 源码分析

ArrayList 源码分析

版本:JDK 1.8

        ArrayList list = new ArrayList(5);

        list.add('a');
        list.add('b');
        list.add('c');
        list.add('d');
        list.add('e');
        list.add('f');
        list.add('g');

Debug

继承关系如下:

image-20220402131722745

image-20220402132025462

image-20220402131926070

image-20220402132100654image-20220402132301213

// 默认初始容量为 10(当ArrayList的容量低于9时才会用到)
private static final int DEFAULT_CAPACITY = 10;
// 存放数据的数组,数组的每个位置并不一定都填充数据,用transient修饰避免序列化、避免浪费资源
transient Object[] elementData;
// 记录实际存放的元素个数
private int size;
// 记录着ArrayList的修改次数,也就每次add或remove,它的值都会加1
protected transient int modCount = 0;

1、 构造函数 初始化

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

image-20220402132702710

2、添加数据

image-20220402132848756

public boolean add(E e) {
    // modCount++,并且校验容量,不够用就扩容
    ensureCapacityInternal(size + 1);  // 增加 modCount
    // 将新元素添加到新数组size位置,并将size加1(千万不要理解成将新元素添加到size+1的位置)
    elementData[size++] = e;
    return true;
}

// 确保容量够用
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    
    // 下标溢出表示容量不够用
    if (minCapacity - elementData.length > 0)
    	
    	//不够用扩容
        grow(minCapacity);
}

ArrayList 扩容方法 grow(int minCapacity)

private void grow(int minCapacity) {
    // oldCapaticy表示当前长度
    int oldCapacity = elementData.length;
    
    // ArrayList 的扩容是 1.5 倍 或 1.5 倍 - 1
    // oldCapacity为偶数按1.5倍扩容,oldCapacity为奇数按1.5倍-1扩容
    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);
}

删除元素

public boolean remove(Object o)

如果添加的是 list.remove(“a”) 或者 list.remove(‘a’) 有什么区别呢?

如果添加的为'a'为字符串,需要进行转换为Character。由Debug可知,也就相当于 new Character© 与 “a”,一个在堆中,一个地址在常量池中,地址不同

如果删除list.remove(‘a’) ,为什么不能成功?

删除时地址是用o.equals(elementData[index])进行比较,地址不一样,则无法删除。

public boolean remove(Object o) {
    // 两种情况,被删除的元素是否为 null
    // for 循环遍历、判断、删除(请注意:千万不要使用 foreach 进行删除!!!)
    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;
            }
    }
    // 如果删除了,返回为true,否则返回false。
    return false;
}

/*
 * Private remove method that skips bounds checking and does not
 * return the value removed.
 * 跳过边界检查的私有移除方法,并且不返回删除的值
 */
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        // 重点在这里,数据向前移动
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 清除 size-1 位置上的元素(设置空引用),让GC去收集该元素,同时 size 减 1
    elementData[--size] = null; 
}

💣💣💣注意:

  1. 如果存在多个对象o,仅仅删除距离 index = 0 处最近的一个元素;
  2. 元素被删除后,该位置不会空出来,后面的元素会前移。

所以 ArrayList 适合读多删少场景。

3、重新设置指定index位置的元素值public E set(int index, E element)

public E set(int index, E element) {
    // 有index,必检查
    rangeCheck(index);
    // 返回旧的(被重置的)元素
    E oldValue = elementData(index);
    // 直接把 index位置的元素覆盖掉行了
    elementData[index] = element;
    return oldValue;
}

4、获取指定index位置的元素 public E get(int index)

public E get(int index) {
   // 还是那句话,凡是遇到index索引,必检查
    rangeCheck(index);
    // 直接返回index的元素,数组查询还不快吗。
    return elementData(index);
}

E elementData(int index) {
    // 因为 ArrayList 本质上是个数组,直接通过index获取元素
    return (E) elementData[index];
}

5、判断元素是否存储 public boolean contains(Object o) 判断是否存在某个元素

public boolean contains(Object o) {
    // 简单粗暴,直接遍历查看index是否大于0
    return indexOf(o) >= 0;
}

// 查找元素是否存在,如果存在返回下标,否则返回 -1
public int indexOf(Object o) {
    // 遍历的时候分两种情况,是否为 null
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            // 用 equals 判断值是否相等
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}
a[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            // 用 equals 判断值是否相等
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值