zoukankan      html  css  js  c++  java
  • 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》

  • 相关阅读:
    使用 Spring data redis 结合 Spring cache 缓存数据配置
    Spring Web Flow 笔记
    Linux 定时实行一次任务命令
    css js 优化工具
    arch Failed to load module "intel"
    go 冒泡排序
    go (break goto continue)
    VirtualBox,Kernel driver not installed (rc=-1908)
    go运算符
    go iota
  • 原文地址:https://www.cnblogs.com/polestar/p/7155200.html
Copyright © 2011-2022 走看看