zoukankan      html  css  js  c++  java
  • 秋招之路1:ArrayList的底层实现原理

    ArrayList

    概述

    1. ArrayList 是基于数组实现的,是一个动态数组
    2. ArrayList 不是线程安全的,只能在单线程环境下;多线程使用ArrayList,应该考虑Collections.synchronizedList(List l)和concurrent并发包下的CopyOnWriteArrayList类
    3. ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输;
      实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问;
      实现了Cloneable接口,能被克隆。

    首先,我的length和size有点混

    1.length属性是针对Java中的数组来说的,要求数组的长度可以用其length属性;
    2.length()方法是针对字符串来说的,要求一个字符串的长度就要用到它的length()方法;
    3.java中的size()方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!

    elementData

    transient Object[] elementData:
    transient:用transient关键字标记的成员变量不参与序列化过程。

    size

        /**
         * The size of the ArrayList (the number of elements it contains).
         *
         * @serial        这个是什么意思呢?说明一个序列化属性
         */
        private int size;
    

    构造方法

    一共三种:
    //指定初始大小的构造方法
    public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    
    //默认:
    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }     
    
    //使用集合
    public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();    //转换为数组,这个数组中的类型是根据c判断的,有可能不是Object[]
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)    //不是Oject[]时候,利用copyOf进行转换
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    

    常常出现的modCount,expectedModCount变量

    这一变量常常和线程安全有关,是记录修改次数的两个变量

    add方法

    public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    //调用下面的这个方法
    private void ensureCapacityInternal(int minCapacity) {
            //用来判定是否是初次增加。
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {    
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
    
            ensureExplicitCapacity(minCapacity);
        }
    //调用下面这个方法
    private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
    //看看本次增加的值,是否大于elementData了,大于再增加
                grow(minCapacity);
        }
    //调用下面的这个方法
    private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
    //每次成1.5倍的增长
            if (newCapacity - minCapacity < 0)
    //1.5倍都不够,就直接用minCapacity作为新容量
                newCapacity = minCapacity;
    //是否超过MAX_ARRAY_SIZE[一般虚拟机的防溢出的最大内存]
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    

    add方法的另外一个重载

    public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }
    

    //作用就是: 如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加1)。

    主要涉及这个方法
    public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
    代码解释:
      Object src : 原数组
    int srcPos : 从元数据的起始位置开始
      Object dest : 目标数组
      int destPos : 目标数组的开始起始位置
      int length : 要copy的数组的长度

    add的另一个重载

    //从指定的位置开始,将指定collection中的所有元素插入到此列表中。  
    public boolean addAll(Collection<? extends E> c) {
            Object[] a = c.toArray();
            int numNew = a.length;
            ensureCapacityInternal(size + numNew);  // Increments modCount
            System.arraycopy(a, 0, elementData, size, numNew);
            size += numNew;
            return numNew != 0;
        }
    

    get方法

    public E get(int index) {
            rangeCheck(index);    
    //检查是否越界
            return elementData(index);    
    //抑制返回值警告的一个方法
        }
    

    set方法

    // 用指定的元素替代此列表中指定位置上的元素,并返回以前位于该位置上的元素。  
    public E set(int index, E element) {
            rangeCheck(index);
    
            E oldValue = elementData(index);
            elementData[index] = element;
            return oldValue;
        }
    

    remove

    有三种实现

    //移除制定位置上的元素
    public E remove(int index) {
            rangeCheck(index);
    
            modCount++;
            E oldValue = elementData(index);
    
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; // clear to let GC do its work
    
            return oldValue;
        }
    
    //移除指定元素:
    remove(Object o)中通过遍历element寻找是否存在传入对象,一旦找到就调用fastRemove移除对象。
    fastRemove:基本是和remove(int index)一致,但是跳过了边界检查条件。
    public boolean remove(Object o) {
            if (o == null) {
                for (int index = 0; index < size; index++)
                    if (elementData[index] == null) {    //java 判断null的方法。
                        fastRemove(index);
                        return true;
                    }
            } else {
                for (int index = 0; index < size; index++)
                    if (o.equals(elementData[index])) {    //java的.equals是判断是否是值相等
                        fastRemove(index);
                        return true;
                    }
            }
            return false;
        }
    
    //removeRange
    protected void removeRange(int fromIndex, int toIndex) {
            modCount++;
            int numMoved = size - toIndex;
            System.arraycopy(elementData, toIndex, elementData, fromIndex,
                             numMoved);
    
            // clear to let GC do its work    //后面的null元素,让gc去回收。
            int newSize = size - (toIndex-fromIndex);
            for (int i = newSize; i < size; i++) {
                elementData[i] = null;
            }
            size = newSize;
        }
    

    toArray方法

    public Object[] toArray() {
            return Arrays.copyOf(elementData, size);
        }
    
    //这个toArray方法,主要是用于array转换成特定的比如是String[]等数组时候用的
    public <T> T[] toArray(T[] a) {
            if (a.length < size)
                // Make a new array of a's runtime type, but my contents:
                return (T[]) Arrays.copyOf(elementData, size, a.getClass());
            System.arraycopy(elementData, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }
    

    链接:toArray详解

    Fail-Fast机制:

    ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。
    具体参见HashMap的实现原理 中的Fail-Fast机制。

  • 相关阅读:
    Python正课38 —— 有参装饰器
    Python正课37 —— 无参装饰器
    Python正课36 —— 闭包函数
    Python正课35 —— 函数对象与函数嵌套
    Python正课34 —— Global与Nonlocal
    Python正课33 —— 名称空间 与 作用域 介绍
    vue中wowjs的使用
    js得到时间戳(10位数)
    腾讯地图使用中,出现了“鉴权失败,请传入正确的key”
    js数组操作大全
  • 原文地址:https://www.cnblogs.com/whyaza/p/12318346.html
Copyright © 2011-2022 走看看