ArrayList源码阅读
一、基本介绍
2020/8/2
2020年3月份开始学习java,由于疫情原因在家上的网课,与其说是上网课,更多的其实是自学。
直到前天java基础知识终于学完了,肯定有很多大佬觉得这速度有点慢。确实,我也觉得慢,因为6月初到7月初我都在准备期末考试,7月初到7月中旬,遇到了一件很不愉快的事情,也无心学习。不过后问题解决后,我又重新找回了学习的状态,继续肝java。
说起看jdk源代码的起因也是很搞笑,因为基础知识学完了,有点骄傲,又有点彷徨,不知道下一步该干啥,所以我就去洛谷刷题。但是我的算法真的学的太差了,一个简单的暴力搜索,愣是一个半小时没写出来,一直写到自闭,写到怀疑人生。所以,今天我决定阅读jdk的源代码,就从我常用的ArrayList集合开始吧。
二、成员变量(类的属性)
1、DEFAULT_CAPACITY
ArrayList的默认容量,该变量被final修饰,是私有不可变的。
private static final int DEFAULT_CAPACITY = 10;
2、EMPTY_ELEMENTDATA
用于空实例的共享数组。
private static final Object[] EMPTY_ELEMENTDATA;
3、DEFAULTCAPACITY_EMPTY_ELEMENTDATA
用于默认大小的空实列共享数组。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
4、elementData
用于存储ArrayList元素的数组缓冲区。(具体数据就存储在这个位置)
transient Object[] elementData;
5、size
ArrayList对象的有效数据长度。
private int size;
6、MAX_ARRAY_SIZE
要分配的数组的最大大小(除非必要)。
一些虚拟机在数组中保留一些标题字。
尝试分配更大的数组可能会导致
OutOfMemoryError:请求的数组大小超出了VM限制
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
三、构造函数
无参构造函数
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
无参构造函数就必须举一个例子:
ArrayList<Integer> list = new ArrayLIst<>();
以上是我们常用的创建ArrayList对象的语法,调用的就是无参构造方法。调用时,会将成员变量Object[ ] = DEFAULTCAPACITY_EMPTY_ELEMNTDATA, 赋值给this.elementData,也就是我们的数据存储区域。
有参构造方法
1.指定容量大小
算法过程:
判断initialCapaicity大小是否大于0,如果小于0就抛出异常,否则创建指定大小的数组并返回给this.elementData;
public ArrayList(int initialCapacity) {
/**
*如果列表的初始容量不为负,
*那么就将数组缓冲区elementData重新初始化大小为initialCapacity
*如果intialCapacity初始化为0,那么将用于空实例的共享空数组实例引用赋值给它。
*如果initialCapacity为负,那么抛出异常。
*/
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
2.指定元素列表的构造方法。
算法步骤:
1.获取到元素列表的数组副本。
2.如果大小等于等于0,就给elementData赋值空数组。
3.如果大小不等于0,继续判断。
4.如果数据不是对象数组,那么将其复制一份封装为对象数组(Object[ ])。
/**
* 构造一个包含指定Collection的元素的列表,这些元素是按照
* 该collection的迭代器返回他们的顺序排列
* @param c 将其元素放入此列表的集合
* @throws NullPointerException 如果指定的集合为null
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
四、成员方法
1.trimTosize()
public void trimTosize();
如果实际数组的长度小于给定数组的空间,就开始判断,
如果数组的长度 == 0, 那么返回共享的空数组实例,
否则使用Arrays.copyOf方法返回一个新的且大小刚好为数组元素数量的数组。
/**
* 将此{@code ArrayList}实例的容量修改为
* 列表的当前大小。应用程序可以使用此操作来最小化
* 存储{@code ArrayList}实例。
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
2.ensureCapacity()
public void ensureCapacity();
如果所设置的最小容量大于当前数组的长度,
且数组不为空,
且所设置的最小容量大于默认值10的话,
就增长数组的长度的容量。
/**
* 如果有必要,增加此ArrayList实列的容量,
* 以确保它至少能够容纳最小参数所指定的元素数量。
* 保证容量
* @param minCapacity //所需的最小容量
*/
public void ensureCapacity(int minCapacity) {
/**
* 如果所设置的最小容量大于当前数组的长度,
* 且数组不为空,
* 且所设置的最小容量大于默认值10的话,
* 就增长数组的长度的容量。
*/
if (minCapacity > elementData.length &&
!(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA /*空数组 */
&& minCapacity <= DEFAULT_CAPACITY/*10*/)) {
modCount++;
grow(minCapacity);
}
}
3.grow(int minCapacity)
增长数组的长度。
调用newCapacity(minCapacity);函数来计算需要增长的长度。
/**
* 要分配的数组的最大大小(除非必要)。
* 一些虚拟机在数组中保留一些标题字。
* 尝试分配更大的数组可能会导致
* OutOfMemoryError:请求的数组大小超出了VM限制
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 增加容量以确保它至少可以容纳
* 最小容量参数指定的元素数。
* @param minCapacity 所需的最小容量
* @throws OutOfMemoryError 如果minCapacity小于零抛出异常
*/
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
4.grow( )
调用grow(minCapacity)函数来增长长度。
private Object[] grow() {
return grow(size + 1);
}
5.newCapacity(int minCapacity)
/**
* 返回至少等于给定最小容量的容量。
* 如果足够则多增加,则返回增多50%的当前容量。
*
* 步骤:
* 1.先获取到当前数组的有效容量。
* 2.计算出新的数组容量。
* 3.如果新的数组容量 < 保证容量,那么返回保证容量。
* 但是在此之前,需要先判断当前数组的安全性,
* 如果是空数组,就返回max(默认容量,保证容量)
* 如果保证容量 < 0,那么抛出异常
* 4.如果新的数组容量大于保证容量,且小于MAX_ARRAY_SIZE,
* 那么返回新的数组容量,否则返回超大容量。
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private int newCapacity(int minCapacity) {
// overflow-conscious code
//获取原来数组的长度
int oldCapacity = elementData.length;
//计算新的数组的长度,是原来数组长度的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果计算出来的新的数组长度 < 保证容量
if (newCapacity - minCapacity <= 0) {
//如果当前数据缓冲区是空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
//返回默认容量和保证容量的最大值
return Math.max(DEFAULT_CAPACITY, minCapacity);
//如果保证容量的最小值小于0,那么抛出异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
6.hugeCapacity(int minCapacity)
public static int hugeCapaCity(int minCapacity);
用于处理超大容量的函数,被newCapacity(int minCapacity)调用。
/**
* 用于计算数组的超大容量
* 触发前提:
* 计算出来的新的容量 > MAX_ARRAY_SIZE,就使用保证容量来扩充获取超大容量
*/
private static int hugeCapacity(int minCapacity) {
//如果保证容量小于0,抛出异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果保证容量大于 MAX_ARRAY_SIZE,就返回Interger.MAX_VALUE
//否则返回数组最大容量
return (minCapacity > MAX_ARRAY_SIZE)
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
7.size()
public int size();
获取ArrayList对象的长度
/**
*返回此列表中的元素数
*
* @return 返回此列表中的元素数
*/
public int size() {
return size;
}
8.isEmpty()
public boolean isEmpty();
获取数组是否为空。
通过判断数组长度是否为0;
/**
*如果此列表不包含任何元素,则返回{@code true}。
*
* @return 如果此列表不包含任何元素,则返回{@code true}。
*/
public boolean isEmpty() {
return size == 0;
}
9.contains(Object o)
public boolean contains(Object o);
判断指定元素时候在列表中。
调用的是indexOf(Object o);
/**
*
* 如果此列表包含指定的元素,则返回{@code true}。
* 更正式地说,仅当此列表包含以下内容时,返回{@code true}
* 至少一个元素{@code e}这样
* {@code Objects.equals(o,e)}。
* @param o 要检查其在此列表中是否存在的元素
* @return{@code true}如果此列表包含指定的元素
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
10.indexOf(Object o)
public int indexOf(Object o);
获取指定元素的下标值。
如果指定元素不存在,那么返回-1;
/**
* 返回指定元素首次出现的索引
* 在此列表中;如果此列表不包含元素,则为-1。
* 更正式地,返回最低索引{@code i},这样
* {@code Objects.equals(o,get(i))},
* 或-1(如果没有这样的索引)。
*/
public int indexOf(Object o) {
/**
* 如果传入数据位空指针,那么将找到第一个为空的位置返回,
* 如果不为空,那么找到指定元素的位置返回,
* 如果列表中不包含该对象那么返回-1。
*/
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;
}
11.lastIndexOf( Object o)
public int lastIndexOf(Object o);
返回指定元素最后一次出现的索引值。
/**
* 返回指定元素最后一次出现的索引
* 在此列表中;如果此列表不包含元素,则为-1。
* 更正式地,返回最高索引{@code i},这样
* {@code Objects.equals(o,get(i))},
* 或-1(如果没有这样的索引)。
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
12.clone()
public Object clone();
/**
*返回此{@code ArrayList}实例的浅表副本。 (
*元素本身不会被复制。)
*
*@返回此{@code ArrayList}实例的副本
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
13.toArray()
public Object[] toArray()
返回一个包含此对象中所有元素的数组。
/**
* 返回一个包含此列表中所有元素的数组
* 按正确的顺序(从第一个元素到最后一个元素)。
*
* <p>返回的数组将是“安全的”,因为没有对其的引用
* 由该列表维护。 (换句话说,此方法必须分配
* 一个新数组)。因此,调用者可以自由修改返回的数组。
*
* <p>此方法充当基于数组和基于集合的桥梁
* API。
*
* @返回包含此列表中所有元素的数组
* 正确的顺序
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

1120

被折叠的 条评论
为什么被折叠?



