zoukankan      html  css  js  c++  java
  • 源码分析(5)-ArrayList、Vector和LinkedList(JDK1.8)

    一、概述

    1、线程安全:ArrayList和LinkedList非线程安全的、Vector线程安全的,synchronized同步锁机制

    2、底层数据结构:ArrayList和Vector底层数据结构是数组;LinkedList双向链表。

    3、时间复杂度是否受插入和删除元素位置影响:ArrayList和Vector受影响,add(E e)方法时间复杂度O(1)和add(int index, E element)方法时间复杂度O(n-index);LinkedList受影响,add(E e)方法时间复杂度O(1)和add(int index, E element)方法时间复杂度O(n)。

    4、是否支持随机访问:ArrayList和Vector支持随机访问,因为实现了java.util.RandomAccess接口;LinkedList不支持。

    5、内存空间占用:ArrayList和Vector空间浪费主要体现在在list列表的结尾会预留一定的容量空间;LinkedList空间花费则体现在每一个元素都需要消耗更多的空间(因为要存放前向元素和后向以及数据)。

    6、是否支持克隆:ArrayList、Vector和LinkedList都实现java.lang.Cloneable接口,实现clone方法。

    7、序列化:ArrayList、Vector和LinkedList都实现java.io.Serializable接口。

    二、集合UML类图 

    ArrayList和Vector继承和实现相同,LinkedList实现java.util.Deque<E>接口和继承java.util.AbstractSequentialList<E>抽象类。

      

    三、ArrayList源码分析

     1、构造方法

     1 //无参构造方法
     2 public ArrayList() {
     3         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     4     }
     5 //指定初始容量的,构造方法
     6     public ArrayList(int initialCapacity) {
     7         if (initialCapacity > 0) {
     8             this.elementData = new Object[initialCapacity];
     9         } else if (initialCapacity == 0) {
    10             this.elementData = EMPTY_ELEMENTDATA;//指定为默认空数组
    11         } else {
    12             throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
    14         }
    15     }
    16 //指定初始集合对象,构造方法
    17     public ArrayList(Collection<? extends E> c) {
    18         elementData = c.toArray();//转化成数组
    19         if ((size = elementData.length) != 0) {
    20             // c.toArray might (incorrectly) not return Object[] (see 6260652)
    21             if (elementData.getClass() != Object[].class)
    22                 elementData = Arrays.copyOf(elementData, size, Object[].class);//复制数组到
    23         } else {
    24             // replace with empty array.
    25             this.elementData = EMPTY_ELEMENTDATA;
    26         }
    27     }

     2、重要成员变量

    //默认初始容量
    private static final int DEFAULT_CAPACITY = 10;
    //空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    //默认容量10的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //
    数组,存储数据 transient Object[] elementData; //数组长度 private int size;
    //最大的数组容量(长度)
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    //数组结构变更次数
    protected transient int modCount = 0; //该属性在父类java.util.AbstractList<E>中

    3、插入

    //添加元素    
    public boolean add(E e) {
            ensureCapacityInternal(size + 1);//确认容量足够
            elementData[size++] = e;
            return true;
    }
    //
    private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    //计算容量    
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(DEFAULT_CAPACITY, minCapacity);//初始化容量10,比较初始容量与最小容量,返回两者之间最大的那个的值
            }
            return minCapacity;
        }
    // 确保容量
    private void ensureExplicitCapacity(int minCapacity) {
            modCount++;//数组结构性修改次数+1
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)//如果需要的最小容量大于当前数组长度,进行扩容
                grow(minCapacity);
    }
    //扩容
    private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);扩容1.5if (newCapacity - minCapacity < 0)//如果扩容后,扩容容量小于最小所需容量,扩容容量直接赋值最小所需容量
                newCapacity = minCapacity;
            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);//复制数组
    }
    //指定位置添加数组    
    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++;
    }

    4、删除

    //删除元素
    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);数组复制,index位置以后的元素前移1个位置
            elementData[--size] = null; // clear to let GC do its work
            return oldValue;
    }

    扩展:

     System.arraycopy()和Arrays.copyOf()方法:

    //本地方法
    public
    static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
     1 //复制数组生成新的长度的数组
     2 public static <T> T[] copyOf(T[] original, int newLength) {
     3         return (T[]) copyOf(original, newLength, original.getClass());
     4     }
     5 //
     6     public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
     7         @SuppressWarnings("unchecked")
     8         T[] copy = ((Object)newType == (Object)Object[].class)
     9             ? (T[]) new Object[newLength]
    10             : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    11         System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    13         return copy;
    14     }

    1、arraycopy()需要目标数组,将原数组拷贝到你自己定义的数组里,而且可以选择拷贝的起点和长度以及放入新数组中的位置。

    2、copyOf()是系统自动在内部新建一个数组,并返回该数组。

    四、Vector源码分析

    成员方法和ArrayList相似,不同之处在于方法都是synchronized同步锁修饰。

    1、构造方法 

    //无参构造方法,初始容量10
        public Vector() {
            this(10);
        }
    //指定初始容量构造方法
        public Vector(int initialCapacity) {
            this(initialCapacity, 0);
        }
    //指定初始容量和容量增长,构造方法
        public Vector(int initialCapacity, int capacityIncrement) {
            super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            this.elementData = new Object[initialCapacity];
            this.capacityIncrement = capacityIncrement;
        }
    //指定集合的构造方法
        public Vector(Collection<? extends E> c) {
            elementData = c.toArray();
            elementCount = elementData.length;
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
        }

    2、重要成员变量

    //数组,存储数据和ArrayList相同
    protected Object[] elementData;
    //数组长度,等同于ArrayList的size
    protected int elementCount;
    //扩容量
    protected int capacityIncrement;

    3、插入

    //添加元素
        public synchronized boolean add(E e) {
            modCount++;
            ensureCapacityHelper(elementCount + 1);//确保容量
            elementData[elementCount++] = e;//尾部添加元素
            return true;
        }
    //
        private void ensureCapacityHelper(int minCapacity) {
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);//扩容
        }
    //
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);//如果容量增长大于0,容量增加capacityIncrement;如果不大于0,扩容2倍
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            elementData = Arrays.copyOf(elementData, newCapacity);//复制数组
        }

    五、LinkedList源码分析

    LinkedList是一个实现了List接口和Deque接口的双端链表。 LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性; LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法:

    List list=Collections.synchronizedList(new LinkedList(...));

    1、构造方法

    //
    public LinkedList() {}
    //
    public LinkedList(Collection<? extends E> c) {
            this();
            addAll(c);
        }

    2、重要成员变量 

    //双向链表节点长度
    transient int size = 0;
    //头结点
    transient Node<E> first;
    //尾结点
    transient Node<E> last;

    3、数据结构

        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;
            }
        }

    4、插入

      //插入到头部  
        public void addFirst(E e) {
            linkFirst(e);
        }
    //
        private void linkFirst(E e) {
            final Node<E> f = first;//头结点
            final Node<E> newNode = new Node<>(null, e, f);
            first = newNode;
            if (f == null)//头结点为空
                last = newNode;
            else
                f.prev = newNode;//原头结点的前一个节点指向新节点
            size++;
            modCount++;
        }
    //尾部添加节点
        public void addLast(E e) {
            linkLast(e);
        }
    //
        void linkLast(E e) {
            final Node<E> l = last;//尾结点
            final Node<E> newNode = new Node<>(l, e, null);
            last = newNode;
            if (l == null)//尾结点为空,此时头尾节点都是空
                first = newNode;
            else
                l.next = newNode;//尾结点执行新节点
            size++;
            modCount++;
        }
     //插入节点默认在尾部添加   
        public boolean add(E e) {
            linkLast(e);
            return true;
        }
    //指定位置添加节点
        public void add(int index, E element) {
            checkPositionIndex(index);
    
            if (index == size)
                linkLast(element);//尾部添加节点
            else
                linkBefore(element, node(index));
        }
    //
        void linkBefore(E e, Node<E> succ) {
            // assert succ != null;
            final Node<E> pred = succ.prev;
            final Node<E> newNode = new Node<>(pred, e, succ);
            succ.prev = newNode;
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            size++;
            modCount++;
        }
    //
        Node<E> node(int index) {
            // assert isElementIndex(index);
    
            if (index < (size >> 1)) {//插入位置小于0.5*size,正向遍历
                Node<E> x = first;
                for (int i = 0; i < index; i++)//查询待插入位置节点
                    x = x.next;
                return x;
            } else {
                Node<E> x = last;
                for (int i = size - 1; i > index; i--)//逆向遍历
                    x = x.prev;
                return x;
            }
        }

    4、删除

    //删除指定位置元素
        public E remove(int index) {
            checkElementIndex(index);
            return unlink(node(index));//遍历查询index位置元素
        }
    //
        E unlink(Node<E> x) {//删除数据
            // assert x != null;
            final E element = x.item;
            final Node<E> next = x.next;
            final Node<E> prev = x.prev;
    
            if (prev == null) {
                first = next;
            } else {
                prev.next = next;
                x.prev = null;
            }
    
            if (next == null) {
                last = prev;
            } else {
                next.prev = prev;
                x.next = null;
            }
    
            x.item = null;
            size--;
            modCount++;
            return element;
        }

    其他方法不再讲解。

  • 相关阅读:
    简易发号SQL,可用于生成指定前缀自增序列--改进版
    关于【【故障公告】数据库服务器 CPU 近 100% 引发的故障(源于 .NET Core 3.0 的一个 bug)】IS NOT NULL测试
    简易发号SQL,可用于生成指定前缀自增序列,如订单号,生成优惠券码等
    [EntityFramework]记录Linq中如何比较数据库中Timestamp列的方法(如大于0x00000000000007D1的记录)
    [高并发]抢红包设计(使用redis)
    [.Net跨平台]部署DTCMS到Jexus遇到的问题及解决思路--验证码
    [.Net跨平台]部署DTCMS到Jexus遇到的问题及解决思路---部署
    【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统 :1.技术简介之Mina连接
    Unity光晕剑效果的Shader简单实现
    Unity3D 调用Android与IOS的剪贴板
  • 原文地址:https://www.cnblogs.com/wangymd/p/12930627.html
Copyright © 2011-2022 走看看