ArrayList源码分析、扩容机制面试题,数组和List的相互转换,ArrayList与LinkedList的区别

时间:2024-12-17 20:22:53
arr[i] = baseAddress + i * dataTypeSize

2.1.3 为什么数组索引从0开始呢?从1开始不行吗?

实际上并不是不行。而是如果数组索引从1开始的话,整体性能会变低。
因为寻址公式会变为a[i] = baseAddress + (i-1) *dataTypeSize,也就是说,多了一个减法操作。

3. ArrayList

ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ArrayList底层API的 ensureCapacity()操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。

 

3.1 ArrayList和和Vector的区别?(了解)

  • ArrayList 是 List 的主要实现类,底层使用 Object[]存储,适用于频繁的查找工作,线程不安全 。  

  • Vector 是 List 的古老实现类,底层使用Object[] 存储,线程安全。

3.2 ArrayList 可以添加 null 值吗?

可以。ArrayList 中可以存储任何类型的对象,包括 null 值。不过,不建议向ArrayList 中添加 null 值, null 值无意义,会让代码难以维护比如忘记做判空处理就会导致空指针异常。

3.3 ArrayList源码

成员变量

构造方法

第一个构造是带初始化容量的构造函数,可以按照指定的容量初始化数组
第二个是无参构造函数,默认创建一个空集合
/**
 *构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
 *如果指定的集合为null,throws NullPointerException。
 */
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
collection 对象转换成数组,然后将数组的地址的赋给 elementData

3.4 ArrayList 底层实现原理 ★

1. 底层数据结构

ArrayList 底层是用动态的数组实现的

2. 初始容量

ArrayList 初始容量为 0 ,当第一次添加数据的时候才会初始化容量为 10

3. 扩容逻辑

ArrayList 在进行扩容的时候是原来容量的 1.5 倍,每次扩容都需要拷贝数组

4. 添加逻辑

添加数据的流程:

1. 确保数组已使用长度( size )加 1 之后足够存下下一个数据
2. 计算数组的容量,如果当前数组已使用长度 +1 后的大于当前的数组长度,
3. 则调用 grow 方法扩容(原来的 1.5 倍)
4. 确保新增的数据有地方存储之后,则将新元素添加到位于 size 的位置上。
5. 返回添加成功布尔值。