目录:
一:总结
0、继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。
1、可以存放null,可以重复(继承 list的原因)
2、可以随机访问 (继承 RandomAccess// get(index))
3、可以使用clone 因为继承 Cloneable接口
4、可以被序列化的 因为继承 Serializable
5、底层数据结构:数组
6、初始容量:10
7、扩容方式:1.5倍扩容,grow方法
8、求交集 并集 差集 addAll removeAll retainAll
9、ArrayList本质上就是A一个elementData数组。
10、增:尾插,随机插(先后移,然后插入)
删:数组的拷贝来实现
11、因为本质是数组,所以查询快,而在插入删除慢。
12、和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。使用iterator遍历可能会引发多线程异常
13、如果我们明确所插入元素的多少,最好指定一个初始容量值, 避免过多的进行扩容操作而浪费时间、效率。
二:源码分析
1、定义:
当定义创建一个新对象时,默认数组大小为10即:DEFAULTCAPACITY_EMPTY_ELEMENTDATA
当传入容量大小为0时返回EMPTY_ELEMENTDATA的空数组
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//序列号
private static final long serialVersionUID = 8683452581122892189L;
//默认容量
private static final int DEFAULT_CAPACITY = 10;
//一个空数组,当指定ArrayList 容量为 0 时,返回该空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//一个空数组实例,当没有指定 ArrayList 的容量时返回该数组,数据量为0
//当用户第一次添加元素时,数组将扩容,变成默认容量为10的一个数组ensureCapacityInternal() 实现
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//保存数据,长度为容量大小
transient Object[] elementData; // non-private to simplify nested class access
//ArrayList实际存储的数据数量
private int size;
//数组缓冲区最大存储容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2、构造方法
方法有三:
//一:带有int的初始容量大小的构造方法
public ArrayList(int initialCapacity) {
//初始容量大于0,创建指定大小对象数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
}
//等于0 返回 之前定义的 EMPTY_ELEMENTDATA
else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
}
//小于0,抛出异常
else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//二:无参构造方法,返回之前定义的DEFAULTCAPACITY_EMPTY_ELEMENTDATA初始为10的数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//三:类型构造器
public ArrayList(Collection<? extends E> c) {
//将集合转为数组
elementData = c.toArray();
//判断是否为空,为空则返回之前定义的 EMPTY_ELEMENTDATA,不为空将c长度赋值给当前 ArrayList 的 size
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
// 若 c.toArray() 返回的数组类型不是 Object[],则利用copyof方法创建一个Object[] 数组
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
3、方法
modeCount的说明:
*modCount 线程安全:其中保存了一个modCount属性 是为了线程安全。遍历的时候++ 使用的时候比较一下这俩一样不。
(1)、添加:add addAll
add():
尾插:为空初始化大小为10,否则1.5倍扩容
指定位置插入:先判断插入位置是否越界然后,为空初始化大小为10,否则1.5倍扩容
尾插:
//尾插
public boolean add(E e) {
//确定内部容量是否够用,size是数组中数据的个数,因为要添加一个元素,所以size+1,先判断size+1当前数组能否放下,放不下就扩容。
ensureCapacityInternal(size + 1); // Increments modCount!!
//执行尾插
elementData[size++] = e;
return true;
}
ensureCapacityInternal():判断当前数组是否为空,为空则初始化大小为10的数组
private void ensureCapacityInternal(int minCapacity) {
// 先判断当前数组是否是空的初始化数组,如果是空,就将minCapacity变成10,即初始化成长度为10的数组,也就是默认大小。
//这里就是判断如果为空则设置minCapacity为10,不为空进行下一步判断
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//判断当前数组是否够用
ensureExplicitCapacity(minCapacity);
}
ensureExplicitCapacity():判断是否要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果需要的长度 大于当前数组的长度就扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow()扩容方法 1.5倍扩容
private void grow(int minCapacity) {
//当前数组大小赋值给oldCapacity
int oldCapacity = elementData.length;
// 扩容为原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//这句话就是适应于elementData就空数组的时候,为空,则初始化大小为10
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 若 newCapacity 大于最大存储容量,就调用hugeCapacity,也就是将能给的最大值给newCapacity
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//进行数组扩容 调用Arrays.copyOf方法,其实就是system.arrayCopy方法。
elementData = Arrays.copyOf(elementData, newCapacity);
}
最大值赋值:
//赋最大值。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//Integer.MAX_VALUE:2147483647 MAX_ARRAY_SIZE:2147483639
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
指定位置插入:
//指定位置插入
public void add(int index, E element) {
//判断插入位置是否合法,否则抛出IndexOutOfBoundsException异常
rangeCheckForAdd(index);
//同上
ensureCapacityInternal(size + 1); // Increments modCount!!
//重新拷贝数组,在index位置插入。
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
addAll():循环调用add方法
public boolean addAll(Collection<? extends E> c) {
return addAll(this.size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
//越界检查
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;
checkForComodification();
parent.addAll(parentOffset + index, c);
this.modCount = parent.modCount;
this.size += cSize;
return true;
}
由于ArrayList 是线程不安全的,该方法没有加锁,所以当一个线程正在将 c 中的元素加入 list 中,但同时有另一个线程在更改 c 中的元素,就会抛出ConcurrentModificationException(并发修改异常),modeCount就是记录了当前数组是否正在被修改。
parent.addAll:是其实就是AbstractLis抽象接口,也就是循环调用add方法。
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
add(index++, e);
modified = true;
}
return modified;
}
(2)、删除
remove(int index):用后面的值覆盖从而删除
remove(Object):这个方法可以看出来,arrayList是可以存放null值得。
clear():每个元素置空
removeAll方法 (移除 list 中和 c 中共有的元素)
removeRange ( int fromIndex, int toIndex ) 方法 : 删除fromIndex到toIndex之间的全部元素左闭右开
1、remove:删除指定位置上的元素,覆盖删除
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);
//将--size上的位置赋值为null,让gc(垃圾回收机制)更快的回收它。
elementData[--size] = null; // clear to let GC do its work
//返回删除的元素。
return oldValue;
}
2、remove(Object):这个方法可以看出来,arrayList是可以存放null值得。
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;
}
3、 clear():每一个元素都置空
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
4、removeAll方法 (移除 list 中和 c 中共有的元素)
public boolean removeAll(Collection<?> c) {
// 当 c == null,则抛出NPE
Objects.requireNonNull(c);
//批量删除c
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
5、removeRange ( int fromIndex, int toIndex ) 方法 : 删除fromIndex到toIndex之间的全部元素
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
//需要移动元素的个数
int numMoved = size - toIndex;
//进行元素拷贝后,需要删除的几个元素就复制到了最后几个位置
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
// 删除后新的长度
int newSize = size - (toIndex-fromIndex);
// 将需要删除的元素(index在最后几个)置为 null
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
(3)、set()方法
public E set(int index, E element) {
// 检验索引是否合法
rangeCheck(index);
// 旧值
E oldValue = elementData(index);
// 赋新值
elementData[index] = element;
// 返回旧值
return oldValue;
}
(4)、indexOf
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;
}
// 没有找到,返回-1
return -1;
}
三:遍历
通过迭代器
查询遍历
迭代器:Iterator<Integer> all = arrayList.iterator();
while(all.hasNext()){
Integer value = all.next();
}