码迷,mamicode.com
首页 > 其他好文 > 详细

jdk1.7 ArrayList源码浅析

时间:2017-06-16 11:33:37      阅读:240      评论:0      收藏:0      [点我收藏+]

标签:安全   nat   str   扩容   来讲   基础   pac   完全   三元   

参考:http://www.cnblogs.com/xrq730/p/4989451.html(借鉴的有点多,哈哈)

  首先介绍ArrayList的特性:

     1、允许元素为空、允许重复元素

     2、有序,即插入顺序和访问顺序一致

     3、非同步

ArrayList实现原理为动态数组

首先看构造方法:

  public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

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

第一个构造方法为指定大小进行初始化,第二个private static final Object[] EMPTY_ELEMENTDATA = {}; 相当于构造一个空的数组。

主要分析add()方法,其他应该可以类比。

常用的方法为add(E e)和add(int index , E element)

add()的方法简单来讲就是:

1、确保数组容量,其中就包括是否扩容然后将数组整个复制。同时modCount(用来记录集合被修改的次数)值加一 

2、将元素添加到数组中

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

ensureCapacityInternal从字面上理解就是确保内部容量

   private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
DEFAULT_CAPACITY等于10。AarryList在用无参的构造方法创建时,是没有为里面的数组成员分配空间的,
只有在进行add操作后才会分配空间,并会和10比较。再调用ensureExplicitCapacity来分配具体的空间,
并且在第一次add时会直接分配10个空间。
  private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

在add操作时,若集合所需的最小容量大于当前数组长度(数组长度计算包括了为null的元素),就需要扩容。

 private void grow(int minCapacity) {
       // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }

关于扩容:

1、如果一次性扩容扩得太大,必然造成内存空间的浪费。

2、如果一次性扩容扩得不够,那么下一次扩容的操作必然比较快地会到来,这会降低程序运行效率,要知道扩容还是比价耗费性能的一个操作

 

newCapacity代表具体扩容成多大。第4行就相当于 int newCapacity=oldCapacity+ordCapacity/2(大约1.5倍)。用移位操作可以在数组容量大时节省点时间。

第一个if只在初次分配10个空间时执行。第二个if,hugeCapacity方法主要就是个三元运算符,MAX_ARRAY_SIZE=整型最大值-8(这个设计不懂)

  private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

疑问:为什么不直接赋值为Integer.MAX_VALUE。

关于Arrays.copyOf方法其实就是调用的System.arraycopy,而后者又是加了native关键字,底层应该是c++之类的语言去实现整个数组的复制。

设计上面,Arrays.copyOf分了一个泛型方法和一个专门处理基本类型如int、float的方法。

在明确数组大小时可以手动为其扩容,来减少反复的扩容。

 public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != EMPTY_ELEMENTDATA)
            // any size if real element table
            ? 0
            // larger than default for empty table. It‘s already supposed to be
            // at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

 

add(int index, E element)方法:

1、判断插入位置是否合法

2、确保数组容量,其中就包括是否扩容然后将数组整个复制。同时modCount(用来记录集合被修改的次数)值加一。

3、将插入位置之后的元素复制到其加1之后的位置去(说的有点绕啊o(╯□╰)o),也就是从插入位置整体向后移一个单位。

4、将元素放到指定位置

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

ArrayList的优缺点

从上面的几个过程总结一下ArrayList的优缺点。ArrayList的优点如下:

1、ArrayList底层以数组实现,是一种随机访问模式,再加上它实现了RandomAccess接口,因此查找也就是get的时候非常快

2、ArrayList在顺序添加一个元素的时候非常方便,只是往数组里面添加了一个元素而已。

不过ArrayList的缺点也十分明显:

1、删除元素的时候,涉及到一次元素复制,如果要复制的元素很多,那么就会比较耗费性能

2、插入元素的时候,涉及到一次元素复制,如果要复制的元素很多,那么就会比较耗费性能

因此,ArrayList比较适合顺序添加、随机访问的场景

ArrayList和Vector的区别

ArrayList是线程非安全的,这很明显,因为ArrayList中所有的方法都不是同步的,在并发下一定会出现线程安全问题。那么我们想要使用ArrayList并且让它线程安全怎么办?一个方法是用Collections.synchronizedList方法把你的ArrayList变成一个线程安全的List,比如:

List<String> synchronizedList = Collections.synchronizedList(list);
synchronizedList.add("aaa");
synchronizedList.add("bbb");
for (int i = 0; i < synchronizedList.size(); i++)
{
    System.out.println(synchronizedList.get(i));
}

另一个方法就是Vector,它是ArrayList的线程安全版本,其实现90%和ArrayList都完全一样,区别在于:

1、Vector是线程安全的,ArrayList是线程非安全的

2、Vector可以指定增长因子,如果该增长因子指定了,那么扩容的时候会每次新的数组大小会在原数组的大小基础上加上增长因子;如果不指定增长因子,那么就给原数组大小*2,源代码是这样的:

int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                 capacityIncrement : oldCapacity);

 

jdk1.7 ArrayList源码浅析

标签:安全   nat   str   扩容   来讲   基础   pac   完全   三元   

原文地址:http://www.cnblogs.com/zuochengsi-9/p/7019746.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!