ArrayList源码详解篇

// transient Object[] elementData;

return Arrays.copyOf(elementData, size);

}

使用了Arrays.copyOf()方法:

public static T[] copyOf(T[] original, int newLength) {

// original.getClass() 是 class [Ljava.lang.Object

return (T[]) copyOf(original, newLength, original.getClass());

}

copyOf()的具体实现:

public static <T,U> T[] copyOf(U[] original,

int newLength, Class<? extends T[]> newType) {

@SuppressWarnings(“unchecked”)

/**

  • 如果newType是Object[] copy 数组 类型就是 Object

  • 否则就是 newType 类型

*/

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

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

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

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

return copy;

}

我们知道ArrayList中elementData就是Object[]类型,所以ArrayListtoArray()方法必然会返回Object[]。

我们再看一下java.util.Arrays的内部ArrayList源码(截取的部分源码):

private static class ArrayList extends AbstractList

implements RandomAccess, java.io.Serializable {

// 存储元素的数组

private final E[] a;

ArrayList(E[] array) {

// 直接把接收的数组 赋值 给 a

a = Objects.requireNonNull(array);

}

/**

  • obj 为空抛出异常

  • 不为空 返回 obj

*/

public static T requireNonNull(T obj) {

if (obj == null)

throw new NullPointerException();

return obj;

}

@Override

public Object[] toArray() {

// 返回 a 的克隆对象

return a.clone();

}

}

这是Arrays.asList()方法源码

public static List asList(T… a) {

return new ArrayList<>(a);

}

不难看出来java.util.Arrays的内部ArrayList的toArray()方法,是构造方法接收什么类型的数组,就返回什么类型的数组。

所以,在我们上面的例子中,实际上返回的是String类型的数组,再将其中的元素赋值成Object类型的,自然报错。

🎠插入方法


在列表最后添加指定元素

/**

  • 在列表最后添加指定元素

  • @param e 要添加的指定元素

  • @return true

*/

public boolean add(E e) {

// 增加 modCount !!

ensureCapacityInternal(size + 1);

elementData[size++] = e;

return true;

}

在父类AbstractList上,定义了modCount 属性,用于记录数组修改的次数。

在指定位置添加指定元素

/**

  • 在指定位置添加指定元素

  • 如果指定位置已经有元素,就将该元素和随后的元素移动到右面一位

  • @param index 待插入元素的下标

  • @param element 待插入的元素

  • @throws 可能抛出 IndexOutOfBoundsException

*/

public void add(int index, E element) {

rangeCheckForAdd(index);

// 增加 modCount !!

ensureCapacityInternal(size + 1);

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

size - index);

elementData[index] = element;

size++;

}

插入方法调用的其他私有方法

/**

  • 计算容量

*/

private static int calculateCapacity(

Object[] elementData, int minCapacity) {

if (elementData ==

DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

return Math.max(DEFAULT_CAPACITY, minCapacity);

}

return minCapacity;

}

private void ensureCapacityInternal(int minCapacity) {

ensureExplicitCapacity(

calculateCapacity(elementData, minCapacity)

);

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;

// overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

🎍扩容方法


/**

  • 数组可以分配的最大size

  • 一些虚拟机在数组中预留一些header words

  • 如果尝试分配更大的size,可能导致OutOfMemoryError

*/

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

/**

  • 增加容量,至少保证比minCapacity大

  • @param minCapacity 期望的最小容量

*/

private void grow(int minCapacity) {

// 有可能溢出的代码

int oldCapacity = elementData.length;

int newCapacity = oldCapacity + (oldCapacity >> 1);

if (newCapacity - minCapacity < 0)

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

}

/**

  • 最大容量返回 Integer.MAX_VALUE

*/

private static int hugeCapacity(int minCapacity) {

if (minCapacity < 0) // overflow

throw new OutOfMemoryError();

return (minCapacity > MAX_ARRAY_SIZE) ?

Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

通常情况新容量是原来容量的1.5倍

如果原容量的1.5倍比minCapacity小,那么就扩容到minCapacity

特殊情况扩容到Integer.MAX_VALUE

看完构造方法、添加方法、扩容方法之后,我们发现,原来,new ArrayList()会将elementData 赋值为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,new ArrayList(0)会将elementData 赋值为 EMPTY_ELEMENTDATAEMPTY_ELEMENTDATA添加元素会扩容到容量为1,而DEFAULTCAPACITY_EMPTY_ELEMENTDATA扩容之后容量为10

通过反射我们可以验证这一想法。如下:

public static void main(String[] args) {

printDefaultCapacityList();

printEmptyCapacityList();

}

public static void printDefaultCapacityList() {

ArrayList defaultCapacity = new ArrayList();

System.out.println(

“default 初始化长度:” + getCapacity(defaultCapacity));

defaultCapacity.add(1);

System.out.println(

“default add 之后 长度:” + getCapacity(defaultCapacity));

}

public static void printEmptyCapacityList() {

ArrayList emptyCapacity = new ArrayList(0);

System.out.println(

“empty 初始化长度:” + getCapacity(emptyCapacity));

emptyCapacity.add(1);

System.out.println(

“empty add 之后 长度:” + getCapacity(emptyCapacity));

}

public static int getCapacity(ArrayList<?> arrayList) {

Class arrayListClass = ArrayList.class;

try {

// 获取 elementData 字段

Field field = arrayListClass.getDeclaredField(“elementData”);

// 开启访问权限

field.setAccessible(true);

// 把示例传入get,获取实例字段elementData的值

Object[] objects = (Object[]) field.get(arrayList);

//返回当前ArrayList实例的容量值

return objects.length;

} catch (Exception e) {

e.printStackTrace();

return -1;

}

}

🎨移除方法


移除指定下标元素方法

/**

  • 移除列表中指定下标位置的元素

  • 将所有的后续元素,向左移动

  • @param 要移除的指定下标

  • @return 返回被移除的元素

  • @throws 下标越界会抛出IndexOutOfBoundsException

*/

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

// 将引用置空,让GC回收

elementData[–size] = null;

return oldValue;

}

移除指定元素方法

/**

  • 移除第一个在列表中出现的指定元素

  • 如果存在,移除返回true

  • 否则,返回false

  • @param o 指定元素

*/

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;

}

移除方法名字、参数的个数都一样,使用的时候要注意

私有移除方法

/*

  • 私有的 移除 方法 跳过边界检查且不返回移除的元素

*/

private void fastRemove(int index) {

modCount++;

int numMoved = size - index - 1;

if (numMoved > 0)

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

numMoved);

// 将引用置空,让GC回收

elementData[–size] = null;

}

🥽查找方法


查找指定元素的所在位置

/**

  • 返回指定元素第一次出现的下标

  • 如果不存在该元素,返回 -1

  • 如果 o ==null 会特殊处理

*/

public int indexOf(Object o) {

if (o == null) {

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

if (elementData[i]==null)

return i;

} else {

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

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

return i;

}

return -1;

}

查找指定位置的元素

/**

  • 返回指定位置的元素

  • @param index 指定元素的位置

  • @throws index越界会抛出IndexOutOfBoundsException

*/

public E get(int index) {

rangeCheck(index);

return elementData(index);

}

该方法直接返回elementData数组指定下标的元素,效率还是很高的。所以ArrayList,for循环遍历效率也是很高的。

🎉序列化方法


/**

  • 将ArrayLisy实例的状态保存到一个流里面

*/

private void writeObject(java.io.ObjectOutputStream s)

throws java.io.IOException{

// Write out element count, and any hidden stuff

int expectedModCount = modCount;

s.defaultWriteObject();

// Write out size as capacity for behavioural compatibility with clone()

s.writeInt(size);

// 按照顺序写入所有的元素

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

s.writeObject(elementData[i]);

}

if (modCount != expectedModCount) {

throw new ConcurrentModificationException();

}

}

🥏反序列化方法


/**

  • 根据一个流(参数)重新生成一个ArrayList

*/

private void readObject(java.io.ObjectInputStream s)

throws java.io.IOException, ClassNotFoundException {

elementData = EMPTY_ELEMENTDATA;

// Read in size, and any hidden stuff

s.defaultReadObject();

// Read in capacity

s.readInt();

if (size > 0) {

// be like clone(), allocate array based upon size not capacity

ensureCapacityInternal(size);

Object[] a = elementData;

// Read in all elements in the proper order.

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

a[i] = s.readObject();

}

}

}

看完序列化,反序列化方法,我们明白:elementData之所以用transient修饰,是因为JDK不想将整个elementData都序列化或者反序列化,而只是将size和实际存储的元素序列化或反序列化,从而节省空间和时间

🩰创建子数组


public List subList(int fromIndex, int toIndex) {

subListRangeCheck(fromIndex, toIndex, size);

return new SubList(this, 0, fromIndex, toIndex);

}

我们看一下简短版的SubList

private class SubList extends AbstractList implements RandomAccess {

private final AbstractList parent;

private final int parentOffset;

private final int offset;

int size;

SubList(AbstractList parent,

int offset, int fromIndex, int toIndex) {

this.parent = parent;

this.parentOffset = fromIndex;

this.offset = offset + fromIndex;

this.size = toIndex - fromIndex;

this.modCount = ArrayList.this.modCount;

}

public E set(int index, E e) {

rangeCheck(index);

checkForComodification();

E oldValue = ArrayList.this.elementData(offset + index);

ArrayList.this.elementData[offset + index] = e;

return oldValue;

}

// 省略代码…

}

SubList的set()方法,是直接修改ArrayList中elementData数组的,使用中应该注意

SubList是没有实现Serializable接口的,是不能序列化的

💟迭代器


创建迭代器方法

public Iterator iterator() {

return new Itr();

}

Itr属性

// 下一个要返回的元素的下标

int cursor;

// 最后一个要返回元素的下标 没有元素返回 -1

int lastRet = -1;

// 期望的 modCount

int expectedModCount = modCount;

Itr的hasNext() 方法

public boolean hasNext() {

return cursor != size;

}

Itr的next()方法

public E next() {

checkForComodification();

int i = cursor;

if (i >= size)

throw new NoSuchElementException();

Object[] elementData = ArrayList.this.elementData;

if (i >= elementData.length)

throw new ConcurrentModificationException();

cursor = i + 1;

return (E) elementData[lastRet = i];

最后

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

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

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

Itr的hasNext() 方法

public boolean hasNext() {

return cursor != size;

}

Itr的next()方法

public E next() {

checkForComodification();

int i = cursor;

if (i >= size)

throw new NoSuchElementException();

Object[] elementData = ArrayList.this.elementData;

if (i >= elementData.length)

throw new ConcurrentModificationException();

cursor = i + 1;

return (E) elementData[lastRet = i];

最后

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

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

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

[外链图片转存中…(img-zYay71VL-1715616813396)]

[外链图片转存中…(img-20rRn6Fj-1715616813397)]

[外链图片转存中…(img-SDJ4A0PW-1715616813397)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

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

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值