ArrayList 与 LinkedList 的实现和区别

ArrayList 的实现

ArrayList 底层是通过数组实现的,其数据保存在 transient Object[] elementData; List 接口主要特性就是长度可变,数组在创建的时候必须要指定其大小,那么基于数组实现无法直接达到长度可变。

通过构造方法参数 initialCapacity 来创建默认大小的 ArrayList,如果使用无参构造方法或者指定 initialCapacity 为 0 创建对象,则 ArrayList 会在第一次 add 时将容器 capacity初始化为 10 这样的做有几个好处:

  • 延迟初始化数组有益于避免空 List 的空间浪费。
  • 长度为 10 可以有效减少初期频繁的扩容操作。(若默认长度为 0 add 10 次需要扩容的次数比较频繁)

确定 ArrayList 的大小可以避免很多扩容操作,也能有效减少空间/内存浪费,所以在使用 ArrayList 时要尽量确认目标大小。

可以通过trimToSize方法去掉浪费掉的空间

ArrayList 可以通过元素下标直接获得,随机访问时间复杂度O(1) 假设 ArrayList 的大小初始化为 10 不过并未添加任何元素,此时访问任意下标的元素会抛出 IndexOutOfBoundsException 异常,也就是下标越界,可见 ArrayList 是根据实际元素长度来界定是否越界的。

ArrayList 随机删除元素需要将数组内元素进行移位来保证元素连续,时间复杂度为 O(n) 如果移除的是最后一位元素时间复杂度则为 O(1),移除的操作实际上是将指定下标赋值为 null 并更新 size,删除元素并不需要创建新的数组,也不需要进行缩容。其中 fastRemove 的私有方法不会访问元素,而remove需要返回被移除的元素,需要访问数组内元素。

1. 为什么 elementData 修饰为transient
2. ArrayList 是泛型对象为什么 elementData 不使用泛型而使用 Object?
3. ArrayList 是如何达到长度可变的(扩容)?
4. ArrayList 可以无限添加数据吗?
5. ArrayList 可以 add null 吗?
6. modCount的作用是什么?

为什么 elementData 修饰为transient

transient关键字修饰的成员不能参与序列化和反序列化。由于数组实现原因直接序列化可能存在空间浪费,ArrayList 提供了支持序列化和反序列化的两个方法 writeObjectreadObject
工作方式:https://www.jianshu.com/p/14876ef38721

ArrayList 是泛型对象为什么 elementData 不使用泛型而使用 Object?

因为 Java 中不能直接 E[] data = new E[1] 创建所谓的泛型数组,可能是遗留问题?
why-does-arraylist-use-object-instead-of-e-internally
Why does the ArrayList implementation use Object[]?

ArrayList 是如何达到长度可变的?

ArrayList 通过创建一个大于现有数组长度的数组,并将原数组数据按顺序拷贝到新数组变相完成长度可变的。

在调用 add 方法时首先会确认目标大小是否大于数组长度,如果小于直接数组末尾插入 elementData[size++] = e; 注意此处会首先确定数组下标再进行自增;如果目标大小无法满足,将会进行扩容操作,因为数组一旦创建长度就确定了,所以扩容实际就是创建一个新的数组将现有数据拷贝过去,来看一下源码:

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    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.5 倍,int newCapacity = oldCapacity + (oldCapacity >> 1); 也就是扩容 50%

ArrayList 可以无限添加数据吗?

不能,ArrayList 内部是存在长度限制的,在 ArrayList 扩容机制里,如果新的数组长度大于 MAX_ARRAY_SIZE 没有超过 Integer 最大值之前可以继续添加,否则将抛出 OOM 异常。

为什么 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

Why the maximum array size of ArrayList is Integer.MAX_VALUE - 8?

ArrayList 可以 add null

可以。

List<Integer> list = new ArrayList<>();
list.add(null);
list.add(null);

modCount的作用是什么

在操作(添加,删除,排除等) ArrayList 时经常出现 modCount 成员变量,其主要作用是为了检测和判定在使用迭代器迭代过程中的 ArrayList 操作不当引起的迭代器游标异常。
modCount 并非仅检测多线程环境下的异常,个人认为主要还是因为在迭代器场景中错误删除等操作,导致迭代器游标未能按照预期结果进行引入的机制。ArrayList 本身就是非线程安全的,切勿在线程竞争的环境下使用。

单线程环境下,下方代码会抛出 ConcurrentModificationException 异常。

public class TestArrayListIterator {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(10);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==10)
                list.remove(integer);   //注意这个地方
        }
    }
}

arraylist等记录修改次数modCount有什么作用? - wuxinliulei的回答 - 知乎
https://www.zhihu.com/question/24086463/answer/64717159

迭代器提供了安全的删除元素的remove方法。

LinkedList 实现

LinkedList 由双向链表实现,保存了 first 和 last 指针,同时实现了 List 和 Deque 接口。由于链表的特性LinkedList原生支持 List 变长特性,LinkedList 支持变相通过下标访问元素,其实现方式是通过遍历链表访问元素的。LinkedList 每一个元素都需要保存 pre 和 next 指针来维持其链表特性,在内存使用上会有所增加。

LinkedList add操作仅需要在尾部链接元素即可,时间复杂度O(1)
LinkedList remove操作非常快速,在查找到元素只需将前一个元素与后一个元素链接即可

1. LinkedList 长度是无限的吗 ?

LinkedList 长度是无限的吗 ?

查看源码后,LinkedList 内部对长度并没有限制,但是记录容器大小 size 可能会溢出,从而导致相关下标访问操作异常。

    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

LinkedList 和 ArrayList 的区别

  • ArrayList 的变长能力是通过创建更大的数组实现的
  • LinkedList 的变长能力通过链表实现
  • ArrayList 可以初始化容器长度
  • LinkedList 无法初始化容器长度
  • ArrayList 在实现上长度是有限制的
  • LinkedList 在实现上是没有长度限制的,但是无限的长度会导致 int size 溢出,依赖 size 的相关方法可能出现异常。
  • 在 List 接口 int size() 可以看出两者都受限于 int 最大值,List 接口对长度是有限制的。
  • ArrayList 随机查找能力非常强时间复杂度 O(1)
  • LinkedList 随机查找能力相较 ArrayList 弱,其具体算法为不是从头遍历到尾,而是通过下标确定从哪个方向开始遍历 index < (size >> 1) 如果小于 mid 则从头部开始,否则从尾部倒序遍历。
  • ArrayList 的插入删除操作理论上时间复杂度较高

  • LinkedList 的插入删除操作理论性能优于 ArrayList

  • 对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。

  • 根据场景的不同,ArrayList 和 LinkedList 并不完全与理论一致。http://blog.sina.com.cn/s/blog_3df05c220102ycq8.html

  • ArrayList 和 LinkedList 都使用 transient 关键字修饰了容器数据,通过 writeObjectreadObject进行序列化和反序列

  • ArrayList 和 LinkedList 对 Itertor 添加了 modCount 检查支持,防止异常操作。

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