Java面试之Java集合2——ArrayList与LinkedList的区别

ArrayList与LinkedList的区别

  ArrayList LinkedList
是否保证线程安全 不保证线程安全 不保证线程安全
底层数据结构 动态Object数组 双向链表(JDK1.7之前为循环链表,之后取消了循环)
优点 查找性能好,适用于查找元素,即支持快速随机访问 对插入或删除操作来说,性能更高
缺点 插入或删除一个元素需要移动大量元素,代价较高 查找元素不方便,要遍历整个链表,即不支持快速随机访问
扩容 默认数组大小是10,在检测到需要扩容后,会扩容到原来大小的1.5倍 是链表结构,不存在扩容问题
内存空间占用 主要是List列表末尾会预留一定的容量空间,因为是数组 主要是每个元素都要花费更多的空间存储直接前驱、数据和直接后继

ArrayList

  • 底层采用动态数组实现

  • 查找性能好,因为数组可以直接通过下标获取对应位置的元素。

  • 插入或删除一个元素比较麻烦,因为是在数组中删除或插入一个元素,那么就需要移动大量的元素来填充删除元素的位置或为要插入的元素腾出位置。

  • ArrayList的默认数组大小是10,如果发现需要扩容,会扩容1.5倍。

获取扩容后数组大小的源码如下:

该段代码注释如下:

    private int newCapacity(int minCapacity) {
        // 扩容前数组的长度,初始默认值为10
        int oldCapacity = elementData.length;
        // 扩容后数组的长度,计算为oldCapacity加上其oldCapacity带符号右移后的值
        // >>表示带符号右移,相当于除以2,而10/2=5,因此newCapacity第一次扩容后等于15,即扩容了1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

LinkedList

  • LinkedList底层采用双向链表实现,下面以add()方法源码为例:

学过数据结构的应该很容易看懂上面的代码,就是链表添加元素的代码。

这是链表结点的定义代码:

  • 链表插入或删除元素比较方便,直接修改指针即可,但查找某个元素可能不方便。

RandomAccess接口

在ArrayList的实现接口中,发现了一个RandomAccess接口,见文知意是“随机访问”的意思。

但打开接口源码,发现没有如何抽象方法

所以说,该接口知识一个标识,标识实现了这个接口的类具有随机访问的能力。

但LinkedList没有实现该接口,因为底层数据结构不一样,ArrayList底层是数组,支持随机访问,而LinkedList底层是双向链表,需要遍历整个链表才能访问到指定的元素。

注意:RandomAccess接口只是一个标识,如一个标签,表明实现了该接口的类具有随机访问的能力,但不是说实现该接口才有随机访问能力的,要注意因果关系。

ArrayList与Vector的区别

  ArrayList Vector
是否保证线程安全 不保证线程安全 保证线程安全
底层数据结构 动态Object数组 动态Object数组
  • ArrayList 是  List 的主要实现类,底层使⽤  Object[ ] 存储,适⽤于频繁的查找⼯作,线程不安全 ;
  • Vector 是  List 的古⽼实现类,底层使⽤ Object[ ] 存储,线程安全的。

因为Vector使用了同步锁,所以线程是安全的。

ArrayList的扩容

  • ArrayList为什么会扩容?

因为ArrayList的底层数据结构是Object数组,而我们知道数组必须指定默认大小,而ArrayList的底层数组的大小为10,也就是说如果没有扩容只能存放十个元素。

  • ArrayList什么时候发生扩容?

查看源码如下:

发现当ArrayList的长度等于数组的长度的时候就会发生扩容,即调用grow()方法。(注意:我所使用的JDK版本是9,可能有所差别)

  • ArrayList是如何扩容的?

grow()方法的源码如下,有两个重载方法:

其中传入的minCapacity参数指的是设置的最小尺寸,即扩容后数组的最小长度,即原长度加1。

而Arrays.copy()方法是将一个数组复制到另一个数组中。

Arrays的copyOf()方法传回的数组是新的数组对象,改变传回数组中的元素值,不会影响原来的数组。copyOf()的第二个参数指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值

其中newCapacity()方法返回的是新数组的长度。

注:capacity的英文意思有容量、容积的含义

下面查看newCapacity(minCapacity))函数的代码:

该段代码最重要的就是框中的两行,下面是注释说明:

    private int newCapacity(int minCapacity) {
        // 扩容前数组的长度,初始默认值为10
        int oldCapacity = elementData.length;
        // 扩容后数组的长度,计算为oldCapacity加上其oldCapacity带符号右移后的值
        // >>表示带符号右移,相当于除以2,而10/2=5,因此newCapacity第一次扩容后等于15,即扩容了1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

总结:扩容的实现就是将原数组复制到一个新的数组中,数组的大小是原数组的1.5倍。

猜你喜欢

转载自blog.csdn.net/cnds123321/article/details/113664656