设为首页 加入收藏

TOP

1、ArrayList源码解析(一)
2023-07-25 21:34:00 】 浏览:57
Tags:ArrayList 解析

1 概述

  1. ArrayList的元素:有序、可重复、允许null
  2. ArrayList没有实现同步(synchronized),因此线程不安全的。(vector线程安全)
  3. ArrayList底层数据结构为数组,容量(capacity):表示底层数组长度。容量不足则触发扩容,创建一个更长的数组,并将元素迁移到新数组
  4. 关于数组:数组一旦被创建,长度不可变
  5. 支持泛型

2 底层数据结构

几个重要的成员变量

    transient Object[] elementData; //存储列表元素的数组,容量(capacity)就是elementData的长度
    private int size;//元素的数量
    protected transient int modCount = 0; //list的修改次数
    private static final int DEFAULT_CAPACITY = 10; //初始化没有指定容量时,容量达到10就会触发扩容

ps:size跟capacity不一样,capacity>=size

3 构造函数

三个构造函数

  1. 指定初始容量大小,创建Object类型数组,且长度等于参数值
  2. 未指定初始容量大小,数据数组赋值为一个无限容量的空数组
  3. 造参数为集合类型,将参数转换成Object类型数组,并赋值给数据数组

注意,只有当第一次add元素时,才会指定数组的长度

源码:

    // 1、指定初始容量大小时,创建Object类型数组,且长度等于参数值
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA; //参数值为0,数据数组赋值为一个无限容量的空数组
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
// 2 不指定初始容量大小时,数据数组赋值为一个无限容量的空数组
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
// 3 构造参数为集合类型,将参数转换成Object类型数组,并赋值给数据数组
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

4 自动扩容

  1. 添加元素时,会判断添加后是否超出当前数组长度,超出则会执行数组扩容;
  2. 数组扩容时,会将老数组中的元素拷贝到新数组
  3. 如果未指定初始容量,那么容量达到10就会触发扩容
  4. 数组扩容后的容量为原来的1.5倍
  5. 尽可能评估所需要容量的大小,避免扩容。(因为扩容占用更多的内存)

参考源码:

重点!!!!!:添加前,都判断是否需要扩容:(如果size+1后,超过elementData的长度,则执行扩容,扩容为原来的1.5倍


public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // size 初始值为0,
    elementData[size++] = e;
    return true;
}

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

//计算所需容量:初始化未指定容量,第一次添加元素时,扩容阈值为10;反则,容量为当前长度+1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);//DEFAULT_CAPACITY = 10,即第一次添加时,数组长度变为10
    }
    return minCapacity;//如果数组不为空时,最小长度是当前长度+1
}

//再次计算数组所需容量
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;//列表修改次数递增
    //所需容量大于数组的长度,则执行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
//扩容,数组的内存状态已经发生变化了
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);//  新的长度为旧长度的1.5倍  【右移1位(除以2)】
    if (newCapacity - minCapacity < 0)  // 如果扩容后的长度小于所需要的最小长度,则使用最小长度(基本不会发生)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)  //这里是极限的情况,即逼近数组分配的最大内存空间
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);  // 执行数组拷贝
}

5 set() get() remove()

  • set()方法也就变得非常简单,直接对数组的指定位置赋值即可。
public E set(int index, E element) {
    rangeCheck(index);//下标越界检查
    E oldValue = elementData(index);
    elementData[index] = element;//赋值到指定位置,复制的仅仅是引用
    return oldValue;/
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇day19-web开发会话技术01 下一篇day02-搭建微服务基础环境01

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目