ArrayList详解

一. 关于 ArrayList 的常见内容

  描述: 实现 List<E> 接口; 元素可排序,可重复,可为 null ;不是线程安全的.

  继承以及实现关系:

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

   List<E> 接口定义了列表的方法和默认实现, AbstractList<E> 实现了 List<E> 接口,对其中的部分方法添加相关实现; RandomAccess 接口表示可以随机访问, Cloneable 接口表示可以克隆, Serializable 表示可以序列化,用于数据传输

二.  ArrayList 的实现原理

   ArrayList 是通过动态数组实现的,对 ArrayList 的操作实质是对其维护的动态数组进行操作.所以对元素的访问就是对数组的访问(查询为随机访问),元素的添加删除实质就是数组的复制或者追加和末尾移除.

三.  ArrayList 中定义的常量、属性和子集

  常量:

    /**
     * 默认容量
     */
    private static final int DEFAULT_CAPACITY = 10;
    /**
     * 空数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
    /**
     * 默认容量空数组
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    /**
     * 最大数组大小
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    常量描述:

      默认容量:无容量参数构造时,创建的数组默认容量

      空数组:有容量参数构造时使用的共享的空数组

      默认容量空数组:无容量构造,使用默认容量时使用的共享的空数组.源码注释中写到:与上面的空数组进行区分是为了了解首次添加元素时,要扩展多大容量

      最大数组大小:用于分配数组容量时的最大值,源码注释中写到:一些虚拟机会在数组中添加头数据,为了避免分配新数组的内存时发生内存溢出(注意:此处的内存溢出不一定是真的内存不够,而是需要的容量值大于 int 型最大值导致的),所以采用 int 型最大值减 8

  属性:

    /**
     * ArrayList中维护的动态数组
     */
    transient Object[] elementData;
    /**
     * 集合大小
     */
    private int size;

    属性描述:

      动态数组:作为集合实现,用于存储集合元素

      集合大小:表示集合的实际大小,集合的容量是当前数组的长度

  子集:

  1     private class SubList extends AbstractList<E> implements RandomAccess {
  2         private final AbstractList<E> parent;
  3         private final int parentOffset;
  4         private final int offset;
  5         int size;
  6 
  7         SubList(AbstractList<E> parent,
  8                 int offset, int fromIndex, int toIndex) {
  9             this.parent = parent;
 10             this.parentOffset = fromIndex;
 11             this.offset = offset + fromIndex;
 12             this.size = toIndex - fromIndex;
 13             this.modCount = ArrayList.this.modCount;
 14         }
 15 
 16         public E set(int index, E e) {
 17             rangeCheck(index);
 18             checkForComodification();
 19             E oldValue = ArrayList.this.elementData(offset + index);
 20             ArrayList.this.elementData[offset + index] = e;
 21             return oldValue;
 22         }
 23 
 24         public E get(int index) {
 25             rangeCheck(index);
 26             checkForComodification();
 27             return ArrayList.this.elementData(offset + index);
 28         }
 29 
 30         public int size() {
 31             checkForComodification();
 32             return this.size;
 33         }
 34 
 35         public void add(int index, E e) {
 36             rangeCheckForAdd(index);
 37             checkForComodification();
 38             // 调用原集合的 add 方法,在原集合上添加元素,add 时,原集合的 size 已经加 1
 39             parent.add(parentOffset + index, e);
 40             // 重新拉取修改次数,处理子集的 size
 41             this.modCount = parent.modCount;
 42             this.size++;
 43         }
 44 
 45         public E remove(int index) {
 46             rangeCheck(index);
 47             checkForComodification();
 48             E result = parent.remove(parentOffset + index);
 49             this.modCount = parent.modCount;
 50             this.size--;
 51             return result;
 52         }
 53 
 54         protected void removeRange(int fromIndex, int toIndex) {
 55             checkForComodification();
 56             parent.removeRange(parentOffset + fromIndex,
 57                                parentOffset + toIndex);
 58             this.modCount = parent.modCount;
 59             this.size -= toIndex - fromIndex;
 60         }
 61 
 62         public boolean addAll(Collection<? extends E> c) {
 63             return addAll(this.size, c);
 64         }
 65 
 66         public boolean addAll(int index, Collection<? extends E> c) {
 67             rangeCheckForAdd(index);
 68             int cSize = c.size();
 69             if (cSize==0)
 70                 return false;
 71 
 72             checkForComodification();
 73             parent.addAll(parentOffset + index, c);
 74             this.modCount = parent.modCount;
 75             this.size += cSize;
 76             return true;
 77         }
 78 
 79         public Iterator<E> iterator() {
 80             return listIterator();
 81         }
 82 
 83         public ListIterator<E> listIterator(final int index) {
 84             checkForComodification();
 85             rangeCheckForAdd(index);
 86             final int offset = this.offset;
 87 
 88             return new ListIterator<E>() {
 89                 int cursor = index;
 90                 int lastRet = -1;
 91                 int expectedModCount = ArrayList.this.modCount;
 92 
 93                 public boolean hasNext() {
 94                     return cursor != SubList.this.size;
 95                 }
 96 
 97                 @SuppressWarnings("unchecked")
 98                 public E next() {
 99                     checkForComodification();
100                     int i = cursor;
101                     if (i >= SubList.this.size)
102                         throw new NoSuchElementException();
103                     Object[] elementData = ArrayList.this.elementData;
104                     if (offset + i >= elementData.length)
105                         throw new ConcurrentModificationException();
106                     cursor = i + 1;
107                     return (E) elementData[offset + (lastRet = i)];
108                 }
109 
110                 public boolean hasPrevious() {
111                     return cursor != 0;
112                 }
113 
114                 @SuppressWarnings("unchecked")
115                 public E previous() {
116                     checkForComodification();
117                     int i = cursor - 1;
118                     if (i < 0)
119                         throw new NoSuchElementException();
120                     Object[] elementData = ArrayList.this.elementData;
121                     if (offset + i >= elementData.length)
122                         throw new ConcurrentModificationException();
123                     cursor = i;
124                     return (E) elementData[offset + (lastRet = i)];
125                 }
126 
127                 @SuppressWarnings("unchecked")
128                 public void forEachRemaining(Consumer<? super E> consumer) {
129                     Objects.requireNonNull(consumer);
130                     final int size = SubList.this.size;
131                     int i = cursor;
132                     if (i >= size) {
133                         return;
134                     }
135                     final Object[] elementData = ArrayList.this.elementData;
136                     if (offset + i >= elementData.length) {
137                         throw new ConcurrentModificationException();
138                     }
139                     while (i != size && modCount == expectedModCount) {
140                         consumer.accept((E) elementData[offset + (i++)]);
141                     }
142                     // update once at end of iteration to reduce heap write traffic
143                     lastRet = cursor = i;
144                     checkForComodification();
145                 }
146 
147                 public int nextIndex() {
148                     return cursor;
149                 }
150 
151                 public int previousIndex() {
152                     return cursor - 1;
153                 }
154 
155                 public void remove() {
156                     if (lastRet < 0)
157                         throw new IllegalStateException();
158                     checkForComodification();
159 
160                     try {
161                         SubList.this.remove(lastRet);
162                         cursor = lastRet;
163                         lastRet = -1;
164                         expectedModCount = ArrayList.this.modCount;
165                     } catch (IndexOutOfBoundsException ex) {
166                         throw new ConcurrentModificationException();
167                     }
168                 }
169 
170                 public void set(E e) {
171                     if (lastRet < 0)
172                         throw new IllegalStateException();
173                     checkForComodification();
174 
175                     try {
176                         ArrayList.this.set(offset + lastRet, e);
177                     } catch (IndexOutOfBoundsException ex) {
178                         throw new ConcurrentModificationException();
179                     }
180                 }
181 
182                 public void add(E e) {
183                     checkForComodification();
184 
185                     try {
186                         int i = cursor;
187                         SubList.this.add(i, e);
188                         cursor = i + 1;
189                         lastRet = -1;
190                         expectedModCount = ArrayList.this.modCount;
191                     } catch (IndexOutOfBoundsException ex) {
192                         throw new ConcurrentModificationException();
193                     }
194                 }
195 
196                 final void checkForComodification() {
197                     if (expectedModCount != ArrayList.this.modCount)
198                         throw new ConcurrentModificationException();
199                 }
200             };
201         }
202 
203         public List<E> subList(int fromIndex, int toIndex) {
204             subListRangeCheck(fromIndex, toIndex, size);
205             return new SubList(this, offset, fromIndex, toIndex);
206         }
207 
208         private void rangeCheck(int index) {
209             if (index < 0 || index >= this.size)
210                 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
211         }
212 
213         private void rangeCheckForAdd(int index) {
214             if (index < 0 || index > this.size)
215                 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
216         }
217 
218         private String outOfBoundsMsg(int index) {
219             return "Index: "+index+", Size: "+this.size;
220         }
221 
222         private void checkForComodification() {
223             if (ArrayList.this.modCount != this.modCount)
224                 throw new ConcurrentModificationException();
225         }
226 
227         public Spliterator<E> spliterator() {
228             checkForComodification();
229             return new ArrayListSpliterator<E>(ArrayList.this, offset,
230                                                offset + this.size, this.modCount);
231         }
232     }
View Code

    子集描述:

      自己的本质是截取原集合相应的部分展示,对自己的操作实际就是对原集合的操作

四. 一些重要的方法:

  1.构造方法

 1     /**
 2      * 初始化容量构造
 3      */
 4     public ArrayList(int initialCapacity) {
 5         if (initialCapacity > 0) {
 6             this.elementData = new Object[initialCapacity];
 7         } else if (initialCapacity == 0) {
 8             this.elementData = EMPTY_ELEMENTDATA;
 9         } else {
10             throw new IllegalArgumentException("Illegal Capacity: "+
11                                                initialCapacity);
12         }
13     }
14 
15     /**
16      * 无参构造
17      */
18     public ArrayList() {
19         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
20     }
21 
22     /**
23      * 集合构造
24      */
25     public ArrayList(Collection<? extends E> c) {
26         Object[] a = c.toArray();
27         if ((size = a.length) != 0) {
28             if (c.getClass() == ArrayList.class) {
29                 elementData = a;
30             } else {
31                 elementData = Arrays.copyOf(a, size, Object[].class);
32             }
33         } else {
34             // replace with empty array.
35             elementData = EMPTY_ELEMENTDATA;
36         }
37     }

    描述:在新建列表时,使用无参构造或者容量为 0 的构造,得到的列表实际上是一个空列表(空数组),只有在首次添加元素时才会扩展至默认容量或者指定容量的列表(新建指定长度的数组)

  2.grow

 1     private void grow(int minCapacity) {
 2         // overflow-conscious code
 3         int oldCapacity = elementData.length;
 4         // 扩容至原容量的1.5倍
 5         int newCapacity = oldCapacity + (oldCapacity >> 1);
 6         // 如果新容量仍然比指定容量小,则新容量就是指定的最小容量
 7         if (newCapacity - minCapacity < 0)
 8             newCapacity = minCapacity;
 9         if (newCapacity - MAX_ARRAY_SIZE > 0)
10             newCapacity = hugeCapacity(minCapacity);
11         // minCapacity is usually close to size, so this is a win:
12         elementData = Arrays.copyOf(elementData, newCapacity);
13     }

    描述:列表容量扩展的主要方法,结果是常规式扩展(原容量的1.5倍),或者最小可用容量,是在不大于最大数组长度的条件下进行的

  3.ensureXxx

 1     private static int calculateCapacity(Object[] elementData, int minCapacity) {
 2         if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
 3             return Math.max(DEFAULT_CAPACITY, minCapacity);
 4         }
 5         return minCapacity;
 6     }
 7 
 8     private void ensureCapacityInternal(int minCapacity) {
 9         ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
10     }
11 
12     // 根据指定容量确定明确的合适容量容量
13     private void ensureExplicitCapacity(int minCapacity) {
14         modCount++;
15 
16         // overflow-conscious code
17         if (minCapacity - elementData.length > 0)
18             grow(minCapacity);
19     }

    描述:这三个方法是为了确认当前容量是否足够

五.常用API

  1.size

1     public int size() {
2         return size;
3     }

  2.isEmpty

1     public boolean isEmpty() {
2         return size == 0;
3     }

    描述:以上两个是直接使用size属性进行操作

  3.indexOf

 1     public int indexOf(Object o) {
 2         if (o == null) {
 3             for (int i = 0; i < size; i++)
 4                 if (elementData[i]==null)
 5                     return i;
 6         } else {
 7             for (int i = 0; i < size; i++)
 8                 if (o.equals(elementData[i]))
 9                     return i;
10         }
11         return -1;
12     }

  4.lastIndexOf

 1     public int lastIndexOf(Object o) {
 2         if (o == null) {
 3             for (int i = size-1; i >= 0; i--)
 4                 if (elementData[i]==null)
 5                     return i;
 6         } else {
 7             for (int i = size-1; i >= 0; i--)
 8                 if (o.equals(elementData[i]))
 9                     return i;
10         }
11         return -1;
12     }

  5.contains

1     public boolean contains(Object o) {
2         return indexOf(o) >= 0;
3     }

  6.remove

 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 {
 9             for (int index = 0; index < size; index++)
10                 if (o.equals(elementData[index])) {
11                     fastRemove(index);
12                     return true;
13                 }
14         }
15         return false;
16     }

  7.forEach

 1     public void forEach(Consumer<? super E> action) {
 2         Objects.requireNonNull(action);
 3         final int expectedModCount = modCount;
 4         @SuppressWarnings("unchecked")
 5         final E[] elementData = (E[]) this.elementData;
 6         final int size = this.size;
 7         for (int i=0; modCount == expectedModCount && i < size; i++) {
 8             action.accept(elementData[i]);
 9         }
10         if (modCount != expectedModCount) {
11             throw new ConcurrentModificationException();
12         }
13     }

    描述:以上五个是遍历数组操作,需要注意的是,在遍历时需要区分 null 和非 null 时的相等比较

  8.get

1     public E get(int index) {
2         rangeCheck(index);
3 
4         return elementData(index);
5     }

  9.set

1     public E set(int index, E element) {
2         rangeCheck(index);
3 
4         E oldValue = elementData(index);
5         elementData[index] = element;
6         return oldValue;
7     }

  10.remove

 1     public E remove(int index) {
 2         rangeCheck(index);
 3         // 修改次数
 4         modCount++;
 5         E oldValue = elementData(index);
 6         // 要移动的元素数,从 index + 1 到 size ,共 size - index - 1 个
 7         int numMoved = size - index - 1;
 8         if (numMoved > 0)
 9             // 将数组从 index + 1 开始到 size 结束的 size - index - 1 个元素前移
10             System.arraycopy(elementData, index+1, elementData, index,
11                              numMoved);
12         elementData[--size] = null; // clear to let GC do its work
13 
14         return oldValue;
15     }

    描述:以上三个是采用随机访问,其中的rangeCheck方法是对下标的判断是否越界

      remove方法有重载方法,即remove(Object o),此方法是通过遍历数组的方式查找元素

  11.toArray

1     public Object[] toArray() {
2         return Arrays.copyOf(elementData, size);
3     }

    描述:直接对列表中维护的集合进行复制返回

  12.add

 1     public boolean add(E e) {
 2         ensureCapacityInternal(size + 1);  // Increments modCount!!
 3         elementData[size++] = e;
 4         return true;
 5     }
 6 
 7     public void add(int index, E element) {
 8         // 校验 index ,需要额外验证是否小于 0
 9         rangeCheckForAdd(index);
10 
11         ensureCapacityInternal(size + 1);  // Increments modCount!!
12         // 将数组从 index + 1 位置开始到 size 结束的 size - index 个元素后移
13         System.arraycopy(elementData, index, elementData, index + 1,
14                          size - index);
15         elementData[index] = element;
16         size++;
17     }

    描述:add方法是通过随机访问找到位置,然后添加(追加或者数组复制)

  13.clear

1     public void clear() {
2         modCount++;
3 
4         // clear to let GC do its work
5         for (int i = 0; i < size; i++)
6             elementData[i] = null;
7 
8         size = 0;
9     }

    描述:clear方法不是将列表清除,此时列表容量(数组和数组长度都)没有发生变化,只是将列表中的元素清空.

  14.subList

1     public List<E> subList(int fromIndex, int toIndex) {
2         subListRangeCheck(fromIndex, toIndex, size);
3         return new SubList(this, 0, fromIndex, toIndex);
4     }

 

 

 

受限于个人水平,如有错误或者补充望请告知(博客园:xiao_lin_unit)

 

posted @ 2021-03-11 11:50  xiao_lin  阅读(321)  评论(0编辑  收藏  举报