面试必备:ArrayList源码解析(JDK8,2024年最新小程序毕业设计方向

//默认构造方法只是简单的将 空数组赋值给了elementData

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

//空数组

private static final Object[] EMPTY_ELEMENTDATA = {};

//带初始容量的构造方法

public ArrayList(int initialCapacity) {

//如果初始容量大于0,则新建一个长度为initialCapacity的Object数组.

//注意这里并没有修改size(对比第三个构造函数)

if (initialCapacity > 0) {

this.elementData = new Object[initialCapacity];

} else if (initialCapacity == 0) {//如果容量为0,直接将EMPTY_ELEMENTDATA赋值给elementData

this.elementData = EMPTY_ELEMENTDATA;

} else {//容量小于0,直接抛出异常

throw new IllegalArgumentException("Illegal Capacity: "+

initialCapacity);

}

}

//利用别的集合类来构建ArrayList的构造函数

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

//直接利用Collection.toArray()方法得到一个对象数组,并赋值给elementData

elementData = c.toArray();

//因为size代表的是集合元素数量,所以通过别的集合来构造ArrayList时,要给size赋值

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

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

if (elementData.getClass() != Object[].class)//这里是当c.toArray出错,没有返回Object[]时,利用Arrays.copyOf 来复制集合c中的元素到elementData数组中

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

} else {

//如果集合c元素数量为0,则将空数组EMPTY_ELEMENTDATA赋值给elementData

// replace with empty array.

this.elementData = EMPTY_ELEMENTDATA;

}

}

小结一下,构造函数走完之后,会构建出数组elementData和数量size。

这里大家要注意一下Collection.toArray()这个方法,在Collection子类各大集合的源码中,高频使用了这个方法去获得某Collection的所有元素。

关于方法:Arrays.copyOf(elementData, size, Object[].class),就是根据class的类型来决定是new 还是反射去构造一个泛型数组,同时利用native函数,批量赋值元素至新数组中。

如下:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {

@SuppressWarnings(“unchecked”)

//根据class的类型来决定是new 还是反射去构造一个泛型数组

T[] copy = ((Object)newType == (Object)Object[].class)

? (T[]) new Object[newLength]
(T[]) Array.newInstance(newType.getComponentType(), newLength);

//利用native函数,批量赋值元素至新数组中。

System.arraycopy(original, 0, copy, 0,

Math.min(original.length, newLength));

return copy;

}

值得注意的是,System.arraycopy也是一个很高频的函数,大家要留意一下。

public static native void arraycopy(Object src, int srcPos,

Object dest, int destPos,

int length);

常用API


1 增

每次 add之前,都会判断add后的容量,是否需要扩容。

public boolean add(E e) {

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

elementData[size++] = e;//在数组末尾追加一个元素,并修改size

return true;

}

private static final int DEFAULT_CAPACITY = 10;//默认扩容容量 10

private void ensureCapacityInternal(int minCapacity) {

//利用 == 可以判断数组是否是用默认构造函数初始化的

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

}

ensureExplicitCapacity(minCapacity);

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;//如果确定要扩容,会修改modCount

// overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

//需要扩容的话,默认扩容一半

private void grow(int minCapacity) {

// overflow-conscious code

int oldCapacity = elementData.length;

int newCapacity = oldCapacity + (oldCapacity >> 1);//默认扩容一半

if (newCapacity - minCapacity < 0)//如果还不够 ,那么就用 能容纳的最小的数量。(add后的容量)

newCapacity = minCapacity;

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

// minCapacity is usually close to size, so this is a win:

elementData = Arrays.copyOf(elementData, newCapacity);//拷贝,扩容,构建一个新数组,

}

public void add(int index, E element) {

rangeCheckForAdd(index);//越界判断 如果越界抛异常

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

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

size - index); //将index开始的数据 向后移动一位

elementData[index] = element;

size++;

}

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

Object[] a = c.toArray();

int numNew = a.length;

ensureCapacityInternal(size + numNew); // Increments modCount //确认是否需要扩容

System.arraycopy(a, 0, elementData, size, numNew);// 复制数组完成复制

size += numNew;

return numNew != 0;

}

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

rangeCheckForAdd(index);//越界判断

Object[] a = c.toArray();

int numNew = a.length;

ensureCapacityInternal(size + numNew); // Increments modCount //确认是否需要扩容

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;

}

总结:

add、addAll。

先判断是否越界,是否需要扩容。

如果扩容, 就复制数组。

然后设置对应下标元素值。

值得注意的是:

1 如果需要扩容的话,默认扩容一半。如果扩容一半不够,就用目标的size作为扩容后的容量。

2 在扩容成功后,会修改modCount

2 删

public E remove(int index) {

rangeCheck(index);//判断是否越界

modCount++;//修改modeCount 因为结构改变了

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 //置空原尾部数据 不再强引用, 可以GC掉

return oldValue;

}

//根据下标从数组取值 并强转

E elementData(int index) {

return (E) elementData[index];

}

//删除该元素在数组中第一次出现的位置上的数据。 如果有该元素返回true,如果false。

public boolean remove(Object o) {

if (o == null) {

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

if (elementData[index] == null) {

fastRemove(index);//根据index删除元素

return true;

}

} else {

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

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

fastRemove(index);

return true;

}

}

return false;

}

//不会越界 不用判断 ,也不需要取出该元素。

private void fastRemove(int index) {

modCount++;//修改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 //置空 不再强引用

}

//批量删除

public boolean removeAll(Collection<?> c) {

Objects.requireNonNull©;//判空

return batchRemove(c, false);

}

//批量移动

private boolean batchRemove(Collection<?> c, boolean complement) {

final Object[] elementData = this.elementData;

int r = 0, w = 0;//w 代表批量删除后 数组还剩多少元素

boolean modified = false;

try {

//高效的保存两个集合公有元素的算法

for (; r < size; r++)

if (c.contains(elementData[r]) == complement) // 如果 c里不包含当前下标元素,

elementData[w++] = elementData[r];//则保留

} finally {

// Preserve behavioral compatibility with AbstractCollection,

// even if c.contains() throws.

if (r != size) { //出现异常会导致 r !=size , 则将出现异常处后面的数据全部复制覆盖到数组里。

System.arraycopy(elementData, r,

elementData, w,

size - r);

w += size - r;//修改 w数量

}

if (w != size) {//置空数组后面的元素

// clear to let GC do its work

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

elementData[i] = null;

modCount += size - w;//修改modCount

size = w;// 修改size

modified = true;

}

}

return modified;

}

从这里我们也可以看出,当用来作为删除元素的集合里的元素多余被删除集合时,也没事,只会删除它们共同拥有的元素。

小结:

1 删除操作一定会修改modCount,且可能涉及到数组的复制相对低效

2 批量删除中,涉及高效的保存两个集合公有元素的算法,可以留意一下。

3 改

不会修改modCount,相对增删是高效的操作。

public E set(int index, E element) {

rangeCheck(index);//越界检查

E oldValue = elementData(index); //取出元素

elementData[index] = element;//覆盖元素

return oldValue;//返回元素

}

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

深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。

因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
img
img
img

既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!

由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频

如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
img

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

深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。

因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
[外链图片转存中…(img-ujVf48id-1712592257237)]
[外链图片转存中…(img-GvWfMft8-1712592257238)]
[外链图片转存中…(img-kpkndOVO-1712592257238)]

既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!

由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频

如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
[外链图片转存中…(img-adyUD0Kg-1712592257238)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值