可以看到ArrayList 继承了 AbstractList
实现List,RandomAccess,Cloneable,序列化接口
怎么理解capacity和size
可以看到我们的初始容量为15
但是它的size为4
这就说明size指的是 实际存储了多少。
elementData: Object[]类型的动态数组
变量
// 序列化ID
private static final long serialVersionUID = 8683452581122892189L;
/**
* 初始容量 10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空的元素数据 且是Obejct数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 默认容量 空的数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* elementData 里面存放的 Object数组
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 实际大小
*/
private int size;
/**
* 数组最大 大小为 2^31-9
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造函数
/**
* 指定初始容量
*/
public ArrayList(int initialCapacity) {
// 指定初始容量 > 0
if (initialCapacity > 0) {
// 创建初始容量大小的元素
this.elementData = new Object[initialCapacity];
// 指定容量大小为0
} else if (initialCapacity == 0) {
// 空的数据
this.elementData = EMPTY_ELEMENTDATA;
// 否则抛出异常
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 无参构造器 数组中为空
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 1)将collection对象转换成数组,然后将数组的地址的赋给elementData。
* 2)更新size的值,同时判断size的大小,如果是size等于0,直接将空对象EMPTY_ELEMENTDATA的地址赋给elementData
* 3)如果size的值大于0,则执行Arrays.copy方法,把collection对象的内容(可以理解为深拷贝)copy到elementData中。
* 注意:this.elementData = arg0.toArray(); 这里执行的简单赋值时浅拷贝,所以要执行Arrays,copy 做深拷贝
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
前面一直对.length的概念有点混淆
特定做了一个测试:
发现length就是数组new出来的大小
如果在其中没有添加
那么Object类型里面 未添加元素的下表默认为null
很多方法的形参里面有minCapacity:
minCapacity:直译过来就是最小容量,即指我们添加元素后,或者自我指定容量的最小容量值。
扩容
/**
* trim实际大小:修整 实际大小
* ArrayList的容量调整为实际元素的大小
*/
public void trimToSize() {
// 记录List结构的修改次数 用于fast-fail
modCount++
//实际大小 < elementData数组长度
if (size < elementData.length) {
// 实际大小为0?
// 为0 定义为空的elementData数组
// 不为0 我们就定义size长的elementData数组
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
/**
* 方法名:确定容量
* 判断所需的ro
* Explicit : 明确的,清晰的
* minCapacity: 所需长度
* 总结:
* elementData数组里面为空 那么就执行ensureExplicitCapacity方法
* 里面不为空 但所需的长度大于默认容量10 执行ensureExplicitCapacity方法
*
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
/**
* 方法名:计算容量
* 数组默认为空 返回默认容量和我们所需容量中的最大值
* 数组不为null 返回所需容量
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
* 确定容量的内部
* internal: 内部的
*
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 确定扩容?
* explicit:明确的,清晰的
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 所需容量 > 数组的长度 扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 真正的扩容
*/
private void grow(int minCapacity) {
// 旧容量
int oldCapacity = elementData.length;
// 新容量 = 旧容量 +旧容量*0.5 = 旧容量*1.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 新容量小于所需容量
// 新容量 = 所需容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 新容量 》 最大数组大小
// 所需容量大于最大数组大小 新容量 = Integer的最大值
// 所需容量小于最大数组大小 新容量 = 最大数组大小
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);
}
/**
* huge:巨大的
*/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
添加元素
这个方法其实和上面的add类似,该方法可以按照元素的位置,指定位置插入元素,具体的执行逻辑如下:
1)确保数插入的位置小于等于当前数组长度,并且不小于0,否则抛出异常
2)确保数组已使用长度(size)加1之后足够存下 下一个数据
3)修改次数(modCount)标识自增1,如果当前数组已使用长度(size)加1后的大于当前的数组长度,则调用grow方法,增长数组
4)grow方法会将当前数组的长度变为原来容量的1.5倍。
5)确保有足够的容量之后,使用System.arraycopy 将需要插入的位置(index)后面的元素统统往后移动一位。
6)将新的数据内容存放到数组的指定位置(index)上
/**
* 确认元素内部容量大小 所需容量为size+1
* e 赋值给 数组后一个下标
* 返回true值
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
*
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
ArrayList总结
底层数组实现,使用默认构造方法初始化出来的容量是10
扩容的长度是原长度的1.5倍
实现了RandomAccess接口,底层又是数组,get读取元素性能很好
线程不安全,所有的方法均不是同步方法也没有加锁,因此多线程下慎用
顺序添加很方便
删除和插入需要复制数组 性能很差(可以使用LinkindList)
为什么ArrayList的elementData是用transient修饰的?
transient修饰的属性意味着不会被序列化,也就是说在序列化ArrayList的时候,不序列化elementData。
为什么要这么做呢?
elementData不总是满的,每次都序列化,会浪费时间和空间
重写了writeObject 保证序列化的时候虽然不序列化全部 但是有的元素都序列化
Int和Integer的区别
Integer是一个包装类,而int是一个基础数据类型
在Integer有最大值和最小值
// 二进制结果为0
@Native public static final int MIN_VALUE = 0x80000000;
// 二进制结果为2147483647 2^31 -1
@Native public static final int MAX_VALUE = 0x7fffffff;
==参考 原文:https://blog.csdn.net/fighterandknight/article/details/61240861 ==