zoukankan      html  css  js  c++  java
  • ArrayList、Vector、LinkedList源码

      List接口的一些列实现中,最常用最重要的就是这三个:ArrayList、Vector、LinkedList。这里我就基于JDK1.7来看一下源码。

    public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
    
    public class Vector<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
    
    public class LinkedList<E> extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable

     从这三个类定义就可以看出一些信息:

    • ArrayList、Vector继承了AbstractList这个抽象类,LinkedList继承了AbstractSequentialList
      这个抽象类,AbstractSequentialList 也是继承AbstractList,只不过它实现了get(int index)、
      set(int index, E element)、add(int index, E element) 和 remove(int index)这些骨干性函数,降低了List接口的复杂度;
    • ArrayList和Vector都实现了RandomAccess接口,而LinkedList没有,这是什么意思呢?在JDK中,RandomAccess接口是一个空接口,
      所以它没有实际意义,就是一个标记,标记这个类支持快速随机访问,所以,arrayList和vector是支持随机访问的,但是LinkedList不支持;
    • serializbale接口表名,他们都支持序列化。
        

     在这三个List实现类里面:  

    1. ArrayList和Vector使用了数组的实现,相当于封装了对数组的操作。这也正是他们能够支持快速随机访问的原因,
      在JDK中所有基于数组实现的数据结构都能够支持快速随机访问。ArrayList和Vector的实现上几乎都使用了相同的算法,
      他们的主要区别就是ArrayList没有对任何一个方法做同步,所以不是线程安全的;而Vector中大部分方法都做了线程同步,是线程安全的
    2. LinkedList使用的是非循环双向链表的数据结构(这是JDK1.7更新部分,LinkedList在1.7之前都是循环双向链表)
      由于是基于链表的,所以是没法实现随机访问的,只能顺序访问,这也正式它没有实现RandomAccess接口的原因。

      ArrayList和Vector方法实现基本一样,所以这里我就拿ArrayList和LinkedList来做对比。

         一、add方法

    1. ArrayList实现add
       1     public boolean add(E e) {
       2         ensureCapacityInternal(size + 1);  // Increments modCount!!
       3         elementData[size++] = e;
       4         return true;
       5     }
       6 
       7     private void ensureCapacityInternal(int minCapacity) {
       8         if (elementData == EMPTY_ELEMENTDATA) {
       9             minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
      10         }
      11 
      12         ensureExplicitCapacity(minCapacity);
      13     }
      14 
      15     private void ensureExplicitCapacity(int minCapacity) {
      16         modCount++;
      17 
      18         // overflow-conscious code
      19         if (minCapacity - elementData.length > 0)
      20             grow(minCapacity);
      21     }
      22 
      23     private void grow(int minCapacity) {
      24         // overflow-conscious code
      25         int oldCapacity = elementData.length;
      26         int newCapacity = oldCapacity + (oldCapacity >> 1);
      27         if (newCapacity - minCapacity < 0)
      28             newCapacity = minCapacity;
      29         if (newCapacity - MAX_ARRAY_SIZE > 0)
      30             newCapacity = hugeCapacity(minCapacity);
      31         // minCapacity is usually close to size, so this is a win:
      32         elementData = Arrays.copyOf(elementData, newCapacity);
      33     }
      第2行,如果数组空间不够,实现数组动态扩容,动态扩容在grow方法实现,第11行,JDK利用移位运算符进行扩容计算,>>1右移一位表示除2,所以newCapacity就是扩容为原来的1.5倍。(PS:JDK1.7中第11行是移位运算,而在JDK1.6中第11行是直接除2,所以说JDK1.7相比于JDK1.6代码优化了),然后数组空间足够大,然后在数组末尾增加元素并且通过后++完成add元素。
    2. LinkedList实现add
       1     public boolean add(E e) {
       2         linkLast(e);
       3         return true;
       4     }
       5 
       6     void linkLast(E e) {
       7         final Node<E> l = last;
       8         final Node<E> newNode = new Node<>(l, e, null);
       9         last = newNode;
      10         if (l == null)
      11             first = newNode;
      12         else
      13             l.next = newNode;
      14         size++;
      15         modCount++;
      16     }

      代码中可以看到,LinkedList基于链表的,不需要扩容,直接把元素加到链表最后,把新元素的前节点指向之前的last元素后节点就ok了。

      二、get方法

    1. ArrayList实现get
      1     public E get(int index) {
      2         rangeCheck(index);
      3 
      4         return elementData(index);
      5     }
      6 
      7     E elementData(int index) {
      8         return (E) elementData[index];
      9     }

      ArrayList的get方法比较方便,通过数组下标能够直接找到数据返回。

    2. LinkedList实现get
       1     public E get(int index) {
       2         checkElementIndex(index);
       3         return node(index).item;
       4     }
       5 
       6     Node<E> node(int index) {
       7         // assert isElementIndex(index);
       8 
       9         if (index < (size >> 1)) {
      10             Node<E> x = first;
      11             for (int i = 0; i < index; i++)
      12                 x = x.next;
      13             return x;
      14         } else {
      15             Node<E> x = last;
      16             for (int i = size - 1; i > index; i--)
      17                 x = x.prev;
      18             return x;
      19         }
      20     }

      ArrayList的get方法需要遍历到具体位置,获得数据返回,这里为了提高效率,需要根据获取的位置判断是从头还是从尾开始遍历,将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果index>size/2,就只从位置size往前遍历到位置index处即可。

      三、remove方法

    1. ArrayList实现remove
       1     public E remove(int index) {
       2         rangeCheck(index);
       3 
       4         modCount++;
       5         E oldValue = elementData(index);
       6 
       7         int numMoved = size - index - 1;
       8         if (numMoved > 0)
       9             System.arraycopy(elementData, index+1, elementData, index,
      10                              numMoved);
      11         elementData[--size] = null; // clear to let GC do its work
      12 
      13         return oldValue;
      14     }
      15 
      16     public boolean remove(Object o) {
      17         if (o == null) {
      18             for (int index = 0; index < size; index++)
      19                 if (elementData[index] == null) {
      20                     fastRemove(index);
      21                     return true;
      22                 }
      23         } else {
      24             for (int index = 0; index < size; index++)
      25                 if (o.equals(elementData[index])) {
      26                     fastRemove(index);
      27                     return true;
      28                 }
      29         }
      30         return false;
      31     }
      32 
      33     private void fastRemove(int index) {
      34         modCount++;
      35         int numMoved = size - index - 1;
      36         if (numMoved > 0)
      37             System.arraycopy(elementData, index+1, elementData, index,
      38                              numMoved);
      39         elementData[--size] = null; // clear to let GC do its work
      40     }

      ArrayList实现remove(int)和remove(Object)两种方式,通过System的arrayCopy方法实现元素的移动(本质上是数组的复制),remove(Object)方法实际上是先找到需要删除元素的下标,然后在实现删除功能,实际和remove(int)一样。

    2. LinkedList实现remove
       1     public E remove(int index) {
       2         checkElementIndex(index);
       3         return unlink(node(index));
       4     }
       5 
       6     public boolean remove(Object o) {
       7         if (o == null) {
       8             for (Node<E> x = first; x != null; x = x.next) {
       9                 if (x.item == null) {
      10                     unlink(x);
      11                     return true;
      12                 }
      13             }
      14         } else {
      15             for (Node<E> x = first; x != null; x = x.next) {
      16                 if (o.equals(x.item)) {
      17                     unlink(x);
      18                     return true;
      19                 }
      20             }
      21         }
      22         return false;
      23     }
      24 
      25     E unlink(Node<E> x) {
      26         // assert x != null;
      27         final E element = x.item;
      28         final Node<E> next = x.next;
      29         final Node<E> prev = x.prev;
      30 
      31         if (prev == null) {
      32             first = next;
      33         } else {
      34             prev.next = next;
      35             x.prev = null;
      36         }
      37 
      38         if (next == null) {
      39             last = prev;
      40         } else {
      41             next.prev = prev;
      42             x.next = null;
      43         }
      44 
      45         x.item = null;
      46         size--;
      47         modCount++;
      48         return element;
      49     }

      LinkedList实现remove(int)和remove(Object),不管是根据下标删除还是根据Object删除,都是先找到对应的Node,然后在删除,对应的上下数据节点改变。

      四、总结

    1. 对于查找get方法,ArrayList的效率是要比LinkedList高的,原因也是ArrayList是基于数组的,直接通过下标能找到,LinkedList则需要遍历到具体位置。
    2. 增加add方法和删除remove方法,虽说链表的增加和删除效率比数组高,但是这也不是绝对,看具体情况,有几种极端情况,一种是在LinkedList和ArrayList的首位增加和删除,这种LinkedList效率好一点,如果在中间处增加和删除,这种的话ArrayList效率好一点,所以说增加add方法和删除remove方法说不上哪个效率高,这要看具体情况分析。
  • 相关阅读:
    微软同步发行Windows 10和Windows 10 Mobile系统更新
    MySQL5.5中文支持
    sqlplus登入和plsql登入的差别
    SQL Server统计信息:问题和解决方式
    OSX: 命令行制作U盘Recovery HD
    Android 使用SwipeBackLayout实现滑动返回上一级页面——实战来袭
    JSP常见的三个编译指令
    matlab-非线性方程求根函数及函数曲线绘制
    走进windows编程的世界-----消息处理函数(3)
    LoadRunner利用ODBC编写MySql脚本
  • 原文地址:https://www.cnblogs.com/hz-cww/p/6047345.html
Copyright © 2011-2022 走看看