备战Java面试[JDK集合源码系列] -- ArrayList源码解析

*/

transient Object[] elementData; // non-private to simplify nested class access

/**

  • The size of the ArrayList (the number of elements it contains).

  • 集合中元素的个数

  • @serial

*/

private int size;

复制代码

  • DEFAULT_CAPACITY:集合的默认容量,默认为10,通过new ArrayList() 创建List集合实例时的默认容量是10。

  • EMPTY_ELEMENTDATA:空数组,通过new ArrayList(0) 创建List集合实例时用的是这个空数组。

  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA:默认容量空数组,这种是通过new ArrayList()无参构造方法创建集合时用的是这个空数组,与EMPTY_ELEMENTDATA的区别是在添加第一个元素时使用这个空数组的会初始化为DEFAULT_CAPACITY(10)个元素。

  • elementData:存储数据元素的数组,使用transient修饰,该字段不被序列化。

  • size:存储数据元素的个数,elementData数组的长度并不是存储数据元素的个数。\

3.ArrayList 构造方法


  • 有参构造 ArrayList(int initialCapacity)

/**

  • 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

  • 传入初始容量,如果大于0就初始化elementData为对应大小,如果等于0就使用EMPTY_ELEMENTDATA空数组,

  • 如果小于0抛出异常。

*/

// ArrayList(int initialCapacity)构造方法

public ArrayList(int initialCapacity) {

if (initialCapacity > 0) {

this.elementData = new Object[initialCapacity];

} else if (initialCapacity == 0) {

this.elementData = EMPTY_ELEMENTDATA;

} else {

throw new IllegalArgumentException("不合理的初识容量: " +

initialCapacity);

}

}

复制代码

  • 无参构造ArrayList()

/**

  • Constructs an empty list with an initial capacity of ten.

  • 构造一个初始容量为10的空数组

  • 不传初始容量,初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组,

  • 会在添加第一个元素的时候扩容为默认的大小,即10。

*/

public ArrayList() {

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

复制代码

  • 有参构造ArrayList(Collection c)

/**

  • Constructs a list containing the elements of the specified

  • collection, in the order they are returned by the collection’s

  • iterator.

  • 把传入集合的元素初始化到ArrayList中

  • @param c the collection whose elements are to be placed into this list

  • @throws NullPointerException if the specified collection is null

*/

public ArrayList(Collection<? extends E> c) {

// 将构造方法中的集合参数转换成数组

elementData = c.toArray();

if ((size = elementData.length) != 0) {

// 检查c.toArray()返回的是不是Object[]类型,如果不是,重新拷贝成Object[].class类型

if (elementData.getClass() != Object[].class)

// 数组的创建与拷贝

elementData = Arrays.copyOf(elementData, size, Object[].class);

} else {

// 如果c是空的集合,则初始化为空数组EMPTY_ELEMENTDATA

this.elementData = EMPTY_ELEMENTDATA;

}

}

复制代码

4. ArrayList 相关操作方法


添加操作

**1.add(E e)添加元素到集合中

添加元素到末尾,平均时间复杂度为O(1):**

/**

  • Appends the specified element to the end of this list.

  • 添加元素到末尾,平均时间复杂度为O(1)

    • @param e element to be appended to this list
  • @return true (as specified by {@link Collection#add})

*/

public boolean add(E e) {

// 每加入一个元素,minCapacity大小+1,并检查是否需要扩容

ensureCapacityInternal(size + 1); // Increments modCount!!

// 把元素插入到最后一位

elementData[size++] = e;

return true;

}

// 计算最小容量

private static int calculateCapacity(Object[] elementData, int minCapacity) {

// 如果是空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

// 返回DEFAULT_CAPACITY 和 minCapacity的大一方

return Math.max(DEFAULT_CAPACITY, minCapacity);

}

return minCapacity;

}

// 检查是否需要扩容

private void ensureCapacityInternal(int minCapacity) {

ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;// 数组结构被修改的次数+1

// overflow-conscious code 储存元素的数据长度小于需要的最小容量时

if (minCapacity - elementData.length > 0)

// 扩容

grow(minCapacity);

}

/**

  • 扩容

  • Increases the capacity to ensure that it can hold at lea

  • number of elements specified by the minimum capacity arg

  • 增加容量以确保它至少可以容纳最小容量参数指定的元素数量

  • @param minCapacity the desired minimum capacity

*/

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

}

// 使用最大容量

private static int hugeCapacity(int minCapacity) {

if (minCapacity < 0) // overflow

throw new OutOfMemoryError();

return (minCapacity > MAX_ARRAY_SIZE) ?

Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

复制代码

执行流程:

  • 检查是否需要扩容;

  • 如果elementData等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA则初始化容量大小为DEFAULT_CAPACITY;

  • 新容量是老容量的1.5倍(oldCapacity + (oldCapacity >> 1)),如果加了这么多容量发现比需要的容量还小,则以需要的容量为准;

  • 创建新容量的数组并把老数组拷贝到新数组;

**2.add(int index, E element)添加元素到指定位置

添加元素到指定位置,平均时间复杂度为O(n):**

/**

  • Inserts the specified element at the specified position in this

  • list. Shifts the element currently at that position (if any) and

  • any subsequent elements to the right (adds one to their indices).

  • 添加元素到指定位置,平均时间复杂度为O(n)。

    • @param index 指定元素要插入的索引
  • @param element 要插入的元素

  • @throws IndexOutOfBoundsException {@inheritDoc}

*/

public void add(int index, E element) {

// 检查是否越界

rangeCheckForAdd(index);

// 检查是否需要扩容

ensureCapacityInternal(size + 1); // Increments modCount!!

// 将inex及其之后的元素往后挪一位,则index位置处就空出来了

// 进行了size-索引index次操作

System.arraycopy(elementData, index, elementData, index + 1,

size - index);

// 将元素插入到index的位置

elementData[index] = element;

// 元素数量增1

size++;

}

/**

  • A version of rangeCheck used by add and addAll.

  • add和addAll方法使用的rangeCheck版本

*/

// 检查是否越界

private void rangeCheckForAdd(int index) {

if (index > size || index < 0)

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

复制代码

执行流程:

  • 检查索引是否越界;

  • 检查是否需要扩容;

  • 把插入索引位置后的元素都往后挪一位;

  • 在插入索引位置放置插入的元素;

  • 元素数量增1;

**3.addAll(Collection c)添加所有集合参数中的所有元素

求两个集合的并集:**

/**

  • Appends all of the elements in the specified collection to the end of

  • this list, in the order that they are returned by the

  • specified collection’s Iterator. The behavior of this operation is

  • undefined if the specified collection is modified while the operation

  • is in progress. (This implies that the behavior of this call is

  • undefined if the specified collection is this list, and this

  • list is nonempty.)

  • 将集合c中所有元素添加到当前ArrayList中

    • @param c collection containing elements to be added to this list
  • @return true if this list changed as a result of the call

  • @throws NullPointerException if the specified collection is null

*/

public boolean addAll(Collection<? extends E> c) {

// 将集合c转为数组

Object[] a = c.toArray();

int numNew = a.length;

// 检查是否需要扩容

ensureCapacityInternal(size + numNew); // Increments modCount

// 将c中元素全部拷贝到数组的最后

System.arraycopy(a, 0, elementData, size, numNew);

// 集合中元素的大小增加c的大小

size += numNew;

// 如果c不为空就返回true,否则返回false

return numNew != 0;

}

复制代码

执行流程:

  • 拷贝c中的元素到数组a中;

  • 检查是否需要扩容;

  • 把数组a中的元素拷贝到elementData的尾部;

访问操作

**4.get(int index)获取指定索引位置的元素

获取指定索引位置的元素,时间复杂度为O(1)。**

/**

  • Returns the element at the specified position in this list.

    • @param index index of the element to return
  • @return the element at the specified position in this list

  • @throws IndexOutOfBoundsException {@inheritDoc}

*/

public E get(int index) {

// 检查是否越界

rangeCheck(index);

// 返回数组index位置的元素

return elementData(index);

}

/**

  • Checks if the given index is in range. If not, throws an appropriate

  • runtime exception. This method does not check if the index is

  • negative: It is always used immediately prior to an array access,

  • which throws an ArrayIndexOutOfBoundsException if index is negative.

  • 检查给定的索引是否在集合有效元素数量范围内

*/

private void rangeCheck(int index) {

if (index >= size)

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

@SuppressWarnings(“unchecked”)

E elementData(int index) {

return (E) elementData[index];

}

复制代码

执行流程:

  • 检查索引是否越界,这里只检查是否越上界,如果越上界抛出IndexOutOfBoundsException异常,如果越下界抛出的是ArrayIndexOutOfBoundsException异常。

  • 返回索引位置处的元素;

删除操作

5.remove(int index)删除指定索引位置的元素,

删除指定索引位置的元素,时间复杂度为O(n)。

/**

  • Removes the element at the specified position in this list.

  • Shifts any subsequent elements to the left (subtracts one from their

  • indices).

  • 删除指定索引位置的元素,时间复杂度为O(n)。

    • @param index the index of the element to be removed
  • @return the element that was removed from the list

  • @throws IndexOutOfBoundsException {@inheritDoc}

*/

public E remove(int index) {

// 检查是否越界

rangeCheck(index);

// 集合底层数组结构修改次数+1

modCount++;

// 获取index位置的元素

E oldValue = elementData(index);

int numMoved = size - index - 1;

// 如果index不是最后一位,则将index之后的元素往前挪一位

if (numMoved > 0)

// 进行了size-索引index-1次操作

System.arraycopy(elementData, index + 1, elementData, index,

numMoved);

// 将最后一个元素删除,帮助GC

elementData[–size] = null; // clear to let GC do its work

// 返回旧值

return oldValue;

}

复制代码

执行流程:

  • 检查索引是否越界;

  • 获取指定索引位置的元素;

  • 如果删除的不是最后一位,则其它元素往前移一位;

  • 将最后一位置为null,方便GC回收;

  • 返回删除的元素。

注意:从源码中得出,ArrayList删除元素的时候并没有缩容。

**6.remove(Object o)删除指定元素值的元素

删除指定元素值的元素,时间复杂度为O(n)。**

/**

  • Removes the first occurrence of the specified element from this list,

  • if it is present. If the list does not contain the element, it is

  • unchanged. More formally, removes the element with the lowest index

  • i such that

  • (o==null ? get(i)==null : o.equals(get(i)))

  • (if such an element exists). Returns true if this list

  • contained the specified element (or equivalently, if this list

  • changed as a result of the call).

  • 删除指定元素值的元素,时间复杂度为O(n)。

    • @param o element to be removed from this list, if present
  •      要从此列表中删除的元素(如果存在的话)
    
  • @return true if this list contained the specified element

*/

public boolean remove(Object o) {

if (o == null) {

// 遍历整个数组,找到元素第一次出现的位置,并将其快速删除

for (int index = 0; index < size; index++)

// 如果要删除的元素为null,则以null进行比较,使用==

if (elementData[index] == null) {

fastRemove(index);

return true;

}

} else {

// 遍历整个数组,找到元素第一次出现的位置,并将其快速删除

for (int index = 0; index < size; index++)

// 如果要删除的元素不为null,则进行比较,使用equals()方法

if (o.equals(elementData[index])) {

fastRemove(index);

return true;

}

}

return false;

}

/*

  • Private remove method that skips bounds checking and does not

  • return the value removed.

  • 专用的remove方法,跳过边界检查,并且不返回删除的值。

*/

private void fastRemove(int index) {

// 少了一个越界的检查

modCount++;

// 如果index不是最后一位,则将index之后的元素往前挪一位

int numMoved = size - index - 1;

if (numMoved > 0)

System.arraycopy(elementData, index + 1, elementData, index,

numMoved);

// 将最后一个元素删除,帮助GC

elementData[–size] = null; // clear to let GC do its work

}

复制代码
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

针对以上面试题,小编已经把面试题+答案整理好了

最新大厂必问微服务面试题汇总:SpringCloud、Boot、Dubbo

最新大厂必问微服务面试题汇总:SpringCloud、Boot、Dubbo

最新大厂必问微服务面试题汇总:SpringCloud、Boot、Dubbo

面试专题

image

除了以上面试题+答案,小编同时还整理了微服务相关的实战文档也可以分享给大家学习

image

image

image
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
227)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

针对以上面试题,小编已经把面试题+答案整理好了

[外链图片转存中…(img-lEcwZC89-1712898998227)]

[外链图片转存中…(img-df8lv8F5-1712898998227)]

[外链图片转存中…(img-BAhRoMQP-1712898998227)]

面试专题

[外链图片转存中…(img-XLyXFbpD-1712898998228)]

除了以上面试题+答案,小编同时还整理了微服务相关的实战文档也可以分享给大家学习

[外链图片转存中…(img-6lLLiAx9-1712898998228)]

[外链图片转存中…(img-KufpWGKF-1712898998228)]

[外链图片转存中…(img-ecWPEWGo-1712898998228)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 22
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值