ArrayList类的申明
ArrayList是一个支持泛型的,底层通过数组实现的一个可以存任意类型的数据结构,源码中的定义如下:
复制代码
1 public class ArrayList extends AbstractList
2 implements List, RandomAccess, Cloneable, java.io.Serializable
3 {}
复制代码
ArrayList类继承了AbstractList抽象类,AbstractList提供了List接口的默认实现
ArrayList实现了以下几个接口:
List接口:约定List的操作规范,提供了一系列操作方法约定
RandomAccess接口:该接口约定其实现类支持随机访问,即可以通过下标的方式访问其中的元素
Cloneable接口:约定其实现类实例是可以被克隆的,通过调用Object.clone方法返回该对象的浅拷贝
Serializable接口:约定其实现类实例可以被序列化和反序列化
ArrayList主要字段、属性说明
复制代码
// 版本号
private static final long serialVersionUID = 8683452581122892189L;
// 缺省容量
private static final int DEFAULT_CAPACITY = 10;
// 空对象数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 缺省空对象数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 元素数组
transient Object[] elementData;
// 实际元素大小,默认为0
private int size;
// 最大数组容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
复制代码
其中有个重要的属性elementData其作用是存放集合元素,这说明了ArrayList内部其实是通过数组实现的。其修饰符transient 表明这个字字段在序列化时被忽略不序列化。
ArrayList部分方法分析
构造函数
无参构造函数:初始化一个长度为0的空数组
复制代码
1 public ArrayList() {
2 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
3 }
4
5 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
复制代码
ArrayList(int) 构造函数:初始化一个指定长度的数组
复制代码
1 public ArrayList(int initialCapacity) {
2 if (initialCapacity > 0) {
3 //初始化一个容量为initialCapacity的数组
4 this.elementData = new Object[initialCapacity];
5 } else if (initialCapacity == 0) {
6 //初始化空数组
7 this.elementData = EMPTY_ELEMENTDATA;
8 } else {
9 //如果尝试初始化一个容量小于0的数组,则直接抛异常
10 throw new IllegalArgumentException("Illegal Capacity: "+
11 initialCapacity);
12 }
13 }
复制代码
ArrayList(Collection extends E>)构造函数:初始化化一个数据,并将参数集合中的元素复制到数组中
复制代码
1 public ArrayList(Collection extends E> c) {
2 //将参数集合转化为数组,赋值到ArrayList内部存储属性上
3 elementData = c.toArray();
4 if ((size = elementData.length) != 0) {
5 // c.toArray might (incorrectly) not return Object[] (see 6260652)
6 //如果elementData数组类型不是Object[],则重新将elementData中元素转为Object复制到elementData中
7 if (elementData.getClass() != Object[].class)
8 elementData = Arrays.copyOf(elementData, size, Object[].class);
9 } else {
10 //为空则返回空数组
11 this.elementData = EMPTY_ELEMENTDATA;
12 }
13 }
复制代码
上面的代码中为什么要再次判断?Collection类本身的toArray方法是返回Object[]类型数组,但是Java中如果子类如果继承Collection并重写了toArray方法,则返回的可能并不是Object[]类型数值,比如String[]等其他类型
Add(E e)、add(int index, E element)、addAll(Collection extends E> c) 、addAll(int index, Collection extends E> c)
ArrayList提供了这两个add操作方法,Add(E e)直接向素组末尾添加元素,add(int index, E element)向指定index索引处添加元素
复制代码
1 //直接向素组末尾添加元素
2 public boolean add(E e) {
3 //判断数组容量是否还可以添加,不够添加则扩充数组容量
4 ensureCapacityInternal(size + 1); // Increments modCount!!
5 //将元素添加到数组末尾
6 elementData[size++] = e;
7 return true;
8 }
9
10 private void ensureCapacityInternal(int minCapacity) {
11 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
12 }
13
14
15 private void ensureExplicitCapacity(int minCapacity) {
16 //用于迭代器
17 modCount++;
18
19 //期望的最小数组容量大于当前数组容量,则扩容
20 if (minCapacity - elementData.length > 0)
21 grow(minCapacity);
22 }
23
24 //计算期望最小的素组容量
25 private static int calculateCapacity(Object[] elementData, int minCapacity) {
26 // DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组
27 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
28 // DEFAULT_CAPACITY=10,也就是说如果此时最小返回一个长度为10的数组
29 return Math.max(DEFAULT_CAPACITY, minCapacity);
30 }
31 return minCapacity;
32 }
33
34 //扩容
35 private void grow(int minCapacity) {
36 //当前数组容量
37 int oldCapacity = elementData.length;
38 //计算新素组容量,为当前数组容量的1.5倍
39 int newCapacity = oldCapacity + (oldCapacity >> 1);
40 //判断新数组容量与期望数组容量大小,取值大的一方
41 if (newCapacity - minCapacity < 0)
42 newCapacity = minCapacity;
43 // MAX_ARRAY_SIZE =Integer.MAX_VALUE – 8= 2147483639
44 //如果新数组容量大于2147483639,则使用扩展到最大Integer.MAX_VALUE
45 if (newCapacity - MAX_ARRAY_SIZE > 0)
46 newCapacity = hugeCapacity(minCapacity);
47 //将原数组中的元素拷贝到新素组中
48 elementData = Arrays.copyOf(elementData, newCapacity);
49 }
复制代码
上面的代码注释已经写的很清楚了,add方法的逻辑是首选检查数组容量是否够用,如果容量不足,则进行扩容,扩容策略是如果原素组为空,则返回一个长度为10的数组,否则数组容量扩充到原素组的1.5倍,最终数组容量最大为Integer.Max_VALUE=2147483647
复制代码
1 //向指定索引处添加元素
2 public void add(int index, E element) {
3 //检查指定索引合法性
4 rangeCheckForAdd(index);
5
6 ensureCapacityInternal(size + 1); // Increments modCount!!
7 System.arraycopy(elementData, index, elementData, index + 1,
8 size - index);
9 elementData[index] = element;
10 size++;
11 }
复制代码
add(int index, E element)内部多了一个验证指定索引合法性逻辑,其他与add(E element)实现逻辑基本一致。
复制代码
1 //添加集合
2 public boolean addAll(Collection extends E> c) {
3 Object[] a = c.toArray();
4 int numNew = a.length;
5 //检查是否需要扩容
6 ensureCapacityInternal(size + numNew); // Increments modCount
7 System.arraycopy(a, 0, elementData, size, numNew);
8 size += numNew;
9 return numNew != 0;
10 }
11 public boolean addAll(int index, Collection extends E> c) {
12 rangeCheckForAdd(index);
13
14 Object[] a = c.toArray();
15 int numNew = a.length;
16 ensureCapacityInternal(size + numNew); // Increments modCount
17
18 int numMoved = size - index;
19 if (numMoved > 0)
20 System.arraycopy(elementData, index, elementData, index + numNew,
21 numMoved);
22
23 System.arraycopy(a, 0, elementData, index, numNew);
24 size += numNew;
25 return numNew != 0;
26 }
复制代码
addAll方法实现逻辑与add方法基本相同
get(int index)
get方法返回此列表中指定位置的元素,内部实现首先判断一下索引是否越界(居然没有判断小于0,实际上小于0时,数组读取也会抛异常),然后取出对应索引位置处的元素,另外由于ArrayList内部是用Object[]实现存储的,get(int index)返回泛型E,实际上elementData(index)内部实现将Object转为E
复制代码
1 public E get(int index) {
2 //验证索引是否越界
3 rangeCheck(index);
4
5 return elementData(index);
6 }
7
8 private void rangeCheck(int index) {
9 if (index >= size)
10 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
11 }
复制代码
set(int index,E element)
set方法的功能是将指定索引处的元素修改为element值,并返回该位置原来的值
复制代码
public E set(int index, E element) {
//验证索引合法性
rangeCheck(index);
//读取原来的值
E oldValue = elementData(index);
//替换为目标值
elementData[index] = element;
return oldValue;
}
复制代码
remove(int index)、remove(Object o)、removeAll(Collection> c)、removeIf(Predicate super E> filter)、removeRange(int fromIndex, int toIndex)
ArrayList提供了一系列删除元素的方法,下面分析一个基础的remove(int index):
复制代码
1 //删除指定索引处的元素
2 public E remove(int index) {
3 //校验索引合法性
4 rangeCheck(index);
5
6 //删除操作影响数组列表结构,所以modCount自增1
7 modCount++;
8 //读取将要删除的元素
9 E oldValue = elementData(index);
10 //需要被移动的元素起始位置(该删除元素后面的元素都需要移动)
11 int numMoved = size - index - 1;
12 //存在移动的元素,则所有元素都往前移动一个位置
13 if (numMoved > 0)
14 System.arraycopy(elementData, index+1, elementData, index,
15 numMoved);
16 //由于所有元素都向前移动了,最后一个空出来的位置设置为null
17 elementData[--size] = null; // clear to let GC do its work
18 //返回被删除的元素
19 return oldValue;
20 }
复制代码
IndexOf(Object o) 、lastIndexOf(Object o)
如果我们需要检查列表中某个元素的位置,则可以使用indexOf方法,此方法返回被检查元素在列表中第一次出现的下标,如果未找到该元素,则返回-1
复制代码
1 //查找指定元素的索引
2 public int indexOf(Object o) {
3 //顺序遍历数组,返回第一个出现位置的下标
4 if (o == null) {
5 for (int i = 0; i < size; i++)
6 if (elementData[i]==null)
7 return i;
8 } else {
9 for (int i = 0; i < size; i++)
10 if (o.equals(elementData[i]))
11 return i;
12 }
13 //不存在返回-1
14 return -1;
15 }
复制代码
lastIndexOf(Object o)返回指定元素在数组中最后一次出现的下标
iterator()、listIterator()、listIterator(int index)
iterator()方法: 返回一个ArrayList中元素的迭代器,实现代码如下:
复制代码
1 public Iterator iterator() {
2 return new Itr();
3 }
4
5 private class Itr implements Iterator {
6 //下一个要返回元素的索引
7 int cursor; // index of next element to return
8 //最后一个返回元素的索引
9 int lastRet = -1; // index of last element returned; -1 if no such
10 int expectedModCount = modCount;
11
12 Itr() {}
13
14 //判断是否还存在下一个元素
15 public boolean hasNext() {
16 return cursor != size;
17 }
18
19 @SuppressWarnings("unchecked")
20 public E next() {
21 //校验,在迭代器进行元素遍历期间如果修改数组长度,则抛出异常
22 checkForComodification();
23 int i = cursor;
24 if (i >= size)
25 throw new NoSuchElementException();
26 Object[] elementData = ArrayList.this.elementData;
27 if (i >= elementData.length)
28 throw new ConcurrentModificationException();
29 //指向下一个元素
30 cursor = i + 1;
31 //返回索引值为i处的元素,并将i赋值给lastRet:代表最后返回元素的索引
32 return (E) elementData[lastRet = i];
33 }
34
35 //通过迭代器删除元素,不会抛异常
36 public void remove() {
37 if (lastRet < 0)
38 throw new IllegalStateException();
39 checkForComodification();
40
41 try {
42 ArrayList.this.remove(lastRet);
43 cursor = lastRet;
44 lastRet = -1;
45 expectedModCount = modCount;
46 } catch (IndexOutOfBoundsException ex) {
47 throw new ConcurrentModificationException();
48 }
49 }
50
51 @Override
52 @SuppressWarnings("unchecked")
53 public void forEachRemaining(Consumer super E> consumer) {
54 Objects.requireNonNull(consumer);
55 final int size = ArrayList.this.size;
56 int i = cursor;
57 if (i >= size) {
58 return;
59 }
60 final Object[] elementData = ArrayList.this.elementData;
61 if (i >= elementData.length) {
62 throw new ConcurrentModificationException();
63 }
64 while (i != size && modCount == expectedModCount) {
65 consumer.accept((E) elementData[i++]);
66 }
67 // update once at end of iteration to reduce heap write traffic
68 cursor = i;
69 lastRet = i - 1;
70 checkForComodification();
71 }
72
73 final void checkForComodification() {
74 if (modCount != expectedModCount)
75 throw new ConcurrentModificationException();
76 }
77 }
复制代码
迭代器的实际应用:
1. 使用迭代器iterator遍历:
复制代码
1 Iterator list= array.iterator();
2 while(list.hasNext()){
3 //array.add(4); add() 和remove()会导致modCount发生变化,从而导致迭代过程中抛出异常
4 int value = it.next();
5 //使用迭代器提供的remove()方法避免抛异常,原因:迭代器的remove方法在删除元素之后对将ArrayList的modCount覆盖了迭代器类的expectedModCount
6 it.remove();
7
8 }
复制代码
2.使用forEach遍历:反编译class文件可以发现其本质还是使用了iterator迭代器
复制代码
for(Integer item : array){
//item.add()和item.remove()都将报错
System.out.println(value);
}
复制代码
listIterator()方法:返回返回ArrayList元素的列表迭代器,与Iterator迭代器相比,它还提供了向前遍历,增加元素,修改元素的操作,其实现代码如下
复制代码
1 public ListIterator listIterator() {
2 return new ListItr(0);
3 }
4
5 private class ListItr extends Itr implements ListIterator {
6 ListItr(int index) {
7 super();
8 //初始化游标
9 cursor = index;
10 }
11
12 //判断是否存在上 一个元素
13 public boolean hasPrevious() {
14 return cursor != 0;
15 }
16
17 //返回下一个将遍历的元素索引
18 public int nextIndex() {
19 return cursor;
20 }
21
22 //返回前一个元素索引
23 public int previousIndex() {
24 return cursor - 1;
25 }
26
27 //向前遍历:返回当前索引的上一个元素
28 @SuppressWarnings("unchecked")
29 public E previous() {
30 //校验,在迭代器进行元素遍历期间如果修改数组长度,则抛出异常
31 checkForComodification();
32 //计算前一个元素索引
33 int i = cursor - 1;
34 if (i < 0)
35 throw new NoSuchElementException();
36 Object[] elementData = ArrayList.this.elementData;
37 if (i >= elementData.length)
38 throw new ConcurrentModificationException();
39 //游标指向前一个元素
40 cursor = i;
41 //返回前一个元素
42 return (E) elementData[lastRet = i];
43 }
44
45 //修改当前位置的元素
46 public void set(E e) {
47 if (lastRet < 0)
48 throw new IllegalStateException();
49 checkForComodification();
50
51 try {
52 ArrayList.this.set(lastRet, e);
53 } catch (IndexOutOfBoundsException ex) {
54 throw new ConcurrentModificationException();
55 }
56 }
57 //当前位置新增元素
58 public void add(E e) {
59 checkForComodification();
60
61 try {
62 int i = cursor;
63 ArrayList.this.add(i, e);
64 cursor = i + 1;
65 lastRet = -1;
66 expectedModCount = modCount;
67 } catch (IndexOutOfBoundsException ex) {
68 throw new ConcurrentModificationException();
69 }
70 }
71 }
复制代码
最后:ArrayList其实就是一个动态的Array,并且提供了一些便携的操作方法而已。
https://www.cnblogs.com/ashleyboy/p/9573340.html