ArrayList源码分析

ArrayList底层由数组实现,并且可以动态扩容。

而数组在内存中分配的是一块连续的空间,直接根据数组的起始位置+偏移量就可以查询到,这使得查询效率高,所以ArrayList实现了 RandomAccess 接口来支持数据的随机访问。

除此之外,ArrayList还实现了 Cloneable 和 Serializable 接口,使其可以被克隆、序列化。

ArrayList 中的成员属性

1     private static final int DEFAULT_CAPACITY = 10;   
2 
3     private static final Object[] EMPTY_ELEMENTDATA = {};
4 
5     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
6 
7     transient Object[] elementData; 
8 
9     private int size;

DEFAULT_CAPACITY 表示默认容量大小

EMPTY_ELEMENTDATA 表示空数组  -->   当构造函数中的参数为0时,elementData = EMPTY_ELEMENTDATA   ==>   new ArrayList(0);

DEFAULTCAPACITY_EMPTY_ELEMENTDATA 表示默认数组  -->   当使用无参构造器时,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA    ==>   new ArrayList();

elementData 表示创建的Arraylist的底层数组,用来存放元素

size 表示数组中的元素个数,不等于数组长度(比如:默认的数组长度为10,但你可能只添加了四个元素,所以size为4)

 

ArrayList 中的构造器

 1 public ArrayList(int initialCapacity) {
 2         if (initialCapacity > 0) {
 3             this.elementData = new Object[initialCapacity];
 4         } else if (initialCapacity == 0) {
 5             this.elementData = EMPTY_ELEMENTDATA;
 6         } else {
 7             throw new IllegalArgumentException("Illegal Capacity: "+
 8                                                initialCapacity);
 9         }
10     }

这个构造器传入一个整型参数,如果等于0,则使用 EMPTY_ELEMENTDATA 空数组

如果参数大于0,则创建一个相应 容量 的数组。

 

1 public ArrayList() {
2         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
3     }

无参构造器,使用默认容量的数组,容量大小为10

 

1 public ArrayList(Collection<? extends E> c) {
2         elementData = c.toArray();
3         if ((size = elementData.length) != 0) {
4             if (elementData.getClass() != Object[].class)
5                 elementData = Arrays.copyOf(elementData, size, Object[].class);
6         } else {
7              this.elementData = EMPTY_ELEMENTDATA;
8         }
9     }

有参构造器,参数是一个集合。

1.将参数转换为数组

2.判断该数组的长度,如果为0,则使用空数组;

  否则,进一步判断该数组是否时 Object[] 类型,如果不是就将其转换为 Object[] 类型

 

数组扩容

1 private void grow(int minCapacity) {
2         int oldCapacity = elementData.length;
3         int newCapacity = oldCapacity + (oldCapacity >> 1);
4         if (newCapacity - minCapacity < 0)
5             newCapacity = minCapacity;
6         if (newCapacity - MAX_ARRAY_SIZE > 0)
7             newCapacity = hugeCapacity(minCapacity);
8         elementData = Arrays.copyOf(elementData, newCapacity);
9     }

假设我们使用 new ArrayList() 创建了一个容量为10的数组,然后往里面加入了10个元素,

此时,我们准备加入第11个元素,那么 elementData 数组就需要进行扩容了。

1.获取数组当前的容量

2.计算出数组当前容量扩容后的 假设容量(为当前容量的1.5倍)

3.如果假设容量不能满足扩容需求,则以需要的容量为准

 

添加元素

1 public boolean add(E e) {
2         ensureCapacityInternal(size + 1);
3         elementData[size++] = e;
4         return true;
5     }

在末尾添加一个元素

ensureCapacityInternal(size + 1); 检查是否需要扩容,需要扩容就进行扩容

 

1 public void add(int index, E element) {
2         rangeCheckForAdd(index);
3         ensureCapacityInternal(size + 1);
4         System.arraycopy(elementData, index, elementData, index + 1,
5                          size - index);
6         elementData[index] = element;
7         size++;
8     }

1.检查要插入的位置是否下标越界

2.判断是否要扩容

3.利用System.arraycopy()方法将 index 下标之后的元素往后移一位

 

1 public boolean addAll(Collection<? extends E> c) {
2         Object[] a = c.toArray();
3         int numNew = a.length;
4         ensureCapacityInternal(size + numNew);
5         System.arraycopy(a, 0, elementData, size, numNew);
6         size += numNew;
7         return numNew != 0;
8     }

在末尾添加一个集合

1.将集合 c 转换为数组

2.判断  (当前元素个数+要添加的元素个数)   是否需要扩容

3.利用 System.arraycopy() 方法将所有的元素添加到末尾

 

 1 public boolean addAll(int index, Collection<? extends E> c) {
 2         rangeCheckForAdd(index);
 3         Object[] a = c.toArray();
 4         int numNew = a.length;
 5         ensureCapacityInternal(size + numNew);
 6         int numMoved = size - index;
 7         if (numMoved > 0)
 8             System.arraycopy(elementData, index, elementData, index + numNew,
 9                              numMoved);
10         System.arraycopy(a, 0, elementData, index, numNew);
11         size += numNew;
12         return numNew != 0;
13     }

在指定位置添加一个集合

1.检查要插入元素的下标是否越界

2.将集合 c 转换数组

3.计算出插入元素后需要往后移动一位的元素个数,如果个数为0,则说明实在末尾添加

4.要移动的元素个数大于0,利用 System.arraycopy() 方法将元素往后移动

5.利用 Syatem.arraycopy() 方法将集合 c 添加进去

 

 1 private void ensureCapacityInternal(int minCapacity) {
 2         if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
 3             minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
 4         }
 5         ensureExplicitCapacity(minCapacity);
 6     }
 7 
 8     private void ensureExplicitCapacity(int minCapacity) {
 9         modCount++;
10         if (minCapacity - elementData.length > 0)
11             grow(minCapacity);
12     }

 

移除元素

 1 public E remove(int index) {
 2         rangeCheck(index);
 3         modCount++;
 4         E oldValue = elementData(index);
 5         int numMoved = size - index - 1;
 6         if (numMoved > 0)
 7             System.arraycopy(elementData, index+1, elementData, index,
 8                              numMoved);
 9         elementData[--size] = null;
10         return oldValue;
11     }

1.检查要移除的元素下标是否越界

2.计算出移除元素后需要向前移动一位的元素的个数。

  如果个数为0则说明移除的是最后一个元素

3.要移动的元素的个数大于0,则不是移除末尾元素,需要将移除元素 后面的所有元素往前移动一位    ==>    利用System.arraycopy()方法

4.将最后一个元素置为 null,方便 GC 回收,size减一

 

 1 public boolean remove(Object o) {
 2         if (o == null) {
 3             for (int index = 0; index < size; index++)
 4                 if (elementData[index] == null) {
 5                     fastRemove(index);
 6                     return true;
 7                 }
 8         } else {modCount != expectedModCount

1.判断数组中是否包含要移除的元素

2.如果包含,则调用 fastRemove(index) 方法将元素移除   ==>     fastRemove(index) 与remove(index)方法相似,只是少了下标越界检查

 

在源码里面有一个 modCount 的成员

protected transient int modCount = 0;

每次添加或移除元素是,都会进行 modCount++ 

它主要是用在迭代器 Iterator 里面

1 public Iterator<E> iterator() {
2         return new Itr();
3     }

它返回一个 Itr 类,是 ArrayList 中的一个内部类,里面有个成员 expectedModCount = modCount

1 final void checkForComodification() {
2             if (modCount != expectedModCount)
3                 throw new ConcurrentModificationException();
4         }

然后在上面得 checkForComodification() 方法中可以看到有一个 if 语句,如果 modCount != expectedModCount 则抛出 ConcurrentModificationException 异常

也就是,我们在使用迭代器遍历集合的时候,如果直接使用 list.add() 或 list.delete() 等修改元素个数得方法时,会抛出该异常

 

转载于:https://www.cnblogs.com/java-hui/p/11312114.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值