ArrayList & LinkedList源码解析

本文记录ArrayList & LinkedList源码解析 基于JDK1.8

ArrayList

ArrayList实现了List接口 所有拥有List接口所有方法 可以看成可'调节'的数组 可以包含任何类型数据(包括null,可重复)ArrayList线程不是安全的

类结构

ArrayList类主要成员变量:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

  	// 初始化容量(数组初始长度) 10
    private static final int DEFAULT_CAPACITY = 10;

	// 表示空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};

  	// 也是空数组,和EMPTY_ELEMENTDATA区分开
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    // 装ArrayList的数组
    transient Object[] elementData; // non-private to simplify nested class access

	// 数组长度
    private int size;
}

方法解析

构造函数

public ArrayList()

public ArrayList() {
		// elementData为DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}


public ArrayList(int initialCapacity)

public ArrayList(int initialCapacity) {
		// initialCapacity初始化容量 由调用者传入
	
		// 如果initialCapacity大于0
        if (initialCapacity > 0) {
			// elementData为 Object类型的  initialCapacity大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
			// 如果初始值为0 elementData为 EMPTY_ELEMENTDATA 空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
			// initialCapacity 小于0  抛错
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
}

public ArrayList(Collection<? extends E> c)

public ArrayList(Collection<? extends E> c) {
		
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
}

创建一个包含指定集合c数据的ArrayList 上面为什么要多此一举使用
Arrays.copyOf(elementData, size, Object[].class)复制一遍数组呢?这是因为在某些情况下
调用集合的toArray()方法返回的类型并不是Object[].class 比如:

Long[] array1 = {1L, 2L};
List<Long> list1 = Arrays.asList(array1);
Object[] array2 = list1.toArray();
System.out.println(array2.getClass() == Object[].class); // false

List<Long> list2 = new ArrayList<>();
System.out.println(list2.toArray().getClass() == Object[].class); // true

add方法
add(E e)向数组尾部添加元素

public boolean add(E e) {
		// 确定数组容量 size 类型为int 默认值(即初始值)为 0 
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
}


private void ensureCapacityInternal(int minCapacity) {
		// minCapacity = size + 1 = 1    
		// elementData = {}
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}


private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
			// Math.max返回最大的数组  DEFAULT_CAPACITY = 10   minCapacity = 1 返回 10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
}


private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
		
        // minCapacity = 10  
		// elementData = {}
  		// 10 - 0 > 0
        if (minCapacity - elementData.length > 0)
			// 扩容
            grow(minCapacity);
}


private void grow(int minCapacity) {
        // oldCapacity = 0
        int oldCapacity = elementData.length;
		// >> 右移运算符 相当于newCapacity为oldCapacity的1.5倍 
		// oldCapacity / 2 此处 0 + 0 / 2 还是等于 0 
        int newCapacity = oldCapacity + (oldCapacity >> 1);
		// newCapacity = 0  minCapacity = 10  0 - 10 < 0 条件成立
        if (newCapacity - minCapacity < 0)
			// newCapacity = 10
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
		// // 复制到新数组 数组容量为10
        elementData = Arrays.copyOf(elementData, newCapacity);
 }

private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
		// MAX_ARRAY_SIZE常量值为Integer.MAX_VALUE - 8 通过
    	// 这段逻辑我们可以知道,ArrayList最大容量为Integer.MAX_VALUE
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
}    

综述 可以知道

  1. 任何一个空的数组 第一次添加元素的时候 内部数组容量将被扩容为10
  2. 扩容时,newCapacity为oldCapacity的1.5倍
  3. 容量最大值为Integer.MAX_VALUE - 8
  4. 尾部添加元素不用移动任何元素 所以速度快

add方法

add(int index, E element)用于在指定位置添加元素

public void add(int index, E element) {
		// 数组下标检查
        rangeCheckForAdd(index);

		// 跟上面add(E e) 一样  确定数组容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
		// 将原来index后面的所有元素往后面移动一个位置
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
		// index位置放入新元素
        elementData[index] = element;
		// 数组长度 + 1
        size++;
}


private void rangeCheckForAdd(int index) {
		// 如果下标大于数组长度 或者 小于0 抛错
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

这里涉及到元素移动 所以速度较慢

get方法
public E get(int index) 获取指定索引位置

public E get(int index) {
		// 检查index
        rangeCheck(index);
		// 直接返回数组指定位置元素
        return elementData(index);
}

private void rangeCheck(int index) {
		// 如果index 大于 数组长度  抛错
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

直接返回索引所在元素 速度较快

set方法
set(int index, E element)向指定位置添加元素

public E set(int index, E element) {
		// 检查index 跟get方法一样
        rangeCheck(index);
		
		// 获取老的index所在位置元素
        E oldValue = elementData(index);
		// 设置新值
        elementData[index] = element;
		// 返回老值
        return oldValue;
}

set方法不涉及元素移动和遍历 所以速度快

remove方法
remove(int index)

public E remove(int index) {
		// 检查index 跟get方法一样
        rangeCheck(index);
		
        modCount++;
		// 获取指定位置需要被删除的元素
        E oldValue = elementData(index);
		
        int numMoved = size - index - 1;
        if (numMoved > 0)
			// elementData数组 index + 1的位置为复制起始点
			// 从index位置开始 复制 numMoved个元素
			// 即被删除元素去除后 其后面的其他元素(如果有)都要向前移一位  覆盖index处的元素
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
		// 设置elementData最后一位元素为 null
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;



public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

# 参数解释
# src 原数组 顾名思义  从哪个数组复制
# srcPos 从原数组哪个位置开始复制
# dest 目标数组
# destPos 目标数组起始位置
# length 复制的长度
}

remove(Object o)


// 区分 null和非null的元素 都是通过fastRemove方法来删除
public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
}


// 删除逻辑和remove(int index)一样  覆盖index位置元素
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
}

LinkedList

类结构

LinkedList底层采用双向链表结构存储数据 允许重复数据和null值 长度没有限制
image

每个节点用内部类Node表示

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

其中包含 item(存储数据)、next(后继节点)、prev(前继节点) Node可以分布在各个内存位置 通过prev 和 next来维护

LinkedList包含如下成员变量

// 元素个数 默认为 0 
transient int size = 0;

// 头节点 该节点必须满足(first == null && last == null) || (first.prev == null && first.item != null)
// 即头节点满足 前继节点为 null 且item(存储数据)不为空
transient Node<E> first;

// 尾节点 该节点必须满足(first == null && last == null) || (last.next == null && last.item != null)
// 即尾节点满足 后继节点为 null 且item(存储数据)不为空
transient Node<E> last;

方法解析

构造函数

public LinkedList()

public LinkedList() {}

空参构造函数 size 默认 为 0 每次添加新元素都需要创建Node类型节点

public LinkedList(Collection<? extends E> c)

public LinkedList(Collection<? extends E> c) {
		// 调用空参构造函数
        this();
        addAll(c);
}


public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
}


public boolean addAll(int index, Collection<? extends E> c) {
		// 检查index 必须要大于 0 且小于 size 否则抛错
        checkPositionIndex(index);

		// 将c转成一个数组
        Object[] a = c.toArray();
		// 获取数组的长度
        int numNew = a.length;
        if (numNew == 0)
            return false;

		// 定义Node类型节点 pred  succ
        Node<E> pred, succ;
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }
		
		// 循环创建节点,设置prev,next指向
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
}


private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}


private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
}

add方法

add(int index, E element)

public void add(int index, E element) {
		// 检查index 同构造函数
        checkPositionIndex(index);
		
		// 如果index 和 链表大小相同
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
}


void linkLast(E e) {
		// 定义一个Node类型变量   获取最后一个节点元素
        final Node<E> l = last;

		// 实例化Node类  prev为原链表最后一个节点 next为null
        final Node<E> newNode = new Node<>(l, e, null);

		// 将新节点赋值给last  成为 最后一个节点
        last = newNode;

        if (l == null)
			// 如果老的链表中 最后一个节点为null 证明是一个空链表
			// 此时 第一个节点和最后一个节点是相同的
            first = newNode;
        else
			// l作为老的最后一个节点 在新节点生成后 需要将newNode 赋值给 老的next(后继节点)
            l.next = newNode;

		// 元素个数 + 1
        size++;
        modCount++;
}


// 采用二分法遍历每个Node节点 直到找到index位置的节点
Node<E> node(int index) {
		// size >> 1 相当于 size / 2
        if (index < (size >> 1)) {
            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;
        }
}


void linkBefore(E e, Node<E> succ) {
        // succ为原链表指定index位置的节点 获取其prev节点
        final Node<E> pred = succ.prev;

		// 创建新节点 prev 为原链表指定index位置节点的prev  next为原链表指定index位置节点
        final Node<E> newNode = new Node<>(pred, e, succ);

		// 将原链表指定index位置的节点的prev更新为新节点
        succ.prev = newNode;

		// 如果succ为第一个节点
        if (pred == null)
			// 将新节点赋值给第一个节点
            first = newNode;
        else
			// 将新节点赋值给原链表指定index节点的prev(上一个节点)的next
            pred.next = newNode;

		// 元素个数 + 1
        size++;
        modCount++;
}

get方法

get(int index)

public E get(int index) {
		// 检查索引
        checkElementIndex(index);

		// 利用二分法查询index位置所在节点  返回其item值
        return node(index).item;
}

set方法

set(int index, E element)

public E set(int index, E element) {
		// 检查索引
        checkElementIndex(index);
		
		// 获取index位置所在节点
        Node<E> x = node(index);

		// 获取节点的item值
        E oldVal = x.item;

		// 将item赋值为新值
        x.item = element;

		// 返回旧的值
        return oldVal;
}

remove方法

remove(int index)

public E remove(int index) {
		// 检查索引
        checkElementIndex(index);

		// node(index)返回当前index对应的节点
        return unlink(node(index));
}


E unlink(Node<E> x) {
        // 获取index位置所在节点item值
        final E element = x.item;

		// 获取index位置所在节点 后继节点
        final Node<E> next = x.next;

		//  获取index位置所在节点 前继节点
        final Node<E> prev = x.prev;

		// 删除的是否是头节点
        if (prev == null) {
			// 头节点赋值为 index位置所在节点的下一个节点(index所在节点要被删了)
            first = next;
        } else {
			// 不是头节点 需要将index位置所在节点的上一个节点的next指向 下一个节点
			// index位置所在节点prev置为 null
            prev.next = next;
            x.prev = null;
        }

		// 删除的是否是最后一个节点
        if (next == null) {
			// 最后一个节点赋值为index位置所在节点的上一个节点
            last = prev;
        } else {
			// 不是删除最后一个节点
			// index位置所在节点的下一个节点的prev指向上一个节点
            next.prev = prev;
            x.next = null;
        }
			
		// item置为
        x.item = null;
		// 元素个数 - 1
        size--;
        modCount++;
		// 返回被删除元素
        return element;
}

RandomAccess接口

RandomAccess接口是一个空接口即标记接口

public interface RandomAccess {
}

实现该接口的类说明其支持快速随机访问 比如ArrayList实现了该接口 说明ArrayList支持快速随机访问 所谓快速随机访问指的是通过元素的下标即可快速获取元素对象 无需遍历 而LinkedList则没有这个特性 元素获取必须遍历链表

即使再小的帆也能远航
posted @ 2021-10-10 22:13  wwbao  阅读(189)  评论(0编辑  收藏  举报