• java源码研究--ArrayList实现


    java.util.ArrayList是十分常用的容器之一,本文针对其常用方法,对其进行简单的研究。
    ArrayList常见方法如下,主要还是增删改查:

    首先,看一下ArrayList中如何保存数据的:

    transient Object[] elementData;

    所以,所有的数据都是保存在数组里的。当然,数组都有个大小:

    若ArrayList使用无参构造函数实例化:

    ArrayList<Integer> arrayList = new ArrayList<Integer>();

    那么,内部数组的大小默认就是10:

    1 public ArrayList() {
    2     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //空数组
    3 }
    4 
    5 //刚实例化完,其实数组为空数组,在往arrayList添加数组时才会扩充容量(默认大小为10),扩充过程继续往后看。
    6 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 
    7 private static final int DEFAULT_CAPACITY = 10; //默认最小容量为10

    下面,研究一下add(E element)方法:

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

      其中,ensureCapacityInternal函数用来确保数组大小足够使用(至少为size+1),该函数如下:

     1 private void ensureCapacityInternal(int minCapacity) {
     2     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //若为空数组(实例化完成后),进入此分支
     3         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //minCapacity = Math.max(10, 1);
     4     }
     5 
     6     ensureExplicitCapacity(minCapacity); //ensureExplicitCapacity(10);
     7 }
     8 
     9 private void ensureExplicitCapacity(int minCapacity) {
    10     modCount++;
    11 
    12     // overflow-conscious code
    13     if (minCapacity - elementData.length > 0) //10-0>0
    14         grow(minCapacity); //grow(10); 这就是首次实例化之后的扩充容量了;
    15 }
    16 
    17 private void grow(int minCapacity) {
    18     // overflow-conscious code
    19     int oldCapacity = elementData.length; 
    20     int newCapacity = oldCapacity + (oldCapacity >> 1); //newCapacity 扩充为1.5倍
    21     if (newCapacity - minCapacity < 0)
    22         newCapacity = minCapacity;
    23     if (newCapacity - MAX_ARRAY_SIZE > 0)
    24         newCapacity = hugeCapacity(minCapacity);
    25     // minCapacity is usually close to size, so this is a win:
    26     elementData = Arrays.copyOf(elementData, newCapacity); //首次插数据,将容量扩充为10;之后容量不够时,数组容量按照1.5倍扩充。
    27 }

    可见,在首次插数据时,容量首先将数组容量扩充为10,再执行elementData[size++]=e;
    在数组满了,继续插数据时,会先将容量扩充为1.5倍(10+10*1/2),再继续插数据;
    当数组又满了,再继续扩充为1.5倍(15+15*1/2)......

    因此,在明知数据量很大的情况下,初始化时应该指定合适大小的容量,避免arrayList反复扩充容量

    例如:若数组总容量为8000,可以这样实例化:

    ArrayList<Integer> arrayList = new ArrayList<Integer>(10000);
    

    带参构造函数如下:

    public ArrayList(int initialCapacity) {
    	if (initialCapacity > 0) {
    		this.elementData = new Object[initialCapacity];
    	}
    	.....
    }
    

      

    下面研究一下add(int index, E element)

    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++;
    }
    

    这个函数是将新元素插入到数组的指定位置(index),从上面的源码来看,实现过程是先将原先index位置及置换的元素向后移1位,然后再将新元素放在位置index。

      

    继续研究一下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;
    }
    

    所以,类似的实现原理:将index+1位置及之后的元素先前移动1位,再把最后一个元素清掉。

    了解了ArrayList实现原理,才能更好的选择合适的容器,以及以正确的方式使用,以获取高效率。

    后面将介绍另外一个List接口容器:LinkedList

  • 相关阅读:
    2019年春第八周作业
    2019 第七周作业
    2019 第六周作业
    2019 第五周作业
    2019 第四周作业
    2019 第三周作业
    2019 第二周作业
    2019第一周编程总结2
    2019第一周编程总结1
    秋季学期学习总结
  • 原文地址:https://www.cnblogs.com/xinxinBlog/p/9926184.html
Copyright © 2020-2023  润新知