Java集合系列(二)(1),前端开发大型网站

本文详细解释了JavaArrayList类的构造器,包括空参、集合和整数参数的创建,以及其内部的容量管理机制,重点介绍了添加、修改和删除元素的方法及扩容过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

================

//基础容量

private static final int DEFAULT_CAPACITY = 10;

//动态数组

transient Object[] elementData;

//空数组,供参数为集合或整数的构造器使用(详细请看下面的分析)

private static final Object[] EMPTY_ELEMENTDATA = {};

//空数组,供空参构造器使用(详细请看下面构造器的分析)

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//动态数组的长度(集合中元素的个数)

private int size;

//记录数组被修改的次数,用于防止fail-fast(具体看下面的分析)

protected transient int modCount = 0;

//动态数组的最大容纳量

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

光看属性似乎会有很多疑问或不解,大概对这些属性有个印象,结合下面的方法来看吧。

ArrayList 的构造器

==============

在 ArrayList 中,提供了三个构造器:空参构造器、参数为集合的构造器,参数为整数的构造器。

1. 空参构造器

public ArrayList() {

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

2. 参数为集合的构造器

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

//将集合转化为数组

elementData = c.toArray();

//判断c.toArray()的长度是否为0

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

// 检查c.toArray()是否为Object类型的数组,如果不是,则重新拷贝成一个Object类型的数组

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

//使用了Arrays这个工具类来进行浅复制,关于Arrays工具类的实现请移步相关文章

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

} else {

// 如果c.toArray()的长度为0,那么就直接使用预定义的空数组

this.elementData = EMPTY_ELEMENTDATA;

}

}

3. 参数为整数的构造器

public ArrayList(int initialCapacity) {//将合法的整型参数作为动态数组的初始化容量

//如果初始化容量大于0,则将参数作为数组的初始化容量

if (initialCapacity > 0) {

this.elementData = new Object[initialCapacity];

} else if (initialCapacity == 0) {//如果参数等于0,则使用预定义的空数组

this.elementData = EMPTY_ELEMENTDATA;

} else {//处理不合法的参数(参数小于0),抛出异常

throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);

}

}

常用的方法

=====

一、添加方法


ArrayList 中,提供了以下 4 个用于增加元素的方法:

1. boolean add(E e) —— 该方法用于向集合的末尾添加一个元素,返回一个布尔值,表示操作是否成功。

public boolean add(E e) {

//检查容量是否满足,具体分析看下面

ensureCapacityInternal(size + 1);

//将元素添加到数组的末尾

elementData[size++] = e;

//所有错误都在相应的方法里面处理了,所以此处返回true

return true;

}

private void ensureCapacityInternal(int minCapacity) {//参数为最小容量

//判断动态数组是否为预定义的空数组

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

//如果为空数组,则取出 默认容纳量(10)和最小容量两者之中的最大值

minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

}

ensureExplicitCapacity(minCapacity);

}

private void ensureExplicitCapacity(int minCapacity) {//参数为最小容量(就是上面方法那两者的最大值)

//修改计数器自增

modCount++;

//如果最小容量比动态数组的长度大,则进行扩容

if (minCapacity - elementData.length > 0)

//真正扩容的方法

grow(minCapacity);

}

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

//通过Arrays工具实现扩容

elementData = Arrays.copyOf(elementData, newCapacity);

}

private static int hugeCapacity(int minCapacity) {

//处理不合法参数

if (minCapacity < 0)

throw new OutOfMemoryError();

//如果需要的最小容纳量大于属性MAX_ARRAY_SIZE,则返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE

//注:MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 (详细看属性的定义)

return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;

}

2. void add(int,E) —— 添加元素到指定的位置。

public void add(int index, E element) {

//处理不合法的参数index。其实就是检测数组的下标是否越界(详细的分析看后面)

rangeCheckForAdd(index);

//检测数组容量是否满足

ensureCapacityInternal(size + 1);

//这是一个native方法,死活不让我看源码!

//大致可以看出意思就是将index位置后面的所有元素往后移一位,空出index这个位置

System.arraycopy(elementData, index, elementData, index + 1, size - index);

//在数组的index这个位置存储元素的值,从而就实现了插入元素的效果

elementData[index] = element;

//size是集合中元素的个数,自增

size++;

}

private void rangeCheckForAdd(int index) {

//如果索引大于数组的长度或者小于0,则为不合法,抛出异常。

if (index > size || index < 0)

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

3.  boolean addAll(Collection<? extend E>) —— 用于合并两个集合,意思就是将两个集合的元素放在源集合中,返回布尔值,表示操作是否成功

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

//老套路,先将集合转换为数组

Object[] a = c.toArray();

//转换后数组的长度

int numNew = a.length;

//还是熟悉的配方,这是检测容量的那套方法,前面已经分析过了。

ensureCapacityInternal(size + numNew);

//该死的native方法

//大致的意思就是将数组的所有元素拷贝到集合的末尾

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

//更新长度

size += numNew;

return numNew != 0;

}

4.  boolean addAll(int,Collection<? extend E>) —— 将一个集合中所有元素插入到源集合中的 index 位置,结果返回布尔值,表示操作是否成功。

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

//检测参数是否合法。下标是否越界,在前面已经分析过了。

rangeCheckForAdd(index);

//一样的套路:转化为数组

Object[] a = c.toArray();

int numNew = a.length;

//考虑容量

ensureCapacityInternal(size + numNew); // Increments modCount

//本地代码无法查看,大致意思就是将index位置后面所有元素往后移,空出a.length个位置,然后将数组的所有元素插入到其中。

int numMoved = size - index;

if (numMoved > 0)

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

numMoved);

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

size += numNew;

return numNew != 0;

}

二、修改与获取元素的方法


ArrayList 中,修改与获取元素的方法分别只有一个,就是 getset

E set(int index, E element) —— 修改 index 位置的元素,结果返回被修改的元素。

public E set(int index, E element) {

//这个方法与前面判断越界的方法有所不同,具体看下面的分析

rangeCheck(index);

//获取旧的值

E oldValue = elementData(index);

//设置新的值

elementData[index] = element;

return oldValue;

}

private void rangeCheck(int index) {

//显然,不同之处就是少了一个条件:index < 0,这也就是意味着可以传递一个负数作为参数

if (index >= size)

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

E get(int index) —— 获取 index 位置的元素。

public E get(int index) {

//检查下标是否越界

rangeCheck(index);

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

return elementData(index);

}

E elementData(int index) {

return (E) elementData[index];

}

三、删除元素的方法


ArrayList 中,提供了四个用于删除元素的方法。

1. E remove(int index) —— 移除集合中某个位置的元素,结果返回这个元素。

public E remove(int index) {

//检测下标是否越界

rangeCheck(index);

//修改计数器

modCount++;

//获取指定位置的元素

E oldValue = elementData(index);

//本地方法。。。

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

return oldValue;

}

2. boolean remove(Object o) —— 删除某个元素,如果集合中存在多个,则删除首次出现的,结果返回布尔值,表示是否成功。

public boolean remove(Object o) {

//分开两种情况考虑,一种为元素为null,另一种为非null,但是做法都是一致,遍历数组,拿到元素对应的索引,然后删除。

if (o == null) {

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

if (elementData[index] == null) {

fastRemove(index);

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

深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img
img
img
img

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

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
img

刷面试题

刷题的重要性,不用多说。对于应届生或工作年限不长的人来说,刷面试题一方面能够尽可能地快速自己对某个技术点的理解,另一方面在面试时,有一定几率被问到相同或相似题,另外或多或少也能够为自己面试增加一些自信心,可见适当的刷题是很有必要的。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 前端字节跳动真题解析

  • 【269页】前端大厂面试题宝典

将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频**

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-jRoHVQSw-1710605115759)]

刷面试题

刷题的重要性,不用多说。对于应届生或工作年限不长的人来说,刷面试题一方面能够尽可能地快速自己对某个技术点的理解,另一方面在面试时,有一定几率被问到相同或相似题,另外或多或少也能够为自己面试增加一些自信心,可见适当的刷题是很有必要的。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 前端字节跳动真题解析

  • 【269页】前端大厂面试题宝典

最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值