继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

JDK源码分析 – ArrayList

HUH函数
关注TA
已关注
手记 320
粉丝 66
获赞 315

ArrayList类的申明

ArrayList是一个支持泛型的,底层通过数组实现的一个可以存任意类型的数据结构,源码中的定义如下:

1 public class ArrayList<E> extends AbstractList<E> 2         implements List<E>, RandomAccess, Cloneable, java.io.Serializable 3 {}

ArrayList类继承了AbstractList抽象类,AbstractList提供了List接口的默认实现

ArrayList实现了以下几个接口:

List<E>接口:约定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<E> iterator() {  2     return new Itr();  3 }  4   5 private class Itr implements Iterator<E> {  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<Integer> 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<E> listIterator() {  2     return new ListItr(0);  3 }  4   5 private class ListItr extends Itr implements ListIterator<E> {  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

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP