ArrayList和Vector的区别,与LinkedList各自的存储性能与特性

Vector和ArrayList这两个集合类的本质并没有太大的不同,它们都实现了List接口,而且底层都是基于Java数组来存储集合元素。

在ArrayList集合类的源代码中可以看到如下一行。

//采用elementData数组来保存集合元素
private transient Object[] elementData;
   
   
   
   
   
   
   
   

    在Vector集合类的源代码中也可看到类似的一行。

    //采用elementData数组来保存集合元素
    protected Object[] elementData;
       
       
       
       
       
       
       
       

      从上面代码可以看出,ArrayList使用 transient 修饰了 elementData 数组。这保证系统序列化ArrayList对象时不会直接序列号elementData数组,而是通过ArrayList提高的writeObject、readObject方法来实现定制序列化;但对于Vector而言,它没有使用transient修饰elementData数组,而且Vector只提供了一个writeObject方法,并未完全实现定制序列化。

      从序列化机制的角度来看,ArrayList的实现比Vector的实现更安全。除此之外,Vector其实就是ArrayList的线程安全版本,ArrayList和Vector绝大部分方法的实现都是相同的,只是Vector的方法增加了 synchronized修饰。

      下面先来看ArrayList中的add(int index, E element)方法的源代码

      public void add(int index, E element) {
          //如果添加位置大于集合长度或者小于0,抛出异常
          if (index > size || index < 0)
              throw new IndexOutOfBoundsException(
              "Index: "+index+", Size: "+size);
      
          //保证ArrayList底层的数组可以保存所有集合元素
          ensureCapacity(size+1);  // Increments modCount!!
          //将elementData数组中index位置之后的所有元素向后移动一位
          //也就是将elementData数组的index位置的元素空出来
          System.arraycopy(elementData, index, elementData, index + 1,
                   size - index);
          //将新元素放入elementData数组的index位置
          elementData[index] = element;
          size++;
      }
         
         
         
         
         
         
         
         

        再来看Vector中add(int index, E element)方法的源代码,如下所示。

        public void add(int index, E element) {
            insertElementAt(element, index);
        }
           
           
           
           
           
           
           
           

          从上面代码可以看出:Vector的add(int index, E element)方法其实就是insertElementAt(element, index)方法。下面是insertElementAt(element, index) 方法的源代码。

          public synchronized void insertElementAt(E obj, int index) {
              //增加集合的修改次数
              modCount++;                                //①
              //如果添加位置大于集合长度,抛出异常
              if (index > elementCount) {
                  throw new ArrayIndexOutOfBoundsException(index
                                       + " > " + elementCount);
              }
              //保证ArrayList底层的数组可以保存所有集合元素
              ensureCapacityHelper(elementCount + 1);
              //将elementData数组中index位置之后的所有元素向后移动一位
              //也就是将elementData数组的index位置的元素空出来
              System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
              //将新元素放入elementData数组的index位置
              elementData[index] = obj;
              elementCount++;
          }
             
             
             
             
             
             
             
             

            将ArrayList中的add(int index, E element)方法和Vector的insertElementAt(E obj, int index)方法进行对比,可以发现Vector的方法只是多了synchronized修饰,而且多了①行代码。这并不代表ArrayList的add(int index, E element)方法就没有这行代码,ArrayList只是将这行代码放在ensureCapacity(size + 1)中完成。

            ArrayList中使用size实例变量来保存集合中元素的个数,而Vector中使用elementCount实例变量来保存集合元素的个数。两个变量的作用没有任何区别,只是size变量名更简洁。

            下面是ArrayList的ensureCapacity(int minCapacity)方法的源代码

            public void ensureCapacity(int minCapacity) {
                //增加ArrayList的修改次数
                modCount++;
                //保存ArrayList底层数组的长度
                int oldCapacity = elementData.length;
                //如果minCapacity大于原数组的长度
                if (minCapacity > oldCapacity) {
                    //创建一个新数组变量保存elementData数组
                    Object oldData[] = elementData;
                    //将新容量扩充为原来的1.5倍
                    int newCapacity = (oldCapacity * 3)/2 + 1;
                    //如果newCapacity依然小于minCapacity,直接将minCapacity赋给newCapacity
                        if (newCapacity < minCapacity)
                    newCapacity = minCapacity;
                        // minCapacity is usually close to size, so this is a win:
                    //通过Arrays的copyof复制一个新数组,新数组长度为newCapacity
                    elementData = Arrays.copyOf(elementData, newCapacity);
                }
            }
               
               
               
               
               
               
               
               

              类似地,Vector提供了ensureCapacityHelper(int minCapacity)方法来完成类似地功能,如下所示。

              private void ensureCapacityHelper(int minCapacity) {
                  //保存Vector底层数组的长度
                  int oldCapacity = elementData.length;
                  //如果minCapacity大于原数组的长度
                  if (minCapacity > oldCapacity) {
                      //创建一个新数组变量保存elementData数组
                      Object[] oldData = elementData;
                      //如果capacityIncrement > 0,则newCapacity等于oldCapacity加capacityIncrement
                      //否则,将新容量扩充为原来的2倍
                      int newCapacity = (capacityIncrement > 0) ?
                      (oldCapacity + capacityIncrement) : (oldCapacity * 2);
                          if (newCapacity < minCapacity) {
                      newCapacity = minCapacity;
                      }
                          elementData = Arrays.copyOf(elementData, newCapacity);
                  }
              }
                 
                 
                 
                 
                 
                 
                 
                 

                将ArrayList中的ensureCapacity(int minCapacity)方法和Vector的ensureCapacityHelper(int minCapacity)方法进行对比,可以发现这两个方法几乎完全相同,只是在扩充底层数组的容量时略有区别而已。ArrayList总是将底层容量扩充为原来的1.5倍,但Vector则多了一个选择:当capacityIncrement实例变量大于0时,扩充后的容量等于原来的容量加上capacityIncrement的值

                Vector的ensureCapacityHelper(int minCapacity)方法在扩充底层数组容量时多一个选择是因为,创建Vector可以传入一个capacityIncrement参数,如下构造器所示。

                Vector(int initialCapacity, int capacityIncrement)
                   
                   
                   
                   
                   
                   
                   
                   

                  以initialCapacity作为底层数组的初始长度,以capacityIncrement作为数组长度扩充时的增大步长来创建Vector对象。但对于ArrayList而言,它的构造器最多只能指定一个initialCapacity参数。

                  ArrayList、Vector、LinkedList的存储性能和特性

                  ArrayList 和Vector他们底层的实现都是一样的,都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。

                  Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java中的遗留容器。
                  LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
                  Vector属于遗留容器(Java早期的版本中提供的容器,除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用,但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。相当于ArrayList中的每个方法都进行synchronized封装了一次。

                  static class SynchronizedList
                          extends SynchronizedCollection
                          implements List {
                          private static final long serialVersionUID = -7754090372962971524L;
                  
                          final List list;
                  
                          SynchronizedList(List list) {
                              super(list);
                              this.list = list;
                          }
                          SynchronizedList(List list, Object mutex) {
                              super(list, mutex);
                              this.list = list;
                          }
                  
                          public boolean equals(Object o) {
                              if (this == o)
                                  return true;
                              synchronized (mutex) {return list.equals(o);}
                          }
                          public int hashCode() {
                              synchronized (mutex) {return list.hashCode();}
                          }
                  
                          public E get(int index) {
                              synchronized (mutex) {return list.get(index);}
                          }
                          public E set(int index, E element) {
                              synchronized (mutex) {return list.set(index, element);}
                          }
                          public void add(int index, E element) {
                              synchronized (mutex) {list.add(index, element);}
                          }
                          public E remove(int index) {
                              synchronized (mutex) {return list.remove(index);}
                          }
                  
                          public int indexOf(Object o) {
                              synchronized (mutex) {return list.indexOf(o);}
                          }
                          public int lastIndexOf(Object o) {
                              synchronized (mutex) {return list.lastIndexOf(o);}
                          }
                  
                          public boolean addAll(int index, Collection c) {
                              synchronized (mutex) {return list.addAll(index, c);}
                          }
                  
                          public ListIterator listIterator() {
                              return list.listIterator(); // Must be manually synched by user
                          }
                  
                          public ListIterator listIterator(int index) {
                              return list.listIterator(index); // Must be manually synched by user
                          }
                  
                          public List subList(int fromIndex, int toIndex) {
                              synchronized (mutex) {
                                  return new SynchronizedList<>(list.subList(fromIndex, toIndex),
                                                              mutex);
                              }
                          }
                  
                          @Override
                          public void replaceAll(UnaryOperator operator) {
                              synchronized (mutex) {list.replaceAll(operator);}
                          }
                          @Override
                          public void sort(Comparator c) {
                              synchronized (mutex) {list.sort(c);}
                          }
                  
                          /**
                           * SynchronizedRandomAccessList instances are serialized as
                           * SynchronizedList instances to allow them to be deserialized
                           * in pre-1.4 JREs (which do not have SynchronizedRandomAccessList).
                           * This method inverts the transformation.  As a beneficial
                           * side-effect, it also grafts the RandomAccess marker onto
                           * SynchronizedList instances that were serialized in pre-1.4 JREs.
                           *
                           * Note: Unfortunately, SynchronizedRandomAccessList instances
                           * serialized in 1.4.1 and deserialized in 1.4 will become
                           * SynchronizedList instances, as this method was missing in 1.4.
                           */
                          private Object readResolve() {
                              return (list instanceof RandomAccess
                                      ? new SynchronizedRandomAccessList<>(list)
                                      : this);
                          }
                      }
                  

                  总结:

                  • ArrayList和Vector都实现了List接口,底层都是基于Java数组来存储集合元素
                  • ArrayList使用transient修饰了elementData数组,而Vector则没有
                  • Vector是ArrayList的线程安全版本
                  • 容量扩充 ArrayList为0.5倍+1,而Vector若指定了增长系数,则新的容量=”原始容量+增长系数”, 否则增长为原来的1倍

                  你可能感兴趣的:(后台)