• ArrayList详解



    ArrayList


    1.概述

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

    • ArrayList是用数组实现的线性列表,其是相当与动态数组。
    • ArrayList查找、修改速度较快,插入、删除速度较慢。
    • ArrayList可以存放任何元素,包括null对象。
    • ArrayList是线程不同步的。

    ArrayList的类关系图如下:

     

    2.详解

    字段与构造函数

     1 private static final int DEFAULT_CAPACITY = 10;  // ArrayList默认容量
     2 transient Object[] elementData;  // ArrayList中存放元素的数组
     3 private int size;  // ArrayList存放元素的个数
     4 /**
     5  * 常量,代表ArrayList中没有元素。只有当使用有参的构造函数且initialCapatity为0时返回它。
     6  */
     7 private static final Object[] EMPTY_ELEMENTDATA = {};
     8 /**
     9  *常量,当使用无参构造函数时返回它。
    10  */
    11 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    12 /**
    13  * 默认的构造函数:创建容量为10的ArrayList
    14  */
    15 public ArrayList(){
    16     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    17 }
    18 /**
    19  * 创建一个指定容量的ArrayList
    20  * @param initialCapatity ArrayList的容量
    21  */
    22 public ArrayList(int initialCapatity){
    23     if(initialCapatity > 0) {
    24         this.elementData = new Object[initialCapatity];
    25     }else if(initialCapatity == 0) {
    26         this.elementData = EMPTY_ELEMENTDATA;
    27     }else {
    28         throw new IllegalArgumentException("Illegal Capatity" + inintCapatity);
    29     }
    30 }
    31 /**
    32  * 创建一个包含Collection的ArrayList
    33  * @param @param c Collection
    34  */
    35 public ArrayList(Collection<? extends E> c){
    36     elementData = c.toArray();
    37     if((size = elementData.length) != 0){
    38         // c.toArray might (incorrectly) not return Object[] (see 6260652)
    39         if(elementData.getClass() != Object[].class){
    40             elementData = Arrays.copyof(elementData, size, Object[].class);
    41         }
    42     }
    43 }

    一些方法

     1 public E get(int index){
     2     // 检查索引位置
     3     rangeCheck(index);
     4     return elementData[index];
     5 }
     6 public E set(int index, E element){
     7     // 检查索引位置
     8     rangeCheck(index); 
     9     E oldValue = elementData[index];
    10     elementData[index] = element;
    11     return oldValue;   
    12 }
    13 public boolean add(E e){
    14     // 确保ArrayList有足够的容量:验证+扩容
    15     ensureCapacityInternal(size + 1);
    16     elementData[size++] = e;
    17     return true;
    18 }
    19 public void add(int index, E element){
    20     // 检查索引是否合适
    21     rangeCheckForAdd(index);
    22     // 确保ArrayList有足够的容量:验证+扩容
    23     ensureCapatityInternal(size + 1);
    24     // 把原数组从index后的每一个元素向后移一位
    25     System.arraycopy(elementData,index,elementData,index+1,size-index);
    26     elementData[index] = element;
    27     size++;
    28 }
    29 public E remove(int index){
    30     // 检查索引位置是否合适
    31     rangeCheck(index);
    32     modCount++;
    33     E oldValue = elementData(index);
    34     // 把原数组从index后的每一个元素向前移动一位
    35     int numMoved = size - index - 1;
    36     if(numMoved > 0){
    37         System.arraycopy(elementData, index+1, elementData, index, numMoved);
    38     }
    39     elementData[--size] = null; //clear to let GC do its work
    40     return oldValue;
    41 }

    扩容
    在上面add()方法中我们都需要验证ArrayList容量是否足够,不足需要扩容。

     1 /**
     2  * 验证+扩容
     3  * @param minCapacity 现在ArrayList存放元素所需要的最小容量
     4  */
     5 private void ensureCapacityInternal(int minCapacity) {
     6     if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
     7         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
     8     }
     9     ensureExplicitCapacity(minCapacity);
    10 }
    11 private void ensureExplicitCapacity(int minCapacity) {
    12     modCount++;
    13     if(minCapacity - elementData.length > 0){
    14         // 扩容
    15         grow(minCapacity);
    16     }
    17 }
    18 /**
    19  * 该方法是扩容:扩大至原来的1.5倍,我没有看懂怎么就扩大到原来的1.5倍~(0)~
    20  */
    21 private void grow(int minCapacity) {
    22     // 扩大至原来的1.5倍
    23     int oldCapacity = elementData.length;
    24     int newCapacity = oldCapacity + (oldCapacity >> 1);
    25     if(newCapacity - minCapacity < 0){
    26         newCapacity = minCapacity;
    27     }
    28     if(newCapacity - MAX_ARRAY_SIZE > 0){
    29         newCapacity = hugeCapacity(minCapacity);
    30     }
    31     // elementData扩容
    32     elementData = Arrays.copyOf(elementData,newCapacity);
    33 }

    以上是部分源码分析。
    还有几点:

    • 以上是JDK1.8的源码,其中怎么就扩大要原来的1.5倍,我没有看懂。而且为什么要扩大要原来的1.5倍我也不知道。之前版本是增加至(oldCapacity * 3)/2 + 1。
    • 从上面我们可以看出在插入、删除元素时我们需要调用System.arraycopy()方法,这就比较耗时,所以说ArrayList在插入、删除时的速度慢的原因。当然这是由于ArrayList底层使用数组这种数据结构的缘故。

    3.自己模仿写的ArrayList

    class ArrayList<E> extends AbstractList<E> implements List<E> {
        // 默认容量
        private static final int DEFAULT_CAPACITY = 10;
        // 存放元素数量
        private int size;
        // 存放元素的数组
        private Object[] elementData;
    
        ArrayList(){
            elementData = new Object[DEFAULT_CAPACITY];
        }
    
        ArrayList(int initCapacity){
            if(initCapacity > 0){
                elementData = new Object[initCapacity];
            }else {
                throw new IllegalArgumentException("Illegal Capacity" + initCapacity);
            }
        }
    
        ArrayList(Collection< ? extends E> c){
            elementData = c.toArray();
            this.size = elementData.length;
            if(size != 0){
                if(elementData.getClass() != Object[].class){
                    elementData = copyOf(elementData,size,Object[].class);
                }
            }
        }
    
        @Override
        @SuppressWarnings("unchecked")
        public E get(int index) {
            check(index);
            return (E) elementData[index];
        }
    
        @Override
        public int size() {
            return size;
        }
    
        @Override
        public boolean add(E e) {
            ensureCapacity(size + 1);
            elementData[size] = e;
            size++;
            return true;
        }
    
        @Override
        public void add(int index, E element) {
            check(index);
            ensureCapacity(size + 1);
            System.arraycopy(elementData,index,elementData,index+1,size - index);
            elementData[index] = element;
            size++;
        }
    
        @Override
        public boolean remove(Object o) {
            for(int index = 0; index < size; index++){
                if(Objects.equals(o,elementData[index])){
                    int numMoved = size - index - 1;
                    if(numMoved > 0){
                        System.arraycopy(elementData, index + 1, elementData, index, numMoved);
                    }
                    elementData[size] = null;
                    size--;
                    return true;
                }
            }
            return false;
        }
    
        @Override
        @SuppressWarnings("unchecked")
        public E remove(int index) {
            check(index);
            E oldValue = (E) elementData[index];
            int numMoved = size - index - 1;
            if(numMoved > 0){
                System.arraycopy(elementData, index + 1, elementData, index, numMoved);
            }
            elementData[size] = null;
            size--;
            return oldValue;
        }
    
        @Override
        @SuppressWarnings("unchecked")
        public E set(int index, E element) {
            check(index);
            E oldValue = (E) elementData[index];
            elementData[index] = element;
            return oldValue;
        }
    
        // 验证索引
        private void check(int index){
            if(index < 0 || index > size){
                throw new IndexOutOfBoundsException("Index:" + index + "Size:" + size);
            }
        }
        // 验证+扩容
        private void ensureCapacity(int minCapacity){
            // 验证
            if(minCapacity > elementData.length){
                // 扩容
                int oldCapacity = elementData.length;
                int newCapacity = (int) (oldCapacity * 1.5);
                elementData =  Arrays.copyOf(elementData, newCapacity);
            }
        }
    }
  • 相关阅读:
    C#操作Windows控制面板
    WPF打印控件内容
    LINQ函数
    通过实现System.IComparable接口的CompareTo方法对两个类进行比较
    泛型和约束
    CSS样式基础总结
    C#调用百度高精度IP定位API通过IP获取地址
    软件下载路径
    RNN学习资料
    mysql 不能插入中文记录
  • 原文地址:https://www.cnblogs.com/maying3010/p/6484037.html
Copyright © 2020-2023  润新知