zoukankan      html  css  js  c++  java
  • 源码探索笔记:ArrayList和LinkedList的源码

    版本:jdk1.8

    ArrayList

    简单看下特性及类图

    特性

    • 动态增长和缩减的索引序列
    • 基于数组实现的List类
    • 线程不安全的

    类图

    image-20210112120237274

    其成员变量及构造方法

        /**
         * Default initial capacity.
         */
        private static final int DEFAULT_CAPACITY = 10; // 默认初始化大小
    
        /**
         * Shared empty array instance used for empty instances.
         */
     	// 共享的空数组(用于空实例)
        private static final Object[] EMPTY_ELEMENTDATA = {};
    
        /**
         * Shared empty array instance used for default sized empty instances. We
         * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
         * first element is added.
         */
    	// 也是一个共享的空数组,作用是和上面的那个区分开来,以便去知道添加第一个元素的时候会扩容多少
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 
    
        /**
         * The array buffer into which the elements of the ArrayList are stored.
         * The capacity of the ArrayList is the length of this array buffer. Any
         * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
         * will be expanded to DEFAULT_CAPACITY when the first element is added.
         */
    	// ArrayList的中的元素
        transient Object[] elementData; // non-private to simplify nested class access
    
        /**
         * The size of the ArrayList (the number of elements it contains).
         *
         * @serial
         */
    	// ArrayList的大小(即有多少元素)
        private int size;
    
        /**
         * Constructs an empty list with the specified initial capacity.
         *
         * @param  initialCapacity  the initial capacity of the list
         * @throws IllegalArgumentException if the specified initial capacity
         *         is negative
         */
    	// 构造函数,如果给的initialCapacity大于0的话就初始化initialCapacity大小的object数组;
    	// 如果等于0的话就把this.elmentData = {};都不是的话就抛IllegalArgumentException
        public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    
        /**
         * Constructs an empty list with an initial capacity of ten.
         */
    	// 啥也没给的时候,就直接给this.elementData = {}
        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    
        /**
         * Constructs a list containing the elements of the specified
         * collection, in the order they are returned by the collection's
         * iterator.
         *
         * @param c the collection whose elements are to be placed into this list
         * @throws NullPointerException if the specified collection is null
         */
    	// 传参是集合的话,把其变成数组,给elementData。然后开始判断其长度,如果长度不为0话就
     	// 判断其类型是否是object[],如果不是的话就把其转换为object[];如果长度为0的话就令
    	// elementData={}
        public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    

    相关个人理解我都写在代码中了

    常用方法

    添加元素 - public boolean add(E e)

    /**
     *	添加元素的方法
     */
    public boolean add(E e) {
        // 确保容量足够(参数为目前已有元素总数 + 1)
    	ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 赋值
        elementData[size++] = e;
        return true;
    }
    
    //确认内部容量
    private void ensureCapacityInternal(int minCapacity) {
        // 确认容量的一些方法,一步一步看进去
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    // 计算容量
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 如果ArrayList此时为空,那就返回minCapacity和10中数值最大的那个数
    	if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
           return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
        // modCount是AbstractList中的变量,目的是记录list修改的次数,我也看了注释、亦找了一些相关的资料,
        // 大概知道是和fail-fast机制有关,但总感觉自己没有领悟到精髓,所以就先放着,以后悟了再来补充
    	modCount++;
    	// overflow-conscious code
        // 如果传过来的参数大于目前elementData的容量时,就扩容
        if (minCapacity - elementData.length > 0)
    		grow(minCapacity);
    }
    
    // 扩容
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 新的容量:oldCapacity右移一位即为0.5*oldCapacity
        // 举个例子:oldCApacity原来为10,也就是1010(2进制),右移1位后为0101,即10进制的15
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果扩容一次后的容量小于传过来的容量的话就把传过来的容量赋值给新的容量
        if (newCapacity - minCapacity < 0)
    		newCapacity = minCapacity;
        // 如果新的容量大于设定的ArrayList最大容量的话,就把hugeCapacity(minCapacity)方法返回值赋给新容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // Arrays的copyOf()方法传回的数组是新的数组对象,不会影响原来的数组
        // 方法第一个参数是要复制的数组,第二个参数是数组长度
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    // 当扩容时到达最大容量的会调用的方法
    private static int hugeCapacity(int minCapacity) {
    	if (minCapacity < 0) // overflow
        	throw new OutOfMemoryError();
        // 如果传过来的参数比ArrayList最大容量还大的话就返回整形最大值,否则返回list设定的最大容量MAX_ARRAY_SIZE
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }
    
    // ArrayList设定最大容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    

    获取元素 - public E get(int index)

    public E get(int index) {
        // 检查下标
        rangeCheck(index);
        // 返回元素
    	return elementData(index);
    }
    
    // 检查下标
    private void rangeCheck(int index) {
        // 如果下标超过了ArrayList的大小,就抛出IndexOutOfBoundsException异常
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    // 获取元素
    @SuppressWarnings("unchecked") // 忽略unchecked警告信息,如使用List、ArrayList等未进行参数化产生的警告信息
    E elementData(int index) {
        // 返回存储数组中相应下标的元素
        return (E) elementData[index];
    }
    

    删除元素 - public E remove(int index) 和 public boolean remove(Object o)

    // 通过下标删除元素
    public E remove(int index) {
        // 下标检查
        rangeCheck(index);
        // 修改量+1
        modCount++;
        // 取出相应下标的元素
        E oldValue = elementData(index);
        // 算出需要移动的元素个数
        int numMoved = size - index - 1;
        // 如果大于0的话,即不是移动elementData的最后一个元素的话,就复制一个
        if (numMoved > 0)
            /**
            * public static native void arraycopy(Object src, int srcPos, Object dest, int destPos,int length);
            * src:要复制的数组(源数组)
        	* srcPos:复制源数组的起始位置
        	* dest:目标数组
        	* destPos:目标数组的下标位置
        	* length:要复制的长度
            */
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 把处理完后(复制后)的数组的最后一个元素,也就是没用的元素指向null,让垃圾处理器司其职
        elementData[--size] = null; // clear to let GC do its work
        // 返回删除的元素
        return oldValue;
    }
    
    // 通过元素删除元素
    public boolean remove(Object o) {
        // 如果传入的对象为空的话就删除elemmentData数组中遍历到的第一个为空的对象
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            // 遍历到第一个和传入对象相等的对象,然后用把下标传给fastRemove方法进行删除
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    
    // 删除数组中指定下标的对象
    private void fastRemove(int index) {
        modCount++;
        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
    }
    

    更新元素 - public E set(int index, E element)

    public E set(int index, E element) {
        // 下标检查
        rangeCheck(index);
        // 获取元素,elementData方法前面出现过了
        E oldValue = elementData(index);
        // 把数组相应下标的对象设置为传入的相应对象
        elementData[index] = element;
        // 返回旧值
        return oldValue;
    }
    

    其它的一些方法

    // 判断ArrayList中是否存在相应对象,存在返回true,反之返回false
    public boolean contains(Object o) {
        // 如果对象的下标大于等于0的话就说明这个对象在ArrayLtSt中
        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源码的赏析就到这了~~

    自己动手实现一个简单的ArrayList

    “纸上得来终觉浅,绝知此事要躬行”。

    看过理解了很容易会忘,自己动手写一遍,或者是当作笔记记录下来,则就会记得比较深刻,往后捡起来也快。这也是我写这边文章的初衷。

    以下是本人手写的简易ArrayList实现,省略了很多细节,只做了一些简单的功能

    import java.io.Serializable;
    import java.util.AbstractList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.RandomAccess;
    
    /**
     * @program: xingzhi-learning
     * @description: 手写简易ArrayList
     * @author: 行之
     * @create: 2021-01-18 17:09
     **/
    public class MyArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, Serializable {
    
        // 先完善MyArrayList所需的基本成员变量
        private Object[] elementData;  // 存储ArrayList对象的数组
        private int size; // ArrayList的大小
        private final int MAX_SIZE = Integer.MAX_VALUE - 8; // 最大容量
        private final Object[] EMPTY_ARRAY = {}; // 空数组
        private final int DEFAULT_CAPACITY = 10; // 默认大小
    
        // 再完善MyArrayList的构造方法
        public MyArrayList() {
            this.elementData = new Object[DEFAULT_CAPACITY];
        }
    
        /**
        * @Description: 参数是容量的有参构造函数
        * @Param: capacity 容量
        */
        public MyArrayList(int capacity) {
            if (capacity > 0) {
                this.elementData = new Object[capacity];
            } else if (capacity == 0){
                this.elementData = EMPTY_ARRAY;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                        capacity);
            }
        }
    
        /**
        * @Description: 添加方法,先确认容量,再添加
        * @Param:  e 添加的对象
        * @return: boolean 添加后的状态
        */
        @Override
        public boolean add(E e) {
            ensureCapacity();
            elementData[size] = e;
            size++;
            return true;
        }
    
        /**
        * @Description: 获取元素,先检查下标,再返回对象
        * @Param: index 下标
        * @return: 获取的值
        */
        @Override
        public E get(int index) {
            checkIndex(index);
            return (E) elementData[index];
        }
    
        /**
        * @Description: 通过下标更新对象
        * @Param: index 下标, e 更新的对象
        * @return: oldValue 更新的值
        */
        @Override
        public E set(int index, E e) {
            checkIndex(index);
            E oldValue = (E) elementData[index];
            elementData[index] = e;
            return oldValue;
        }
    
        /**
        * @Description: 通过下标删除对象:先检查下标,再取出旧值,和需要移动的对象数量,进行数组复制
        * @Param: index 下标
        * @return: oldValue 删除的值
        */
        @Override
        public E remove(int index) {
            checkIndex(index);
            E oldValue = (E) elementData[index];
            int numMoved = size - index - 1;
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
            return oldValue;
        }
    
        /**
        * @Description: 删除相应对象
        * @Param: object 要删除的对象
        * @return: boolean 删除状态
        */
        @Override
        public boolean remove(Object object) {
            for (int i = 0; i < this.size; ++i) {
                if (object.equals(elementData[i])) {
                    remove(i);
                    return true;
                }
            }
            return false;
        }
    
        /**
        * @Description: 返回ArrayList大小
        * @Param:  null
        * @return: size 大小
        */
        @Override
        public int size() {
            return this.size;
        }
    
        /**
        * @Description: 确认容量,如果容量不够就扩容:即扩大原容量的0.5
        * @Param:  null
        */
        private void ensureCapacity() {
            if (this.size == elementData.length) {
                // 扩容
                int oldCapacity = elementData.length;
                int newCapacity = oldCapacity + (oldCapacity >> 1);
                elementData = Arrays.copyOf(elementData, newCapacity);
            };
        }
    
        /**
        * @Description: 检查下标
        * @Param: index 下标
        */
        public void checkIndex(int index) {
            if (index >= this.size || index < 0) throw new IndexOutOfBoundsException("outOfBounds: " + (index));
        }
    }
    
    /**
    * @Description: 测试类
    * @author: 行之
    * @Date: 2021/1/18
    */
    class Test {
        public static void main(String[] args) {
            // 初始化
            MyArrayList<Integer> myIntegerArrayList = new MyArrayList<>();
            MyArrayList<Character> myCharacterArrayList = new MyArrayList<>(20);
            // 添加、获取操作
            for (int i = 0; i < 10; ++i) {
                myIntegerArrayList.add(i);
                System.out.println("myIntegerArrayList's size: " + myIntegerArrayList.size());
                System.out.println("myIntegerArrayList's index: " + i + " value: " + myIntegerArrayList.get(i));
            }
            char temp = 'a';
            for (int i = 0; i < 20; ++i) {
                myCharacterArrayList.add(temp++);
                System.out.println("myCharacterArrayList's size: " + myCharacterArrayList.size());
                System.out.println("myCharacterArrayList's index: " + i + " value: " + myCharacterArrayList.get(i));
            }
            // 更新、删除操作
            myIntegerArrayList.set(4, 888);
            System.out.println("myIntegerArrayList's index: " + 4 + " value: " + myIntegerArrayList.get(4));
            myIntegerArrayList.remove(4);
            System.out.println("myIntegerArrayList's index: " + 4 + " value: " + myIntegerArrayList.get(4));
            myCharacterArrayList.set(5, 'z');
            System.out.println("myCharacterArrayList's index: " + 5 + " value: " + myCharacterArrayList.get(5));
            myCharacterArrayList.remove(5);
            System.out.println("myCharacterArrayList's index: " + 5 + " value: " + myCharacterArrayList.get(5));
        }
    }
    

    LinkedList

    其特性和类图

    特性

    • 非线程安全的
    • 擅长插入和删除
    • 是通过双向链表实现的
    • 维护了元素插入时的顺序

    类图

    其成员变量及构造方法

    transient int size = 0; // 其大小
    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first; // 指向第一个结点的指针
    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last; // 指向最后一个结点的指针
    /**
     * Constructs an empty list.
     */
    public LinkedList() { // 无参构造函数,构造了一个空list
    }
    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param  c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedList(Collection<? extends E> c) { // 参数是集合的构造函数,调用了无参构造函数,把集合元素全部加进list
        this();
        addAll(c);
    }
    // 结点的类
    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;
        }
    }
    

    常用方法

    添加方法

    // 添加结点
    public boolean add(E e) {
        linkLast(e); // 把结点加到链表尾部
        return true;
    }
    
    // 添加结点到指定下标
    public void add(int index, E element) {
        checkPositionIndex(index); // 下标检查
        if (index == size) // 如果下标等于当前链表长度则相当于在尾部添加结点,所以直接调用linkLast方法
            linkLast(element); // 把元素加到链表尾部
        else 
            linkBefore(element, node(index)); // 实参是当前元素和相应下标对应的结点,方法作用是把结点加到链表中
    }
    
    // 添加结点到链表尾部
    void linkLast(E e) {
        final Node<E> l = last; // 获取LinkedList的尾节点
        // 新建一个结点,新结点的头指针指向当前LinkedList的尾结点,新结点所存放的元素是e,尾结点为空
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode; // 让LinkedList的尾结点指向新节点
        // 如果LinkedList的尾节点为空的话就把头结点设为新结点(也就是链表为空时,添加的元素就是头结点)
        if (l == null)
            first = newNode;
        // 非空时:让LinkedList的尾节点指向新结点
        else
            l.next = newNode;
        // LinkedList容量+1
        size++;
        // 修改量+1
        modCount++;
    }
    
    // 返回传入的参数index所对应的非空结点 
    Node<E> node(int index) {
        // assert isElementIndex(index);
        if (index < (size >> 1)) { // 如果index小于链表当前长度的一半(这一步应该是为了减少遍历的时间)
            Node<E> x = first; // x结点指向头指针
            for (int i = 0; i < index; i++) // 遍历拿到相应下标的结点
                x = x.next; 
            return x;
        } else {
            Node<E> x = last; // 如果index大于等于链表当前长度的一半时
            for (int i = size - 1; i > index; i--) // 遍历拿到相应下标的结点
                x = x.prev;
            return x;
        }
    }
    
    // 添加结点到链表中:大致流程是先构建指向关系,再判断是否是头结点从而完善最后的指向
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev; // pred指向指定下标的结点的前一结点
        // 新建一个头指针指向pred、尾指针指向下标相应结点、存放对象是e的结点
        final Node<E> newNode = new Node<>(pred, e, succ);
        // 把succ的头指针指向新加入的结点(为了建立双向的关系)
        succ.prev = newNode;
        // 如果pred是空的话,就说明index下标所对应的结点succ是头结点
        if (pred == null)
            // 所以把LinkedList的首结点指向新加入的结点newNode就行了
            first = newNode;
        else
            // succ不为头结点就让pred的尾指针指向新结点
            pred.next = newNode;
        size++;
        modCount++;
    }
    
    // 下标检查
    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size; // 下标大于等于0且小于容量即为在量程内
    }
    

    更新方法

    public E set(int index, E element) {
        checkElementIndex(index); // 检查下标
        Node<E> x = node(index); // 获取相应下标的结点
        E oldVal = x.item; // 拿到结点的旧值
        x.item = element; // 把结点的值设为实参element
        return oldVal; // 返回旧值
    }
    
    // 检查下标
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }
    

    删除方法

    removeremoveFirst

    // 删除方法,其调用的就是删除的是首结点的方法
    public E remove() {
        return removeFirst();
    }
    
    // 删除首结点的方法
    public E removeFirst() {
        final Node<E> f = first; // 拿到首结点
        if (f == null) // 首结点为空就抛出异常
            throw new NoSuchElementException();
        return unlinkFirst(f); // 断开首结点的连接
    }
    
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item; // 拿到首结点的对象
        final Node<E> next = f.next; // 拿到首结点的下一个结点
        f.item = null; // 首结点和尾结点指向空
        f.next = null; // help GC
        first = next; // 令当前LinkedList的首结点指向之前拿到的next结点
        if (next == null) // 如果next为空就说明当前链表为空 
            last = null;
        else
            next.prev = null; // 把next结点的前一结点指向空,因为next目前是首结点了
        size--; // 容量-1
        modCount++; // 修改量+1
        return element; // 返回之前的首结点值
    }
    

    removeLast

    // 删除最后一个结点
    public E removeLast() {
        final Node<E> l = last; // 拿到尾结点
        if (l == null) // 尾结点空则抛异常
            throw new NoSuchElementException();
        return unlinkLast(l); 
    }
    
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        final E element = l.item; // 拿到尾结点的值
        final Node<E> prev = l.prev; // 拿到尾结点之前的结点
        l.item = null;
        l.prev = null; // help GC
        last = prev; // 把LinkedList的尾结点指向尾结点之前的前一个结点
        if (prev == null) // 如果prev结点为空,就说明当前删除的结点就是唯一的结点了,这时删除一个结点就没了
            first = null;
        else
            prev.next = null; // 把它指向后结点的指针指向空
        size--;
        modCount++;
        return element;
    }
    

    获取方法

    public E get(int index) { // 两个方法前面都见过了,老熟人了~~
        checkElementIndex(index); // 检查下标
        return node(index).item; // 获取相应下标的结点,返回结点的值(对象)
    }
    
    // 拿到链表的第一个结点
    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }
    
    // 拿到链表的最后一个系欸但
    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }
    

    总结

    关于ArrayList和Linked的源码就写这么多吧,源码探索之旅第一次探索结束啦由于各种原因也是断断续续才写完,希望后续自己能够坚持更新更多的源码笔记吧

  • 相关阅读:
    学习笔记:松弛
    学习笔记:可持久化线段树
    poj 3784 Running Median
    学习笔记:树状数组
    poj 2823 Sliding Window 题解
    学习笔记:状态压缩DP
    学习笔记:单调队列
    C++ 竞赛常用头文件
    mongodb lock 出毛病时解决方法
    ag使用需要注意的问题
  • 原文地址:https://www.cnblogs.com/xunxian/p/14294868.html
Copyright © 2011-2022 走看看