1ArrayList源码
ArrayList 是最常用的 List 实现类,内部是通过Object数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔, 当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。 当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
1.1 继承结构和层次关系
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList的继承结构:
ArrayList extends AbstractList
AbstractList extends AbstractCollection
所有类都继承Object 。
分析:
1)为什么要先继承AbstractList,而让AbstractList先实现List<E>
?而不是让ArrayList直接实现List<E>
?
这里是有一个思想,接口中全都是抽象的方法,而抽象类中可以有抽象方法,还可以有具体的实现方法,正是利用了这一点,让AbstractList是实现接口中一些通用的方法,而具体的类,如ArrayList就继承这个AbstractList类,拿到一些通用的方法,然后自己在实现一些自己特有的方法,这样一来,让代码更简洁,就继承结构最底层的类中通用的方法都抽取出来,先一起实现了,减少重复代码。所以一般看到一个类上面还有一个抽象类,应该就是这个作用。
2)ArrayList实现了哪些接口?
List<E>
接口:我们会出现这样一个疑问,在查看了ArrayList的父类AbstractList也实现了List<E>
接口,那为什么子类ArrayList还是去实现一遍呢?
这是想不通的地方,所以我就去查资料,有的人说是为了查看代码方便,使观看者一目了然,说法不一,但每一个让我感觉合理的,但是在stackOverFlow中找到了答案,这里其实很有趣。 可以参考这里,开发这个collection 的作者Josh说。这其实是一个mistake,因为他写这代码的时候觉得这个会有用处,但是其实并没什么用,但因为没什么影响,就一直留到了现在。
RandomAccess接口:这个是一个标记性接口,通过查看api文档,它的作用就是用来快速随机存取,有关效率的问题,在实现了该接口的话,那么使用普通的for循环来遍历,性能更高,例如arrayList。而没有实现该接口的话,使用Iterator来迭代,这样性能更高,例如linkedList。所以这个标记性只是为了让我们知道我们用什么样的方式去获取数据性能更好。
Cloneable接口:实现了该接口,就可以使用Object.Clone()方法了。
Serializable接口(标记性接口):实现该序列化接口,表明该类可以被序列化。
1.2类的属性
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 = {};
//它与EMPTY_ELEMENTDATA的区别就是:该数组是默认返回的,而后者是在用户指定容量为0时返回
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
保存元素的数组,ArrayList的容量就是该数组的长度
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* 当第一个元素被添加是其容量会扩展到DEFAULT_CAPACITY
*/
transient Object[] elementData; // non-private to simplify nested class access
//ArrayList包含元素的数量
private int size;
}
1.3构造方法
/*
*构造一个初始容量为10的空list
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/*
*构造一个具有指定初始容量的空list
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/*
*构造一个包含指定集合元素的list,这些元素是按照该collection 的迭代器的顺序返回。
*@param c 其元素将放置在此列表中的 collection
*@throws NullPointerException 如果指定的 collection 为 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.4 主要方法
1.1.4 add(E e)方法
//将指定的元素追加到此列表的末尾
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
//此辅助方法从add(E)中分离出来,以使方法的字节码大小保持在35以下
private void add(E e, Object[] elementData, int s) {
/*如果该elementData数组的长度与elementData里面元素个数相等
*说明数组要扩容了,就进行数组扩容函数grow(),如果不是则将数组s*位置存放该元素,同时元素数量加1
*/
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
//增加容量以确保它至少可以容纳最小容量参数指定的元素数量
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果扩容前数组容量大于0,或者elementData不为默认的空elementData
//新的容量由下面函数确定,然后复制扩容前数组里的元素到新数组,如果超过原来的数组长度即oldCapacity,超过的部分赋值为null
//不满足条件elementData为新创建的长度为max(10,指定最小容量)的Object数组
int newCapacity = ArraysSupport.newLength(oldCapacity,
// minimum growth 原数组长度所需最小增长值,这里就等于1
minCapacity - oldCapacity,
//preferred growth 比较理想的数组扩容长度 这里为原数组长度的一半
oldCapacity >> 1
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
/*
*这个函数就是给原数组计算一个新的数组长度
*@param oldLength 原数组长度
*@param minGrowth 原数组长度所需最小增长值 ,上一个函数传过来的该值等于1,就扩容一个数组位置
* @param prefGrowth 比较理想的数组扩容长度
*/
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
if (newLength - MAX_ARRAY_LENGTH <= 0) {
return newLength;
}
return hugeLength(oldLength, minGrowth);
}
private static int hugeLength(int oldLength, int minGrowth) {
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//jvm中,数组对象的头上是有一些信息的,这些信息需要占用一定的空间,所以在java中,新建数组是不可以指定其大小为Integer.MAX_VALUE
int minLength = oldLength + minGrowth;
if (minLength < 0) { // overflow
throw new OutOfMemoryError("Required array length too large");
}
if (minLength <= MAX_ARRAY_LENGTH) {
return MAX_ARRAY_LENGTH;
}
return Integer.MAX_VALUE;
}
1.1.5 add(int index, E element)方法
/*
*将指定的元素插入此列表中的指定位置。 将当前在该位置的元素(如果*有)和任何后续元素右移(将其索引加一)。
*/
public void add(int index, E element) {
//索引检测
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
//当该数组满了的时候,首先要扩容
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
//将elementData的index至index+(s-index-1)的数据拷贝到
//elementData的index+1至(index+1)+(s-index-1)的位置上
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
//再将eleent填充到index上
elementData[index] = element
//数组元素个数加1
size = s + 1;
}
```java
//A version of rangeCheck used by add and addAll.
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
总结:add(E e),如果容量够就在列表尾部直接添加元素,如果不够就扩容了再添加。
add(int index,E element),如果数组元素个数等于数组长度,
就先扩容,否则不用扩容,然后将elementData的index至index+(s-index-1)的数据拷贝到elementData的index+1至(index+1)+(s-index-1)的位置上,这里s等于数组元素个数,最后将数组元素个数加1.
扩容:理想值:原数组容量的一半,最小扩容值:1;
newLength = Math.max(minGrowth, prefGrowth) + oldLength;
然后将原数组元素拷贝到新数组,超过原数组长度部分用null填充。
1.1.6 ensureCapacity(int minCapacity)方法
//如有必要,增加此{@code ArrayList}实例的容量,以确保它至少可以容纳最小容量参数指定的元素数。
public void ensureCapacity(int minCapacity) {
//如果最小容量大于数组长度且(数组不为默认空或最小容量大于默认容量10)的时候,进行数组扩容
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
我的是JDK13,这里add方法已经不调用ensureCapacity方法了。
1.1.7 addAll(Collection<? extends E> c)方法
//将指定集合中的所有元素按指定集合Iterator返回的顺序追加到此列表的末尾
public boolean addAll(Collection<? extends E> c) {
//将集合转换为数组
Object[] a = c.toArray();
modCount++;
//得到该集合元素个数
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
//如果elementData数组长度减去elementData数组里面元素个数,即数组剩余容量小于c集合元素个数,就扩容,扩容成最小容量为当前数组元素个数加上c集合元素个数
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
//再将a拷贝到elementData中
System.arraycopy(a, 0, elementData, s, numNew);
size = s + numNew;
return true;
}
//toArray()方法
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
1.1.8 addAll(int index, Collection<? extends E> c)
//和上面差不多,也是容量不够先扩容,再根据index插入
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
modCount++;
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
int numMoved = s - index;
if (numMoved > 0)
System.arraycopy(elementData, index,
elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size = s + numNew;
return true;
}
1.1.9 indexOf(Object o)和lastIndexOf(Object o)
//在数组中查找元素o第一次出现的索引
//分为o==null和o!=null,正序遍历数组查找
public int indexOf(Object o) {
return indexOfRange(o, 0, size);
}
int indexOfRange(Object o, int start, int end) {
Object[] es = elementData;
if (o == null) {
for (int i = start; i < end; i++) {
if (es[i] == null) {
return i;
}
}
} else {
for (int i = start; i < end; i++) {
if (o.equals(es[i])) {
return i;
}
}
}
return -1;
}
//在数组中查找元素o最后一次出现的索引
//分为o==null和o!=null,倒序序遍历数组查找
public int lastIndexOf(Object o) {
return lastIndexOfRange(o, 0, size);
}
int lastIndexOfRange(Object o, int start, int end) {
Object[] es = elementData;
if (o == null) {
for (int i = end - 1; i >= start; i--) {
if (es[i] == null) {
return i;
}
}
} else {
for (int i = end - 1; i >= start; i--) {
if (o.equals(es[i])) {
return i;
}
}
}
return -1;
}
//根据indexOf(o)的返回值判断数组是否存在元素o
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
1.1.10 get(int index)和set(int index, E element)方法
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
public E set(int index, E element) {
Objects.checkIndex(index, size);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
public static
int checkIndex(int index, int length) {
return Preconditions.checkIndex(index, length, null);
@HotSpotIntrinsicCandidate
public static <X extends RuntimeException>
int checkIndex(int index, int length,
BiFunction<String, List<Integer>, X> oobef) {
if (index < 0 || index >= length)
throw outOfBoundsCheckIndex(oobef, index, length);
return index;
}
}
1.1.11 remove(int index)方法
public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
1.1.12 remove(Object o)方法
//先找到该元素再删除
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}
1.1.13 equals(Object o)方法
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof List)) {
return false;
}
final int expectedModCount = modCount;
// ArrayList can be subclassed and given arbitrary behavior, but we can
// still deal with the common case where o is ArrayList precisely
boolean equal = (o.getClass() == ArrayList.class)
? equalsArrayList((ArrayList<?>) o)
: equalsRange((List<?>) o, 0, size);
checkForComodification(expectedModCount);
return equal;
}
boolean equalsRange(List<?> other, int from, int to) {
final Object[] es = elementData;
if (to > es.length) {
throw new ConcurrentModificationException();
}
var oit = other.iterator();
for (; from < to; from++) {
if (!oit.hasNext() || !Objects.equals(es[from], oit.next())) {
return false;
}
}
return !oit.hasNext();
}
private boolean equalsArrayList(ArrayList<?> other) {
final int otherModCount = other.modCount;
final int s = size;
boolean equal;
if (equal = (s == other.size)) {
final Object[] otherEs = other.elementData;
final Object[] es = elementData;
if (s > es.length || s > otherEs.length) {
throw new ConcurrentModificationException();
}
for (int i = 0; i < s; i++) {
if (!Objects.equals(es[i], otherEs[i])) {
equal = false;
break;
}
}
}
other.checkForComodification(otherModCount);
return equal;
}
private void checkForComodification(final int expectedModCount) {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
总结:boolean result = obj instanceof Class
其中 obj 为一个对象,Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。
equalsRange(List<?> other, int from, int to) 根据迭代器一个一个判断范围内两个数组元素是否相等。
equalsArrayList(ArrayList<?> other)遍历数组元素判断是否相等。两者最后都要判断它们的modCount
是否相等。
1.1.14 modCount
ArrayList在调用add和remove方法时,会让modCount++;
modCount就像一个标志位,我在遍历前取得modCount的值,命名为expectedModCount,我在遍历的过程中不断判断此时的modCount和我之前的modCount是否相等,如果不进行add ,remove操作肯定会是相等的,就继续遍历,但是在遍历过程中,其他线程对这个集合进行了add,remove,此时modCount就会改变,与之前的expectedModCount不相等,就会抛出异常。这就是线程不安全的ArrayList尽量来一些操作使线程安全。当大家遍历那些非线程安全的数据结构时,尽量使用迭代器。可以参考这篇文章。
/**
* The number of times this list has been <i>structurally modified</i>.
* Structural modifications are those that change the size of the
* list, or otherwise perturb it in such a fashion that iterations in
* progress may yield incorrect results.
**/
protected transient int modCount = 0;
private void checkForComodification(final int expectedModCount) {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
public ListIterator<E> listIterator(int index) {
rangeCheckForAdd(index);
return new ListItr(index);
}
那么还有其他方法使ArrayList在边修改边遍历变得安全吗?
/*
* 解决方案 1 使用Vector Vector add方法加了synchronized 线程安全 而ArrayList没加 线程不安全
* 2 List<String> list=Collections.synchronizedList(new ArrayList<>());
* 3 List<String> list=new CopyOnWriteArrayList<>();
*/
public class NotSafeDemo {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
//List<String> list=new Vector<>();
//List<String> list=new CopyOnWriteArrayList<>();
//List<String> list=Collections.synchronizedList(new ArrayList<>());
for(int i=0;i<3;i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
目前想到有这三种方法。Vector支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
参考:https://www.cnblogs.com/zhangyinhua/p/7687377.html
https://blog.csdn.net/panweiwei1994/article/details/76760238