ArrayList源码分析

in 网站建设
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

ArrayList结构和派系

派生关系

Iterable

 
 集合的最上级接口, 定义了spliterator,iterator和实现了forEach函数

Collection


所有集合的父级接口, 定义了通用函数 

AbstractCollection


实现了Collection中大量函数, 除了特定的几个函数iterator和size之外的函数 

AbstractList


继承了AbstractCollection, 实现了List接口的抽象类, 它实现了List中除size(), get(int location)之外的函数
和AbstractCollection相比, 实现了iterator()接口  

RandomAccess


提供了随机访问(随机查找)功能, 在ArrayList中通过元素的序号快速获取元素对象, 这就是快速随机访问(随机查找)  

List


List有序队列接口, 提供了一些通过下标访问元素的函数.    
List中每一个元素都有一个索引, 第一个元素是0, 往后每次+1

数据结构

ArrayList数据结构是数组
通过一定的算法逻辑动态扩容数组的长度, 可以理解为ArrayList底层是一个动态数组
与java原生的数组相比, 它的容量能动态增长  
内存需要连续的空间保证
除去尾部位置元素的添加, 删除操作, 其余都涉及到位置移动
随机查找效率快(下标搜寻)

初始化

ArrayList源码设计的优雅之道
new ArrayList的时候, 并不会真的创建长度10的对象数组
什么时候往里面添加元素了,什么时候才会完成数组的创建

初始化源码

    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; // non-private to simplify nested class access

    private int size;

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }    
    
    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);
        }
    }

详细分析

serialVersionUID


用来验证版本一致性的字段, 反序列化时会效验

DEFAULT_CAPACITY


默认容量, 为10 

EMPTY_ELEMENTDATA


空对象数组

DEFAULTCAPACITY_EMPTY_ELEMENTDATA


默认容量空对象数组

elementData


动态数组的空间大小, 包含空元素

size


动态数组的实际大小, 记录数组里有多少元素, 不记录空元素

ArrayList()


无参构造
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
把默认容量空对象数组赋值给elementData, 还是空
无参构造创建动态数组时, size为0, elementData为0

ArrayList(int initialCapacity)


带参构造
参数大于0时
    this.elementData = new Object[initialCapacity];
    创建一个大小为initialCapacity的Object数组赋给elementData
参数小与于0时
    this.elementData = EMPTY_ELEMENTDATA;
    开一个长度为10的空间
参数小与于0时
    抛异常

总结

代码测试

    public static void main(String[] args) throws Exception {
        ArrayList  arr = new ArrayList();
        Field field = arr.getClass().getDeclaredField("elementData");
        field.setAccessible(true);
        Object[] o = (Object[]) field.get(arr);
        System.out.println(arr.size());
        System.out.println(o.length);

        arr.add("1");
        field = arr.getClass().getDeclaredField("elementData");
        field.setAccessible(true);
        o= (Object[]) field.get(arr);
        System.out.println(arr.size());
        System.out.println(o.length);

        for (int i = 0; i < 16; i++) {
            arr.add(i);
        }
        field = arr.getClass().getDeclaredField("elementData");
        field.setAccessible(true);
        o= (Object[]) field.get(arr);
        System.out.println(arr.size());
        System.out.println(o.length);
    }

输出

0
0
1
10
17
22


初始化总结

ArrayList  arr = new ArrayList();
elementData为0
size为0

ArrayList  arr = new ArrayList(1);
elementData为0
size为1, 走了带参构造, 参数为几, 空间为几, ArrayList(int initialCapacity)   

add("test");//添加元素源码分析会讲为什么空间为10
elementData为10 
size为1
如果第一次添加元素数量为11, 基于动态扩容1.5倍, 它的长度就是15

添加元素

添加元素源码

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }    

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

详细分析

添加一个元素1-10个元素时

    add(E e)方法 //以无参初始化(ArrayList arr = new ArrayList();), 并调用add(E e)添加一个元素为例
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    
    进ensureCapacityInternal(size + 1);此时传参为1 (0+1=1)
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
   
    进calculateCapacity, 判断elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA是否成立
    public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
    条件成立, 因为无参初始化方法已经赋过值
    进if, 判断10和参数哪个大, 10大, 返回10
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
  
    此时返回ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); 这里, 参数是10
    10-10不大于0, 没走到动态扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
目前是elementData(空间长度为10), size为1(size不记录空元素)

添加元素超出10的情况, 动态扩容

    添加第11个元素时
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    此时minCapacity为11
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
   
    不走if, 返回11, 则ensureExplicitCapacity(11)
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
   
    11-10大于0, 进入动态扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
         // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    进grow(minCapacity); 动态扩容方法
    private void grow(int minCapacity) {
        老空间为10
        int oldCapacity = elementData.length;
        新空间=10+(10>>1), 位运算10的二进制位为1010, 
        右移动1位二进制为101, 10进制为5, 所以新空间为15=10+5
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        如果新空间-11小于0,则新空间为11 
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        如果新空间大于0, 则赋值
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity); //下面讲这个方法
        拷贝一个新的数组和长度的集合,赋值给我们的对象数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    
    MAX_ARRAY_SIZE为Integer.MAX_VALUE-8 (int最大值-8)
    如果要开的空间大于MAX_ARRAY_SIZE, 就把int最大值-8赋值给它
    如果不大于MAX_ARRAY_SIZE,则把MAX_ARRAY_SIZE赋值给它
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    
    
    官方注释对于-8的介绍
        这8bit里存了要分配最大数组的大小(数组下标???)
        虚拟机中在数据中保留的标题字 (既数组形状判断是否为数组,对象是否同步,数组大小, 不知道是否包含了对象类型的类信息指针???)
        尝试分配更大的数组会抛异常, 其实大小还是支持到了Integer.MAX_VALUE
    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

fail-fast失败原因(modCount++)

之前介绍源码的时候把modCount++跳过了, 接下来解释下, 在ArrayList源码里有个监听
源码

    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

在每次做集合元素操作时,都会改变modCount,在迭代的时候,会把modCount赋值给expectedModCount,      
如果在迭代过程中删除元素,就会修改modCount,但是迭代器在过程中不会同步expectedModCount,  
 每次迭代会会比较是否相等
 就是说在使用迭代器的时候, 不能改变元素, 但是可以使用迭代器的方法去改变

查找元素

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
    
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    E elementData(int index) {
        return (E) elementData[index];
    }
    
判断下标是否越界
复杂度O(1), 直接根据下标位置返回元素

删除元素

删除元素源码

    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;
    }
    
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);
                                        
    src - 源数组。
    srcPos - 源数组中的起始位置。
    dest - 目标数组。
    destPos - 目的地数据中的起始位置。
    length - 要复制的数组元素的数量。

总结

将指定源数组中的数组从指定位置复制到目标数组的指定位置

    比如我们有个数组
    值为 a b c d e f g h i g k
    下标 0 1 2 3 4 5 6 7 8 9 10
    如果删除f
    rangeCheck(index); //效验下标越界, index=5
    int numMoved = size - index - 1; //numMoved= 4
    调用System.arraycopy(elementData, index+1, elementData, index, numMoved);

从原数组下标从6开始copy到新数组从下标从5开始的后面

关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看