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 <? extends E> 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函数,实际调用的函数为


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

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

List lists = new ArrayList();

lists.add(9);


我们可以看到,在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)中的类来代替。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343