Java集合源码分析(ArrayList)

      ArrayList 的底层是数组队列,相当于动态数组可以进行动态的扩容。它继承于 AbstractList,实现了 List, RandomAccess, Cloneable, java.io.Serializable 这些接口。插入删除元素的时间复杂度为O(n),求表长以及增加元素,取第 i 元素的时间复杂度为O(1)ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。和 Vector 不同,ArrayList 中的操作不是线程安全的!所以,建议在单线程中才使用 ArrayList,而在多线程中可以选择 Vector 或者使用。

ArrayList的默认参数:

// ArrayList的默认长度是多少
private static final int DEFAULT_CAPACITY = 10;
// ArrayList的默认的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 用于默认大小空实例的共享空数组实例。我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 保存ArrayList数据的数组
transient Object[] elementData; // non-private to simplify nested class access
// ArrayList 所包含的元素个数
private int size;
// 要分配的最大数组大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ArrayList构造函数:

/**
 * 默认无参构造函数
 * DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为0.初始化为10,也就是说初始其实是空数组 当添加第一个元素的时候数组容量才变成10
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * 带初始容量参数的构造函数(用户可以在创建ArrayList对象时自己指定集合的初始大小)
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        //如果传入的参数大于0,创建initialCapacity大小的数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        //如果传入的参数等于0,创建空数组
        this.elementData = EMPTY_ELEMENTDATA;
        
    } else {
        //其他情况,抛出异常
        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
}

/**
 * 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
 */
public ArrayList(Collection<? extends E> c) {
    //将指定集合转换为数组
    elementData = c.toArray();
    //如果elementData数组的长度不为0
    if ((size = elementData.length) != 0) {
        // 如果elementData不是Object类型数据(c.toArray可能返回的不是Object类型的数组所以加上下面的语句用于判断)      
        if (elementData.getClass() != Object[].class)
            //将原来不是Object类型的elementData数组的内容,赋值给新的Object类型的elementData数组
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // 其他情况,用空数组代替
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

 

ArrayList的扩容机制:

//下面是ArrayList的扩容机制,ArrayList的扩容机制提高了性能,如果每次只扩充一个,那么频繁的插入会导致频繁的拷贝,降低性能,而ArrayList的扩容机制避免了这种情况。
/**
 * 如有必要,增加此ArrayList实例的容量,以确保它至少能容纳元素的数量
 *
 * @param minCapacity 所需的最小容量
 */
public void ensureCapacity(int minCapacity) {
    //如果是true,minExpand的值为0,如果是false,minExpand的值为10
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;
    //如果最小容量大于已有的最大容量
    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}

//得到最小扩容量
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 获取“默认的容量”和“传入参数”两者之间的最大值
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

//判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        //调用grow方法进行扩容,调用此方法代表已经开始扩容了
        grow(minCapacity);
}

/**
 * 要分配的最大数组大小
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
 * ArrayList扩容的核心方法。
 */
private void grow(int minCapacity) {
    // oldCapacity为旧容量,newCapacity为新容量
    int oldCapacity = elementData.length;
    //将oldCapacity 右移一位,其效果相当于oldCapacity /2,
    //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //再检查新容量是否超出了ArrayList所定义的最大容量,
    //若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE,
    //如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。
    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);
}

//比较minCapacity和 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;
}

 

索引检查:

/**
 * 检查给定的索引是否在范围内。
 */
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

/**
 * 判断下标是否越界
 */
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

 

System.arraycopy:

/**
 * src:源数组
 * srcPos:源数组要复制的起始位置
 * dest:目的数组
 * destPos:目的数组放置的起始位置
 * length:复制的长度
 *  注意:src 和 dest都必须是同类型或者可以进行转换类型的数组
 */
public static native void arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length);

 

ArrayList的核心方法分析:

add方法(4种重载实现)--新增:

  1. add(E);//默认直接在末尾添加元素
/**
 * 新增元素
 */
public boolean add(E e) {
    //赋值初始长度  或者扩容,新增元素,当前实际size+1的长度
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //添加元素
        elementData[size++] = e;
        return true;
}
    /**
     * 确保elemenData数组有合适的大小
     * 如果元素为空,则复制长度默认为10 或者更大
    * @author jiaxiaoxian
    * @date 2019年2月12日 
     */
    private void ensureCapacityInternal(int minCapacity) {
            if (elementData == EMPTY_ELEMENTDATA) {//如果数组为空,则从size+1的值和默认值10中取最大的
                    minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            ensureExplicitCapacity(minCapacity);
    }
/**
* 确保elemenData数组有合适的大小
* @author jiaxiaoxian
* @date 2019年2月12日 
* 如果长度大于元素长度则扩容
 */
private void ensureExplicitCapacity(int minCapacity) {
    //记录修改次数,迭代中不一致会触发fail-fast机制,因此在遍历中删除元素的正确做法应该是使用Iterator.remove()
    modCount++;
    if (minCapacity - elementData.length > 0)
        grow(minCapacity); //扩容
}
/**
 * 扩容
 */
private void grow(int minCapacity) {
    int oldCapacity = elementData.length; // 旧容量
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 新容量为旧容量的1.5倍
    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);
}
    //如果小于0 就报错,如果大于最大值 则取最大值
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

2.add(int index, E element);//给指定下标,添加元素

/**
 * 在此列表中的指定位置插入指定的元素。
 * 先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大;
 * 再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。
 */
public void add(int index, E element) {
    //判断下标是否越界
    rangeCheckForAdd(index);
    //赋值初始长度  或者扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //将源数组中从index位置开始后的size-index个元素统一后移一位
    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));
}
/**
 *     src:源数组
 *   srcPos:源数组要复制的起始位置
 *   dest:目的数组
 *   destPos:目的数组放置的起始位置
 *   length:复制的长度
 *   注意:src 和 dest都必须是同类型或者可以进行转换类型的数组
 */
public static native void arraycopy(Object src,  int  srcPos,
                                    Object dest, int destPos,
                                    int length);

 

3.addAll(Collection<? extends E> c);//添加Collection类型元素

/**
 * 按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此列表的尾部
 */
public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    //将数组a[0,...,numNew-1]复制到数组elementData[size,...,size+numNew-1]
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

 

4.addAll(int index, Collection<? extends E> c);//指定位置,添加Collection类型元素

/**
 * 从指定的位置开始,将指定collection中的所有元素插入到此列表中,新元素的顺序为指定collection的迭代器所返回的元素顺序
 */
public boolean addAll(int index, Collection<? extends E> c) {
    //判断下标是否越界
    rangeCheckForAdd(index);
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    int numMoved = size - index;
    //先将数组elementData[index,...,index+numMoved-1]复制到elementData[index+numMoved,...,index+2*numMoved-1]
    //即,将源数组中从index位置开始的后numMoved个元素统一后移numNew位
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

总结:

   正常情况下会扩容1.5倍,特殊情况下(新扩展数组大小已经达到了最大值)则只取最大值。

 

remove方法(4种重载实现)--删:

  1. remove(int index); //根据指定下标 删除元素     
/**
 * 根据指定下标 删除元素
 */
public E remove(int index) {
    //判断索引是否越界
    rangeCheck(index);
    modCount++;
    //获取旧元素
    E oldValue = elementData(index);
    //将数组elementData中index位置之后的所有元素向前移一位
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //将原数组最后一个位置置为null,由GC清理
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}       

 

2.remove(Object o); //根据指定元素 删除元素 

/**
 * 移除ArrayList中首次出现的指定元素(如果存在),ArrayList中允许存放重复的元素
 */
public boolean remove(Object o) {
    // 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                //私有的移除方法,跳过index参数的边界检查以及不返回任何值
                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++;
    //将数组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
}
 /**
 * 清空ArrayList,将全部的元素设为null,等待垃圾回收将这个给回收掉,所以叫clear
 */
public void clear() {
    modCount++;
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    size = 0;
}

 

removeAll(Collection<?> c); //删除包含在指定容器c中的所有元素 

/**
 * 删除ArrayList中包含在指定容器c中的所有元素
 */
public boolean removeAll(Collection<?> c) {
    //检查指定的对象c是否为空
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}
 /**
 * 删除全部
* @author jiaxiaoxian
* @date 2019年2月12日 
 */
private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0; //读写双指针
    boolean modified = false;
    try {
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement) //判断指定容器c中是否含有elementData[r]元素
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

removeIf(Predicate<? super E> filter); //按照一定规则过滤(删除)集合中的元素 

/**
 * 按照一定规则过滤(删除)集合中的元素
 * 如:idList.removeIf(id -> id == nul);
    *   去掉 List idList 集合中id 为 null 的
 * @param filter
 * @return
 */
@Override
public boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    // figure out which elements are to be removed
    // any exception thrown from the filter predicate at this stage
    // will leave the collection unmodified
    int removeCount = 0;
    final BitSet removeSet = new BitSet(size);
    final int expectedModCount = modCount;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        @SuppressWarnings("unchecked")
        final E element = (E) elementData[i];
        if (filter.test(element)) {
            removeSet.set(i);
            removeCount++;
        }
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }

    // shift surviving elements left over the spaces left by removed elements
    final boolean anyToRemove = removeCount > 0;
    if (anyToRemove) {
        final int newSize = size - removeCount;
        for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
            i = removeSet.nextClearBit(i);
            elementData[j] = elementData[i];
        }
        for (int k=newSize; k < size; k++) {
            elementData[k] = null;  // Let gc do its work
        }
        this.size = newSize;
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

    return anyToRemove;
}

总结:

   remove函数用户移除指定下标的元素,此时会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后将整个数组不被使用时,会被GC,可以作为小的技巧使用。

 

set方法--改:

/**
 * 覆盖指定下标元素
 */
public E set(int index, E element) {
    //判断索引是否越界
    rangeCheck(index);
    //获取旧元素
    E oldValue = elementData(index);
    //覆盖为新元素
    elementData[index] = element;
    //返回旧元素
    return oldValue;
}
/**
 * 判断下标是否越界
 */
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

 

get方法--查:

 /**
 * 返回指定索引的值
 */
public E get(int index) {
    //判断索引是否越界
    rangeCheck(index);
    return elementData(index);
}
/**
* @author jiaxiaoxian
* @date 2019年2月12日 
* 返回下标元素的 值
 */
@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
}

clear:

/**
 * 从列表中删除所有元素。
 */
public void clear() {
    modCount++;

    // 把数组中所有的元素的值设为null
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

ArrayList总结:
1)ArrayList可以存放null,本质是Object[]类型的数组。
2)ArrayList区别于数组的地方在于能够自动扩展大小,其中关键的方法就是gorw()方法。
3)ArrayList由于本质是数组、而LinkedList是以双向链表来保存数据的,所以ArrayList查询块,增删慢,不安全,效率高。LinkedList查询慢,增删快,不安全.

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值