深入剖析ArrayList的底层源码,再也不怕面试官问了!

3. Collection<? extends E>型构造方法

源码剖析

/**

  • Constructs a list containing the elements of the specified

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

  • iterator.

  • @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

elementData = c.toArray();

// 如果数组的大小不等于 0

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

// c.toArray might (incorrectly) not return Object[] (see 6260652)

// 如果c.toArray()返回的数组类型不是Object[]类型的

// 则利用Arrays.copyOf()创建一个大小为size的、类型为Object[]的数组, 并将elementData中的元素复制到新的数组中

// 最后让elementData 指向新的数组

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

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

} else { // 否则设置元素数组为空数组

// replace with empty array.

this.elementData = EMPTY_ELEMENTDATA;

}

}

总结

  • 将参数中的集合转换为数组,赋值给 elementData

  • .给size进行赋值,size代表集合元素数量。判断参数是否为空,如果数组的大小不等于 0,则进一步判断是否转化为Object类型的数组,如果不是,则进行复制;

  • 否则,设置元素数组为空数组

三、常用方法及其源码剖析

1. get()方法

get(int index)方法是返回集合中指定位置的元素。

源码剖析

/**

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

// 返回指定索引处的值

return elementData(index);

}

  • 首先会检查索引值是否越界,如果索引值大于数组大小,则会抛出异常。

源码如下:

private void rangeCheck(int index) {

// 索引值大于数组大小

if (index >= size)

// 抛出异常

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

  • 如果所引是合法的,则调用elementData(int index)方法获取指定位置的元素。
2. set() 方法

set(int index, E element) 方法将index索引处的元素替换成element对象,返回被替换的旧元素。

源码剖析

/**

  • Replaces the element at the specified position in this list with

  • the specified element.

  • @param index index of the element to replace

  • @param element element to be stored at the specified position

  • @return the element previously at the specified position

  • @throws IndexOutOfBoundsException {@inheritDoc}

*/

public E set(int index, E element) {

// 检查下标索引是否越界

rangeCheck(index);

// 指定下标处的旧值

E oldValue = elementData(index);

// 指定下标处赋新的值

elementData[index] = element;

// 返回旧值

return oldValue;

}

3. add() 方法
  • add(E e) 方法 将指定的元素追加到此集合的末尾

源码剖析

/**

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

  • @param e element to be appended to this list

  • @return true (as specified by {@link Collection#add})

*/

public boolean add(E e) {

// 插入元素之前,会检查是否需要扩容

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

// 将元素添加到数组中最后一个元素的后面,最后将集合中实际的元素个数加 1

elementData[size++] = e;

return true;

}

  • add(int index, E element) 在集合元素序列 index 位置处插入

源码剖析

/**

  • 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).

  • @param index index at which the specified element is to be inserted

  • @param element element to be inserted

  • @throws IndexOutOfBoundsException {@inheritDoc}

*/

public void add(int index, E element) {

// 检查下标索引是否越界

rangeCheckForAdd(index);

// 插入元素之前,会检查是否需要扩容

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

// 将 index 位置及其后面的所有元素都向后移一位

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

size - index);

// 将新元素插入到 index 位置处,元素个数加 1

elementData[index] = element;

size++;

}

4. remove() 方法
  • remove(int index) 方法是删除该集合中指定位置的元素

源码剖析

/**

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

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

  • indices).

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

modCount++;

// 返回被删除的元素值

E oldValue = elementData(index);

// 需要移动的元素的个数

int numMoved = size - index - 1;

if (numMoved > 0)

// 将 index + 1 及其后面的元素向前移动一位,覆盖被删除的元素值

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

numMoved);

// 把数组最后一个元素赋值为null,将元素个数减一

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

// 返回旧值

return oldValue;

}

  • remove(Object o) 方法是删除指定的元素,如果有重复的元素,则只会删除下标最小的元素

源码剖析

/**

  • 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).

  • @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++)

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;

}

}

return false;

}

  • fastRemove(int index) 方法是快速删除,删除跳过边界检查的方法,也不返回删除的元素值

源码剖析

/*

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

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

}

5. size() 方法

返回集合中的元素个数

源码剖析

/**

  • Returns the number of elements in this list.

  • @return the number of elements in this list

*/

public int size() {

return size;

}

6. indexOf(Object o) 方法

返回对象o在集合中第一次出现的位置的索引

源码剖析

/**

  • Returns the index of the first occurrence of the specified element

  • in this list, or -1 if this list does not contain the element.

  • More formally, returns the lowest index i such that

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

  • or -1 if there is no such index.

*/

public int indexOf(Object o) {

// 如果查找的元素为空

if (o == null) {

// 循环遍历数组

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

// 如果 i 位置的原素为空

if (elementData[i]==null)

// 返回下标

return i;

// 否则,查找的元素不为空

} else {

// 循环遍历数组

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

// 找到第一个和要查找的元素相等的元素

if (o.equals(elementData[i]))

// 返回下标

return i;

}

// 返回 -1.则表示没有找到

return -1;

}

四、ArrayList 和 Vector 的区别

  • ArrayList 和 Vector 底层都是 数组

  • ArrayList 每次扩容的情况下扩容为原来的1.5 倍。线程不安全,当多个线程同时访问同一个ArrayList 集合时,如果两个或两个以上的线程修改了 ArrayList 集合,则必须手动保证该集合的同步性。

  • Vector 是同步类,其线程安全,但是它的访问比较慢。Vector 每次扩容为其空间大小的 2 倍。

五、对扩容原理源码剖析及其思考

  1. 在add()方法中调用ensureCapacityInternal()方法,用来确保添加元素的时候,最小容量是 minCapacity。

源码剖析

private void ensureCapacityInternal(int minCapacity) {

// 判断元素数组是否为空数组

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

// 取最大值

minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

}

ensureExplicitCapacity(minCapacity);

}

  1. 在ensureCapacityInternal()方法里面调用ensureExplicitCapacity(int minCapacity)方法,用来判段集合在添加元素的时候是否需要对当前的数组进行扩容。

源码剖析

private void ensureExplicitCapacity(int minCapacity) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

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

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

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

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

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

img

最后的内容

在开头跟大家分享的时候我就说,面试我是没有做好准备的,全靠平时的积累,确实有点临时抱佛脚了,以至于我自己还是挺懊恼的。(准备好了或许可以拿个40k,没做准备只有30k+,你们懂那种感觉吗)

如何准备面试?

1、前期铺垫(技术沉积)

程序员面试其实是对于技术的一次摸底考试,你的技术牛逼,那你就是大爷。大厂对于技术的要求主要体现在:基础,原理,深入研究源码,广度,实战五个方面,也只有将原理理论结合实战才能把技术点吃透。

下面是我会看的一些资料笔记,希望能帮助大家由浅入深,由点到面的学习Java,应对大厂面试官的灵魂追问

这部分内容过多,小编只贴出部分内容展示给大家了,见谅见谅!

  • Java程序员必看《Java开发核心笔记(华山版)》

  • Redis学习笔记

  • Java并发编程学习笔记

四部分,详细拆分并发编程——并发编程+模式篇+应用篇+原理篇

  • Java程序员必看书籍《深入理解 ava虚拟机第3版》(pdf版)

  • 大厂面试必问——数据结构与算法汇集笔记

其他像Spring,SpringBoot,SpringCloud,SpringCloudAlibaba,Dubbo,Zookeeper,Kafka,RocketMQ,RabbitMQ,Netty,MySQL,Docker,K8s等等我都整理好,这里就不一一展示了。

2、狂刷面试题

技术主要是体现在平时的积累实用,面试前准备两个月的时间再好好复习一遍,紧接着就可以刷面试题了,下面这些面试题都是小编精心整理的,贴给大家看看。

①大厂高频45道笔试题(智商题)

②BAT大厂面试总结(部分内容截图)

③面试总结

3、结合实际,修改简历

程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:

以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
的时间再好好复习一遍,紧接着就可以刷面试题了,下面这些面试题都是小编精心整理的,贴给大家看看。

①大厂高频45道笔试题(智商题)

[外链图片转存中…(img-cgLpQAgn-1712485012813)]

②BAT大厂面试总结(部分内容截图)

[外链图片转存中…(img-0xSuN6gd-1712485012813)]

[外链图片转存中…(img-HQIdthZa-1712485012813)]

③面试总结

[外链图片转存中…(img-Mj59beFs-1712485012813)]

[外链图片转存中…(img-NrSx0Xli-1712485012813)]

3、结合实际,修改简历

程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:

[外链图片转存中…(img-r7EbBoiM-1712485012814)]

以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值