zoukankan      html  css  js  c++  java
  • ArrayList和LinkedList 内部结构分析(一)

       在了解集合的时候,都会学到不同集合之间的区别,比如ArrayList和LinkedList,其中ArrayList是类似于数组结构的,查询比较快速。而LinkedList则是链表结构,在插入和删除的时候效率较高。

      通过研究源码,可以更深入的了解其内部实现,真的是ArrayList所有查询都快么?  真的是 LinkedList所有的插入和删除都更效率么?

      废话不多说,上代码!

      一、先摘抄一些ArrayList的代码吧,这里只分析一些关键的方法:

    1、ArrayList的内部结构是怎么样的?

    1public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
       private static final Object[] EMPTY_ELEMENTDATA = {};
    //这就是ArrayList集合内部结构,是一个object数组
    private transient Object[] elementData;
      //集合里面实际有值的长度
    private int size;   //构造函数,入参为初始化list长度 public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; }
      //默认构造函数,初始化数组大小为0
    public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }
      //入参为集合将集合类型数据为数组,list长度为数组长度
    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); } }

    2、对ArrayList中元素进行操作的实现?

    get(index):其实就是根据index,直接获取对应数组下角标的值

    \根据下角标获取元素
    public E get(int index) {
        \检查下角标范围
            rangeCheck(index);
          \根据下角标返回数组元素
            return elementData(index);
        }
    
    \下角标范围必须小于数组有值的实际长度
    private void rangeCheck(int index) {
            if (index >= size)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }

    \获取下角标元素
    E elementData(int index) {
    return (E) elementData[index];
    }
    set(int index, E element):这里需要稍微注意一下的是,set()方法返回的结果是对应index值的原始值。
    public E set(int index, E element) {
           \检查下角标
            rangeCheck(index);
            \原index 对应的值
            E oldValue = elementData(index);
           \设置新值
            elementData[index] = element;
          \返回原值
            return oldValue;
        }

    add(e):当添加一个元素时候,默认添加到list最后,并且对容器大小是否够容纳进行check,如果容器大小不够,则将原来容器

    增大到1.5倍,在这种情况还不够的情况,则增加到最小要求容器长度。

    \添加一个元素
    public boolean add(E e) {
          \确保当前elementData容量是否足够
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    
    \确保当前elementData容量是否足够,入参:最小需要长度
    private void ensureCapacityInternal(int minCapacity) {
          
            if (elementData == EMPTY_ELEMENTDATA) {
               \如果elementData为空,则选择默认长度DEFAULT_CAPACITY或者最小长度
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            //
            ensureExplicitCapacity(minCapacity);
        }
    
    
    private void ensureExplicitCapacity(int minCapacity) {
           //记录集合修改次数
            modCount++;
           //如果现有容器长度不够用则添加容器长度
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    
    
    private void grow(int minCapacity) {
            \原始容器长度
            int oldCapacity = elementData.length;
           \新容器长度为原来长度的1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
    
           \1.5倍的容器长度还比最小要求长度小
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
    
            \判断新容器长度超过最大限制
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            
            //将原来数组的值copy新数组中去, ArrayList的引用指向新数组
         //这儿会新创建数组,如果数据量很大,重复的创建的数组,那么还是会影   响效率,
            elementData = Arrays.copyOf(elementData, newCapacity);
        }

    在最后需要通过Arrays.copyOf(elementData, newCapacity),复制整个数组的方式来实现添加数据,由此看来add(e)在需要扩容的时候添加元素,

    效率是比较低的。

    add(int index, E element):在指定位置后插入元素
     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++;
        }

    可以看到,在指定位置插入元素,首先需要进行容器长度check,如果不足,会进行一个数组copy,接下来还

    将当前位于该位置的元素以及所有后续元素右移一个位置。效率更加低了!


    remove(int index):
    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; // clear to let GC do its work
    
            return oldValue;
        }

    首先是检查范围,修改modCount,保留将要被移除的元素,将移除位置之后的元素向前挪动一个位置,将list末尾元素置空(null),返回被移除的元素,

    移除一个元素,除了最后一位,别的都需要将之后的元素前移一位,可见效率很低!

     

    contains(o):直接通过遍历数组,来判断,说明遍历查找效率较高

    //是否包含某元素,遍历整个list 
    public boolean contains(Object o) {
            return indexOf(o) >= 0;
        }
    
    
    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;
        }

    ------------------------------------

    上面的源码能看出来,对于ArrayList是如何动态的控制长度,在插入元素、新增元素、删除元素的时候,效率是比较低的。而遍历查找元素,效率较高!

    LinkedList,见下一篇吧,太困了!

    23:57:43

    2016/03/23  夜  杭州

     
    
    
  • 相关阅读:
    Mac php使用gd库出错 Call to undefined function imagettftext()
    centos 使用 locate
    Mac HomeBrew 安装 mysql
    zsh 命令提示符 PROMPT
    新的开始
    Java 面试题分析
    Java NIO Show All Files
    正确使用 Volatile 变量
    面试题整理 2017
    有10阶梯, 每次走1,2 or 3 阶,有多少种方式???
  • 原文地址:https://www.cnblogs.com/guoliangxie/p/5306318.html
Copyright © 2011-2022 走看看