jdk1.7 ArrayList源码浅析

参考: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);
时间: 2024-08-28 06:57:50

jdk1.7 ArrayList源码浅析的相关文章

ArrayList源码浅析(jdk1.8)

ArrayList的实质就是动态数组.所以可以通过下标准确的找到目标元素,因此查找的效率高.但是添加或删除元素会涉及到大量元素的位置移动,所以效率低. 一.构造方法 ArrayList提供了3个构造方法 1.无参的,就是把表示集合的数组赋值为空. public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 2.带初始化大小的构造方法,就是new一个指定大小的数组赋值给表示集合的数组. public Ar

java ArrayList 源码浅析

学习的东西越多就会发现自己越无知,最近看各种大牛的博客之类,深觉自己的无知啊,瀑布汗...摆正心态,慢慢学习,希望勤能补拙了. ArrayList算是Java集合框架中相对简单的一个了,学习数据结构的时候很多人也会选择去自己实现一个类似功能的数组的线性存储,其实ArrayList也是如此,只是其开发人员写的更加正规一些,下面就看下源码去看下他们的思路. 1. 定义 public class ArrayList<E> extends AbstractList<E> implement

ArrayList源码浅析

这里只理解主要的常用方法: 1 public class ArrayList<E> extends AbstractList<E> 2 implements List<E>, RandomAccess, Cloneable, java.io.Serializable 3 { 4 private static final long serialVersionUID = 8683452581122892189L; 5 6 /** 7 * 默认的初始化数组容量为10 8 */

jdk1.8.0_45源码解读——ArrayList的实现

jdk1.8.0_45源码解读——ArrayList的实现 一.ArrayList概述 ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小. 每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小.它总是至少等于列表的大小.随着向ArrayList中不断添加元素,其容量也自动增长.自动增长会带来数据向新数组的重新拷贝,因此,如果可预

给jdk写注释系列之jdk1.6容器(1):ArrayList源码解析

原文出自吞噬天地,链接整理自ImportNew 给jdk写注释系列之jdk1.6容器(2):LinkedList源码解析 给jdk写注释系列之jdk1.6容器(3):Iterator设计模式 给jdk写注释系列之jdk1.6容器(4)-HashMap源码解析 给jdk写注释系列之jdk1.6容器(5)-LinkedHashMap源码解析 给jdk写注释系列之jdk1.6容器(6)-HashSet源码解析&Map迭代器 给jdk写注释系列之jdk1.6容器(1):ArrayList源码解析 工作中

ArrayList源码解读(jdk1.8)

概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayList.先对ArrayList有个整体认识,再学习它的源码,最后再通过例子来学习如何使用它.内容包括:第1部分 ArrayList简介第2部分 ArrayList数据结构第3部分 ArrayList源码解析(基于JDK1.8)第4部分 ArrayList遍历方式 第1部分 ArrayList介绍

java并发:jdk1.8中ConcurrentHashMap源码浅析

ConcurrentHashMap是线程安全的.可以在多线程中对ConcurrentHashMap进行操作. 在jdk1.7中,使用的是锁分段技术Segment.数据结构是数组+链表. 对比jdk1.7,在jdk1.8中,ConcurrentHashMap主要使用了CAS(compareAndSwap).volatile.synchronized锁. 跟jdk1.8中的HashMap一样,数据结构是数组+链表+红黑树.当链表长度过长时,会转变为红黑树. jdk1.8的HashMap源码浅析,见

ArrayList源码分析--jdk1.8

ArrayList概述   1. ArrayList是可以动态扩容和动态删除冗余容量的索引序列,基于数组实现的集合.  2. ArrayList支持随机访问.克隆.序列化,元素有序且可以重复.  3. ArrayList初始默认长度10,使用Object[]存储各种数据类型. ArrayList数据结构   数据结构是集合的精华所在,数据结构往往也限制了集合的作用和侧重点,了解各种数据结构是我们分析源码的必经之路.  ArrayList的数据结构如下: ArrayList源码分析 /* * 用数

Java集合干货系列-(一)ArrayList源码解析

前言 今天来介绍下ArrayList,在集合框架整体框架一章中,我们介绍了List接口,ArrayList继承了AbstractList,实现了List.ArrayList在工作中经常用到,所以要弄懂这个类是极其重要的.构造图如下:蓝色线条:继承绿色线条:接口实现 正文 ArrayList简介 ArrayList定义 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomA