zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    五一训练礼包 — B
    五一训练礼包—坐标问题
    单链表
    顺序表
    链表
    基础DP(3)
    基础DP(2)
    基础DP(1)
    分治法
    最小表示法
  • 原文地址:https://www.cnblogs.com/xiao-lin-unit/p/14516980.html
Copyright © 2011-2022 走看看