zoukankan      html  css  js  c++  java
  • 浅谈ArrayList、Vector和LinkedList

    ArrayList

    ArrayList就是数组列表,是一个动态数组,其容量能自动增长。
    特点:查询效率高,增删效率低,线程不安全,日常使用频率高。

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
        //默认容量
        private static final int DEFAULT_CAPACITY = 10;
    
        //new ArrayList<>(0)时,就会调用this.elementData=EMPTY_ELEMENTDATA 
        private static final Object[] EMPTY_ELEMENTDATA = {};
    
        //new ArrayLisT<>()时,即获得这个空数组
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
        //数据都存储在Object[] elementData中
        transient Object[] elementData; // non-private to simplify nested class access
    
        //size,指的是elementData中含有元素的数量,而不是数组的大小
        private int size;
    ..........
    }
    

    ArrayList的构造方法

    public ArrayList()//构造一个空数组,在第一次add添加元素的时候,会把该数组扩容成容量为(DEFAULT_CAPACITY=10指定)的数组
    public ArrayList(int initialCapacity)//构造一个指定容量大小的数组
    public ArrayList(Collection<? extends E> c)//构造一个包含指定 collection 的元素的列表
    

    ArrayList新增方法

    1. public boolean add(E e) //在数组的尾部增加元素
    2. public void add(int index, E element)//将>=index位置的元素往后移动一位,然后在指定位置index处插入该元素
    3. public boolean addAll(Collection<? extends E> c)//在数组尾部添加一个Collection中的所有元素
    4. public boolean addAll(int index, Collection<? extends E> c)//原理同2
    

    这里详解一下新增逻辑

        public boolean add(E e) {
            //校验容量是否够,如果容量不够会进行扩容*
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            
            //校验完(不足扩容后),再把该元素加入到数组尾部,数组元素个数(size)+1
            elementData[size++] = e;
            return true;
        }
    
        //下面看一下扩容方法(minCapacity指的是要容纳新增 元素 后最小所需的容量)
        private void grow(int minCapacity) {
            // 获取当前容量大小
            int oldCapacity = elementData.length;
            // 获取增长1.5倍后容量的大小
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            // 如果扩大1.5倍后容量 还不足够*
            if (newCapacity - minCapacity < 0)
                //则新数组的容量大小直接确定为minCapacity
                newCapacity = minCapacity;
    
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            //扩容,Arrays.cpoyOf会返回一个新的数组(保留原来数组的值)
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    

    常用方法

    在数组的尾部增加元素

    • public boolean add(E e)

    在指定位置处插入元素

    • public void add(int index, E element)

    在数组尾部添加该Collection中的所有元素

    • public boolean addAll(Collection<? extends E> c)

    在指定位置插入Collecton中的所有元素

    • public boolean addAll(int index, Collection<? extends E> c)

    移除此列表中的所有元素(底层实现为for循环遍历数组,给所有值=null,size=0)

    • public void clear()

    克隆数组列表(tip:浅克隆

    • public Object clone()

    是否包含该元素

    • public boolean contains(Object o)

    返回数组列表中指定位置的元素

    • public E get(int index)

    返回数组列表中首次出现指定元素的下标

    • public int indexOf(Object o)

    返回数组列表中最后一次出现指定元素的下标

    • public int lastIndexOf(Object o)

    数组列表中的元素是否为0

    • public boolean isEmpty()

    删除指定位置
    删除指定元素
    删除指定范围[fromIndex,toIndex)内的元素

    • public E remove(int index)
    • public boolean remove(Object o)
    • protected void removeRange(int fromIndex, int toIndex)

    替代指定位置的元素

    • public E set(int index, E element)

    以数组的形式返回数组中所有的元素

    • public Object[] toArray()

    Vector

    VectorArrayList类似, 区别在于Vector是同步类(synchronized)。故线程安全
    特点:线程安全,有增长因子这个概念

    构造方法

    1. public Vector()
    2. public Vector(int initialCapacity)
    3. public Vector(Collection<? extends E> c)
    //前面三个都和ArrayList的类似,多了第四个构造方法,其中第二个参数capacityIncrement是增长因子
    4. public Vector(int initialCapacity, int capacityIncrement)
    

    扩容源码解析

    新增逻辑和ArrayList逻辑相同,但是扩容代码有些不同
    在JDK1.8中,ArrayList的扩容是1.5倍
    `下方源码可知,如果设置了增长因子,则扩容容量=旧容量+增长因子,否则=2*旧容量

        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            //如果设置了增长因子,则扩容容量=旧容量+增长因子,否则=2*旧容量
            int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                             capacityIncrement : oldCapacity);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    

    LinkedList

    双向链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList.
    特点:双向链表,继承了Deque接口,所以有peek、pool、pop、push等方法

    链表在get和set方面比较耗时

        public E get(int index) {
            //检查index是否溢出
            checkElementIndex(index);
            //返回指定位置元素
            return node(index).item;
        }
    
        //查找index位置的Node
        Node<E> node(int index) {
            // assert isElementIndex(index);
            // index在链表的左半侧
            if (index < (size >> 1)) {
                Node<E> x = first;
                for (int i = 0; i < index; i++)
                    x = x.next;
                return x;
            } else {
            // index在链表的左半侧
                Node<E> x = last;
                for (int i = size - 1; i > index; i--)
                    x = x.prev;
                return x;
            }
        }
    

    由上面源码可知,get方法是从0遍历到index,或者从size-1遍历到index的,耗时都是非常高的。

  • 相关阅读:
    Spring IoC容器实现
    Spring IoC简介及使用
    tomcat使用及原理
    tomcat的连接数与线程池
    tomcat配置文件server.xml
    java源码之Comparable和Comparator
    java源码之TreeSet
    25 二叉搜索树与双向链表
    24 复杂链表的复制
    条款04:确定对象被使用前已被初始化
  • 原文地址:https://www.cnblogs.com/bendandedaima/p/13535256.html
Copyright © 2011-2022 走看看