Arraylist(JDK1.8)

1. 数据结构

数组(没错就是简单的数组)

2.类的继承关系

public class ArrayList extends AbstractListimplements List, RandomAccess, Cloneable, java.io.Serializable

(ArrayList继承AbstractList抽象父类,实现了List接口(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)。)

3.类的属性

private static final long serialVersionUID = 8683452581122892189L;//版本号

private static  final int DEFAULT_CAPACITY = 10;//默认大10

private static final Object[] EMPTY_ELEMENTDATA = {};//空对象数组

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//元素数组

transient Object[] elementData//实际元素大小默认为零

private int size;//ArrayList的大小(它包含的元素的数量)。

4.构造函数

1. ArrayList(Collectionextends c) {

//参数为集合的构造函数   

elementData = c.toArray();

//转化为数组

if ((size elementData.length) != 0) {

//判断参数是否为空        

if (elementData.getClass() != Object[].class)      //判断是否成功转化为object数组

elementData = Arrays.copyOf(elementDatasize, Object[].class);//不为object数组就进行复制    } else {

this.elementData EMPTY_ELEMENTDATA;

//设置元素数组为空。    }}

 

 

 

2.public ArrayList(int initialCapacity) {

//参数为int的构造函数

if (initialCapacity > 0) {//初始容量大于0

this.elementData=new Object[initialCapacity];    }//初始化元素数组

else if (initialCapacity == 0)//初始容量为0 {this.elementData EMPTY_ELEMENTDATA;    }

//设置为空对象数组

else {

throw new IllegalArgumentException("Illegal Capacity: "+                                           initialCapacity);    }}//初始容量小于0,抛出异常.

3.public ArrayList() {

        //无参构造函数,设置元素数组为空 

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

    }

5.重要函数

5.1 add函数

public boolean add(E e) {

ensureCapacityInternal(size + 1);          elementData[size++] = e;

        return true;

    }

//内容很简单就是添加元素,我们重点看一下ensureCapacityInternal()

private void ensureCapacityInternal(int minCapacity)

{        ensureExplicitCapacity

(calculateCapacity(elementData, minCapacity));

}

private void ensureExplicitCapacity(int minCapacity) {

        modCount++;//结构性修改加1

        // overflow-conscious code

        if (minCapacity - elementData.length > 0)

            grow(minCapacity);

    }

private static int calculateCapacity(Object[] elementData, int minCapacity) {

        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

//判断elementData是否为空

            return Math.max(DEFAULT_CAPACITY, minCapacity);

        }//返回较大值

        return minCapacity;

    }

这几个函数有两个作用

1.赋予elementData合适的大小

2.扩容

此时我们看一下扩容函数grow()

private void grow(int minCapacity) {

        // overflow-conscious code

        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);

//新容量大于最大容量

elementData=Arrays.copyOf(elementData, newCapacity);

}

//拷贝扩容

正常情况下会扩容1.5倍,特殊情况下(新扩展数组大小已经达到了最大值)则只取最大值。当我们调用add方法时,实际上的函数调用如下

也就是说我们调用add函数,实际调用的函数为


Arraylist(JDK1.8)_第1张图片

程序调用add,实际上还会进行一系列调用,可能会调用到grow,grow可能会调用hugeCapacity。

接下来用一个例子说明一下

List lists = new ArrayList();

lists.add(9);


Arraylist(JDK1.8)_第2张图片

我们可以看到,在add方法之前开始elementData = {};调用add方法时会继续调用,直至grow,最后elementData的大小变为10,之后再返回到add函数,把9放在elementData[0]中.

5.2 set函数

  public E set(int index, E element) {

        rangeCheck(index);

//检查索引是否合法(有没有超出范围)

        E oldValue = elementData(index);

    //旧值

        elementData[index] = element;

    //新值

        return oldValue;

}

[if !supportLists]5.2 [endif]indexOf函数

public int indexOf(Object o) {

        if (o == null) {

            for (int i = 0; i < size; i++)//遍历数组,找到第一个为空的元素,返回下标

                if (elementData[i]==null)

                    return i;

        } else {

            for (int i = 0; i < size; i++)//遍历数组,找到第一个和指定元素相等的元素,返回下标

              if (o.equals(elementData[i]))

                    return i;

        }

        return -1; }

5.3 get函数

public E get(int index) {

        rangeCheck(index);//判断索引是否合法

        return elementData(index);

}

E elementData(int index) {

        return (E) elementData[index];

}//返回值都经过了向下转型onject-E。

5.4 remove函数

public E remove(int index) {

        rangeCheck(index);//检查索引是否合法

        modCount++;

        E oldValue = elementData(index);

        int numMoved = size - index - 1;//需要移动的元素的个数

        if (numMoved > 0)

            System.arraycopy(elementData, index+1, elementData, index,

                             numMoved);

        elementData[--size] = null; 赋值为空,有利于进行GC

        return oldValue;

    }

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

6.fail-fast的原理

1. fail-fast 机制,即快速失败机制,是java集合(Collection)中的一种错误检测机制。当在迭代集合的过程中该集合在结构上发生改变的时候,就有可能会发生fail-fast,即抛出ConcurrentModificationException异常。fail-fast机制并不保证在不同步的修改下一定会抛出异常,它只是尽最大努力去抛出,所以这种机制一般仅用于检测bug。

具体由Iterator的内部类Itr实现

public Iterator iterator() {

return new Itr();

}

private class Itr implements Iterator {

        int cursor;       // index of next element to return

        int lastRet = -1; // index of last element returned; -1 if no such

        int expectedModCount = modCount;

 

        Itr() {}

..............

}

cursor是指集合遍历过程中的即将遍历的元素的索引,lastRet是cursor -1,默认为-1,即不存在上一个时,为-1,它主要用于记录刚刚遍历过的元素的索引。expectedModCount这个就是fail-fast判断的关键变量了,它初始值就为ArrayList中的modCount。(modCount是抽象类AbstractList中的变量,默认为0,而ArrayList 继承了AbstractList ,所以也有这个变量,modCount用于记录集合操作过程中作的修改次数,与size还是有区别的,并不一定等于size)迭代器迭代结束的标志就是hasNext()返回false,

public boolean hasNext() {

return cursor !=size;

}

而该方法就是用cursor游标和size(集合中的元素数目)进行对比,当cursor等于size时,表示已经遍历完成。在ArrayList进行add,remove,clear等涉及到修改集合中的元素个数的操作时,modCount就会发生改变(modCount ++),所以当另一个线程(并发修改)或者同一个线程遍历过程中,调用相关方法使集合的个数发生改变,就会使modCount发生变化,这样在checkForComodification方法中就会抛出ConcurrentModificationException异常。

6.1解决方案

1.使用Arraylist迭代器的remove(其中没有modCount ++)。

public void remove() {

if (lastRet <0)

throw new IllegalStateException();

checkForComodification();

try {

ArrayList.this.remove(lastRet);

cursor =lastRet;

lastRet = -1;

expectedModCount =modCount;

}catch (IndexOutOfBoundsException ex) {

throw new ConcurrentModificationException();

}

}

2.使用java并发包(java.util.concurrent)中的类来代替。

你可能感兴趣的:(Arraylist(JDK1.8))