ArrayList PK LinkedList

Posted javaurl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList PK LinkedList相关的知识,希望对你有一定的参考价值。

? ArrayList和LinkedList,这两个集合大家都不陌生.尤其是ArrayList,可以说是日常开发中用的最多的容器了.而且这两个集合的知识点几乎可以说面试必问的.

ArrayList

? ArrayList是List接口的一个实现类,底层是基于数组实现的存储结构,数据都是存放到一个数组中.

所以ArrayList的优缺点与数组大致相同,区别是ArrayList可以动态扩容

优点 缺点
随机访问性强 插入和删除效率低
查找效率高 可能浪费内存
支持动态扩展

ArrayList源码分析

ArrayList的属性

	 /**
     * 默认初始容量大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空数组(用于空实例)。
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

 	/**
     * 用于默认大小空实例的共享空数组实例。我们把它从EMPTY_ELEMENTDATA数组中区分出来,以	 * 	知道在添加第一个元素时容量需要增加多少。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 保存ArrayList数据的数组
     * 将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化。
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * ArrayList 所包含的元素个数
     */
    private int size;

ArrayList的构造方法

	 /**
     * 带初始容量参数的构造函数。(用户自己指定容量)
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //创建initialCapacity大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //创建空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     *	默认构造函数,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为0.初始化为10,	  
     *	也就是说初始其实是空数组 当添加第一个元素的时候数组容量才变成10
     *	看扩容过程的ensurecapacityinternal()方法!
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

ArrayList的扩容

	public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)  
        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }
 
   //得到最小扩容量
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
              //若是无参构造时,用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组
              // 获取默认的容量和传入参数的较大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
             //否则直接去判断是否需要扩容
        ensureExplicitCapacity(minCapacity);
    }
 
  //判断是否需要扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        if (minCapacity - elementData.length > 0)
            //调用grow方法进行扩容,调用此方法代表已经开始扩容了
            grow(minCapacity);
    }
 
    /**
     * 最大数组大小
     * 数组作为一个对象,需要一定的内存存储对象头信息,对象头信息最大占用内存不可超过8字节。
     */
//https://stackoverflow.com/questions/35756277/why-the-maximum-array-size-of-arraylist-is-integer-max-value-8
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 
    /**
     * ArrayList扩容的核心方法。
     */
    private void grow(int minCapacity) {
        // oldCapacity为旧容量,newCapacity为新容量
        int oldCapacity = elementData.length;
        //将oldCapacity 右移一位,其效果相当于oldCapacity /2,
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //再检查新容量是否超出了ArrayList所定义的最大容量,
        //若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE,
        //如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。
        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);
    }
    //比较minCapacity和 MAX_ARRAY_SIZE
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

序列化

/**
 * 每次序列化的时候调用这个方法,先调用defaultWriteObject()方法序列化ArrayList
 * 中的非transient元素,elementData这个数组对象不去序列化它,而是遍历elementData,
 * 只序列化数组里面有数据的元素,这样一来,不仅提高了序列化的效率,还减少了空间的开销。
 *            
 */
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

LinkedList

? LinkedList 是基于双向链表实现的,不需要指定初始容量,链表中任何一个存储单元都可以通过向前或者向后的指针获取到前面或者后面的存储单元.所以LinkedList 的优缺点与链表基本相同

优点 缺点
插入删除速度快 不能随机查找
内容利用率高 必须从第一个开始遍历,查找效率低
大小没有固定,扩展和灵活

LinkedList 源码分析

技术图片

/**
*	内部私有类Node
*/
private static class Node<E> {
    //节点值
    E item;
    //后继节点
    Node<E> next;
    //前驱节点
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

哪个更占空间?

? 一般情况下,LinkedList的占用空间更大,因为每个节点要维护指向前后地址的两个节点

? 如果刚好数据量超过ArrayList默认的临时值时,ArrayList占用的空间也是不小的,因为扩容的原因会浪费将近原来数组一半的容量.

以上是关于ArrayList PK LinkedList的主要内容,如果未能解决你的问题,请参考以下文章

优雅代码13-linkedList插入真的比arrayList快么

从源代码来理解ArrayList和LinkedList差别

ArrayList与LinkedList的区别 ?

ArrayList与LinkedList的区别 ?

ArrayList和LinkedList介绍

何时在 ArrayList 或 LinkedList 上使用 GlueList? [复制]