java.util系列源码解读之ArrayList

Java中ArrayList的实现原理: ArrayList内部会维护一个Object的数组对象, 针对ArrayList的任何操作在内部都是通过操作Object这个数组对象的实现的,那么ArrayList是如何实现动态的扩容的呢? 首先ArrayList中有一个成员变量size来记录ArrayList实际存储的数据量大小, 当每次add新的数据时,就会通过这个size和Object数组的length比较,当size + 1>length就会扩容到原数组大小的1.5倍,然后再执行add操作.
效率: 根据索引查找快, 末尾插入快, 中间插入要执行后面位置数据复制, 查找特定对象慢(一样要循环比较)

成员变量讲解

修饰符 变量名 作用
private static final int DEFAULT_CAPACITY 默认的初始化数组存储空间大小
private static final Object[] EMPTY_ELEMENTDATA 空的实例list
private transient Object[] elementData 存储数据的数组对象
private int size list中存储数据的实际容量
protected transient int modCount 继承自AbstractList,记录size变化的次数

构造方法

空的构造方法: 创建一个空的list, 不包含任何数据, list中包含的数组长度也是空的,
此种方式下,当add第一个元素时,才会用DEFAULT_CAPACITY默认容量来重新new一个Object[]

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

构造初始化数组容量的list: 根据initialCapacity参数来实例化一个存储数据的数组

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

从其他Collection构造:

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    size = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)  // 类型转换
        elementData = Arrays.copyOf(elementData, size, Object[].class);
}

方法选讲

public int indexOf(Object o)
返回对象o在list中第一次出现的下标值

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

public int lastIndexOf(Object o)
返回对象o在list中最后一次出现的下标值

public int lastIndexOf(Object o) {
    if (o == null) {
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

public E get(int index)
获取下标index处的对象

public E get(int index) {
    rangeCheck(index);  // 范围检查
    return elementData(index); // elementData[index]
}

public boolean add(E e)
在list末尾添加一个新的元素,同时size执行加1操作 此操作会先检查数组的容量(目的:如果当前数组所有位置都已存放了数据,直接添加就会报错,下标越界,所以要先检查容量是否已满)

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 扩充数组容量, 抛出OutOfMemoryError
    elementData[size++] = e; // 在末尾设置值
    return true;
}

public void add(int index, E element)
在指定下标位置添加元素, 同时size+1操作, 此方法跟add()方法一样

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

public boolean retainAll(Collection<?> c)
从当前list中移除在c中不存在的元素

ArrayList中的迭代器 Iterator 讲解
首先我们要清楚的一点就是迭代器的工作原理是什么? list中的迭代器是根据list的修改次数来实现迭代的,什么意思呢?说白一点就是根据list的继承成员属性modCount来工作的.modCount-list的size发生改变modCount就会加一或者减一,这里要注意的是size的改变时针对的size的改变次数,而不是size的变化长度,也就是说如果size一次增加的数量不管是多少,modCount始终只会加一; 比如add()一次增加一个元素modCount+1, addAll()一次增加多个元素modCount还是+1.这样造成的问题有:
a. 如果我们在用Iterator做循环操作时, 在循环操作中调用了add(), remove()等会改变size值的操作, 都将引起异常抛出(ConcurrentModificationException);
b. 在多线程的环境下, 如果一个线程用Iterator在操作list,而另一个线程在操作同一个list,那么也会引起抛出异常(ConcurrentModificationException).此种情况可以考虑==Vector==;
==补充:在这种情况下使用Vector也是于事无补,虽然在Vector中针对Iterator的next()和remove()都添加了针对当前list的synchronized条件,但始终next()和add()这些并不是一个原子性的操作,所以在Vector中还是会有同样的问题存在.==

ArrayList中有两种Iterator

  1. Itr内部类-iterator()方法, 可执行hasNext, next(), remove()操作,要remove()则必须先执行next();
  2. ListItr内部类-listIterator()方法,多了操作list数据的方法add(), set()方法

ArrayList影响modCount的方法有: add(), remove(),clear(),addAll();也就是这些方法都会影响Iterator迭代器的执行.

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

推荐阅读更多精彩内容