ArrayList 深入理解底层源码

ArrayList 集合源码解读

    • 介绍
    • 构造方法
    • add方法
    • remove方法
    • 总结

介绍

ArrayList Jdk1.8采用的是数组的数据结构,是非线程安全的一个集合 (多线程下数据不安全),本文章主要讲解ArrayList集合添加集合扩容,其他方法都相对简单,读懂这个后相信你翻翻源码即可读懂其他方法原理,下面讲解源码的可能会有点枯燥,请着重去了解集合添加和扩容的一个流程

构造方法

ArrayList 深入理解底层源码_第1张图片
我们来看源码迅速带过这三个构造方法,为了方便阅读,以下我们将当前集合数组统称为集合

  1. 无参构造方法,创建一个空集合

	//存放数据的Object数组
	transient Object[] elementData; // non-private to simplify nested class access
	//默认为空数组
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    public ArrayList() {
    	//将空数组赋值给当前集合
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  1. 指定初始化大小构造方法

    private static final Object[] EMPTY_ELEMENTDATA = {};

	public ArrayList(int initialCapacity) {
		//如果指定数组大小大于0执行
        if (initialCapacity > 0) {
        	//new 一个对象数组赋值给当前ArrayList集合
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
        	//等于零一样赋值为空集合
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
        	//非法参数抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
  1. 集合参数的构造方法,作用是将集合参数转换为当前ArrayList集合的值
 public ArrayList(Collection<? extends E> c) {
 		//将Collection接口集合转换成数组 赋值给当前ArrayList集合
        elementData = c.toArray();
       //如果当前集合(Collection接口集合)中参数不为空
        if ((size = elementData.length) != 0) {
        	//如果集合数组类型不是Object类型 比如int[] 则执行
            if (elementData.getClass() != Object[].class)
            	//将当前集合转换成Object类型的数组返回给当前集合
            	//这里直接完成了集合大小的初始化
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 当前集合大小为空则赋值空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

add方法

集合初始化以及集合扩容都在add方法中完成!请认真阅读,我们首先看一下文档,发现有两个add重载方法,我们来一一解刨
ArrayList 深入理解底层源码_第2张图片

  1. add(E e)
	/**
     * Default initial capacity.
     * 集合默认初始化大小
     */
    private static final int DEFAULT_CAPACITY = 10;

	//未初始化 默认赋值为0
	//该变量是用来存储集合数据个数
	private int size;

	public boolean add(E e) {
	
		//调用ensureCapacityInternal方法 传入size+1
        ensureCapacityInternal(size + 1); 
        
        //扩容方法执行完后进行数据赋值 size++
        elementData[size++] = e;
        
        //添加成功
        return true;
    }
    
    // 该方法负责调用其他两个方法
     private void ensureCapacityInternal(int minCapacity) {
     
     	//首先调用calculateCapacity(elementData, minCapacity)方法
     	//elementData 当前集合 
     	//minCapacity add方法传过来的size大小
     	
     	//接受到calculateCapacity方法返回的集合大小又调用ensureExplicitCapacity方法
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    //集合大小计算方法
	private static int calculateCapacity(Object[] elementData, int minCapacity) {
	
		//如果当前集合等于空集合
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        	// 比较DEFAULT_CAPACITY和minCapacity
        	// 返回最大值
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //不为空则返回当前集合大小
        return minCapacity;
    }
	private void ensureExplicitCapacity(int minCapacity) {
		//记录数据操作次数
        modCount++;
       
       //当集合满时(size+1>集合长度时)进行扩容
       //第一次集合大小初始化也会执行
        if (minCapacity - elementData.length > 0)
        	//扩容和集合大小初始化方法实现
            grow(minCapacity);
    }

	//扩容和集合大小初始化方法
	private void grow(int minCapacity) {
	
        // 当前数组长度赋值给oldCapacity
        int oldCapacity = elementData.length;
        
		//计算集合需要扩容的大小  集合原大小+原大小/2(奇数舍小数)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        
        //这句条件语句执行主要用于初始化场景
        //当前集合为空集合 还没初始化大小 newCapacity=0
        if (newCapacity - minCapacity < 0)
        
        	//则传递过来的集合大小赋值给newCapacity
        	//没有指定大小就是默认值10
            newCapacity = minCapacity;
            
        //这个不用考虑,数据量达到上亿才执行
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
            
         //扩容完成赋值给当前集合 Arrays.copyOf底层其实还是调用了System类的arraycopy方法
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

   
  1. add(int index, E element)
	public void add(int index, E element) {
		//索引验证
        rangeCheckForAdd(index);

		//调用扩容机制 可以借鉴前面代码
        ensureCapacityInternal(size + 1);  
        //作用是为要插入的索引留出空位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
          //添加数据
        elementData[index] = element;
        //添加完成,数据个数++
        size++;
    }

	//索引验证方法
	private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

remove方法

讲完来重要的add方法还是有必要看一下remove方法,首先看文档,两个重载方法,一个传的是索引,一个传的是对象,那么我们一个一个来刚
在这里插入图片描述

  1. remove(int index)
	public E remove(int index) {
		//索引验证
        rangeCheck(index);
		
		//数据操作记录
        modCount++;
        //存储要删除的值
        E oldValue = elementData(index);

		//集合大小减去待删除索引-1
        int numMoved = size - index - 1;
        //大于0表示在倒数一位数据前
        if (numMoved > 0)
        	//数组拷贝,在删除的索引接上后面的值(内存换时间,效率提升)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
         //不满足上述条件表示要删除的是最后一位,这里直接赋值为null
        elementData[--size] = null; 

		//返回旧值
        return oldValue;
    }
  1. remove(Object o)
// 我们可以看出集合根据数据删除 效率相对来说变差很多
 public boolean remove(Object o) {
 		//要删除数据为null
        if (o == null) {
        	//遍历整个数组
            for (int index = 0; index < size; index++)
            	//该索引值等于null 进行删除
                if (elementData[index] == null) {
                	//传入索引进行删除
                    fastRemove(index);
                    //返回true
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        //没有找到这个数据 返回false
        return false;
    }
	
	//删除实现 和前面的根据索引删除一摸一样
	private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; 
    }

总结

ArrayList是一个非线程安全的一个数组集合,在多线程资源抢夺时必定会出现安全隐患,
ArrayList还可以存储null数据,
ArrayList采用数组的添加和扩容的机制,以及根据索引删除,都是内存换时间的方法,时间复杂度都控制在O(1),
ArrayList根据数据删除时间复杂度在O(n),效率相对索引删除相差太多
ArrayList当集合满时进行扩容 扩容方式:原大小+原大小/2 (奇数舍小数)

本篇文章主要讲解这些核心知识点
但相信你看完这篇文章,其他的方法肯定是不在话下

你可能感兴趣的:(java底层源码,笔记)