• java(6) ArrayList源码


    系统环境: JDK 1.7

    成员变量

    //默认的初始化数组大小
    private static final int DEFAULT_CAPACITY = 10;
    
    //空的对象数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    
    //声明对象数组,不可序列化 transient
    private transient Object[] elementData;
    
    //数组的大小
    private int size;

    构造方法

    1.带有容量initialCapacity的构造方法

    public ArrayList(int initialCapacity) {
        super();
        //参数小于0 则抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        //new一个该大小的object数组赋给elementData  
        this.elementData = new Object[initialCapacity];
    }

    2.不带参数的构造方法

    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }    

    3.带参数Collection的构造方法

    public ArrayList(Collection<? extends E> c) {
        //将 c 转换为对象数据并赋值给elementData
        elementData = c.toArray();
        //获取数组大小
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }    
    
    注:c.toArray()返回的类型可能是非Object[]类型,如:Arrays.asList().toArray(),详情参考《关于 ArrayList.toArray()和Arrays.asList().toArray()方法

    总结:通过三个构造方法 我们发现ArrayList的实质就是封装了对数组的一些操作,通过这些操作,从而达到我们需要的目的

    Add方法分析

    public boolean add(E e) {
        // 确保当前的数组大小可以装的下传入的对象e
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 在内部数组中存放对象,并将索引值+1
        elementData[size++] = e;
        return true;
    }
    
    public void add(int index, E element) {
        // 确保传入的数值没有越界
        rangeCheckForAdd(index);
        // 确保当前的数组大小可以装的下传入的对象e
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }    
    
    private void ensureCapacityInternal(int minCapacity) {
        //针对new ArrayList()这种初始化方法有效,只有无参构造方法让内部数组等于empty。
        if (elementData == EMPTY_ELEMENTDATA) {
            // 确保 minCapacity 最小值是 10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //
        ensureExplicitCapacity(minCapacity);
    }    
    
    
    //判断是不是真的需要扩容。
    //如果我们用new ArrayList()初始化,根据上边的代码 minCapacity=10,而现在内部类真正的大小elementData.length是 0,所以需要扩容。
    //如果我们用new ArrayList(20)初始化,内部数组在构造方法内已经初始化了,内部数组长度为20,而在add方法的时候,第一个操作传入的是size+1,即0+1。
    //所以到达这个判断的时候,最小需要的容量为1,而长度为20 ,必然不需要扩容    
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
    
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }    
    
    //数组真正的扩容操作
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        
        // 新长度为原数组长度的1.5倍  整数右移1为 相当于除2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        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);
    }    

    Remove方法分析

      remove方法有2个,传入的参数分别是int和Object

    public E remove(int index) {
        //判断传入的数字是否在合理范围内,即是否小于数组内真实的数据个数
        rangeCheck(index);
    
        modCount++;
        E oldValue = elementData(index);//将要remove的索引位置的元素取出
        //将内部数组中空出来的那个位置之后的元素移动到前边去
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 将最后一位置空,size自减  
        elementData[--size] = null; // clear to let GC do its work
    
        return oldValue; // 返回移除的那个数据
    }    
    
    //另一个重载方法,实质上是一样的 
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        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; // clear to let GC do its work
    }
    
    //判断传入的数字是否在合理范围内
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }    
    
    
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }    

    System.arraycopy()方法

    *5个参数的含义分别是:
    --src  源数组.
    --srcPos  源数组中的起始位置。
    --dest  目标数组。
    --destPos  目标数据中的起始位置。
    --length  要复制的数组元素的数目    
    int[] arr1 = { 0, 1, 2, 3, 4, 5 };  
    int[] arr2 = { 6, 7, 8, 9, 10, 11};  
    // 将arr1的元素复制到arr2中,从arr1的索引位置为3开始,复制长度为1个,到arr2中,arr2从索引为0的位置开始接受复制  
    System.arraycopy(arr1, 3, arr2, 0, 1);  
    // 所以最后结果是--  arr1:{ 0, 1, 2, 3, 4, 5 }   arr2:{ 3, 7, 8, 9, 10, 11}  
    System.out.println(Arrays.toString(arr1));  
    System.out.println(Arrays.toString(arr2));

    整理自《http://blog.csdn.net/zw0283/article/details/51122255》

  • 相关阅读:
    第2天 栈和寄存器
    第1天 工作计划和开端
    贯穿实例(1)
    闹心的变量
    开启懒人模式
    前言
    python基础学习7-网络编程、异常处理、面向对象
    python基础学习6-mongodb、sys、接口开发、操作excel
    python基础学习5-常用函数模块、操作数据库、发邮件、写日志、写excel
    python基础学习4-函数、内置函数、os模块、time模块
  • 原文地址:https://www.cnblogs.com/polestar/p/7155200.html
Copyright © 2020-2023  润新知