Java 集合 -- List接口实现之ArrayList、LinkedList源码分析

什么是java集合

  • 对线性表,链表,哈希表这些常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。因此,我们一般的程序员不必自己开发相关的方法
  • 集合(框架):java提供了一组对数组、链表数据结构操作的API,这组API即集合;存在于java.util
  • Collection接口的依赖图
    Collection接口的依赖图
    这篇文章主要讲List下ArrayList、LinkedList的两个实现。

List接口实现之–ArrayList

List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。线程不安全

1.使用

List list = new ArryList(); list.add(111); list.remove(下标|元素)
Arrays.asList(new Integer[]{1,2,3})    

2.源码分析 构造方法 底层结构

//存储数据的桶
transient Object[] elementData; 

//new 构造方法
public ArrayList() {
    //elementData 就是一个空数组
	//private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

3.添加元素add

//add
public boolean add(E e) {
    //确保内部容量 传参当前数组长度+1
    ensureCapacityInternal(size + 1);
    //赋值且size+1
    elementData[size++] = e;
    return true;
}
//ensureCapacityInternal 计算与扩容
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//calculateCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //如果当前data == 默认的数组 即刚初始化添加元素的时候
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //取默认容量DEFAULT_CAPACITY=10 和所需容量两者之间的最大值
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //不为空 之间返回所需容量
    return minCapacity;
}

//ensureExplicitCapacity 扩容
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //所需容量大于当前数组的容量
        if (minCapacity - elementData.length > 0)
            //扩容 ###
            grow(minCapacity);
}
//grow 扩容
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //旧数组的容量+本身右移1位 简单计算为右移的容量/2的一次方 即原始容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        //如果新容量还是少于所需容量 直接把容量设为所需容量
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        //取Integer.MAX_VALUE 或者 Integer.MAX_VALUE-8 
        //-8一些VM在数组中保留一些标题字,分配更大可能会导致OutOfMemoryError:请求的数组大小超过VM限制
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    //Arrays.copyOf 截取原数组或用 null 填充以获得指定的长度  扩容结束
    elementData = Arrays.copyOf(elementData, newCapacity);
}

4.remove移除元素

//下标删除元素 index 所需移除的目标下标
public E remove(int index) {
	//传入下标 index >= size 抛越界异常
    rangeCheck(index);
    modCount++;
    //获取对应下标元素
    E oldValue = elementData(index);
	//这里是获取移除元素后面的个数
    int numMoved = size - index - 1;
    if (numMoved > 0)
        //这里采用的是按下标移动数组的方式 下面是参数意义
        //Object elementData: 原数组  int index+1 : 从元数据的起始位置开始
        //Object elementData: 目标数组 int index : 目标数组的开始起始位置
  	  //int numMoved: 要copy的数组的长度
        System.arraycopy(elementData, index+1, elementData, index,
        numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

List接口实现之 – LinkedList

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null),线程不安全,使用是一样的

1.底层数据结构

//最底层的静态内部类链表
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;		
    }
}  

2.add添加元素

public boolean add(E e) {
    linkLast(e);
    return true;
}
void linkLast(E e) {
    //把成员变量Node<E> last; 赋给 node l
    final Node<E> l = last;
    //new 一个新的node pre传last next传null
    final Node<E> newNode = new Node<>(l, e, null);
    //把当前操作的node赋给成员变量last 用于下次添加的l node
    last = newNode;
    //第一次添加肯定是空的
    if (l == null)
        //记录当前数组的第一个
    	first = newNode;
    else
        //赋值给l的下一个
    	l.next = newNode;
    //加size
    size++;
    modCount++;
}

3.get获取元素

public E get(int index) {
	//检查是否越界
    checkElementIndex(index);
    // 返回节点值 ###
    return node(index).item;
}

//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++)
            //取到目标下标前一个的next,也可以算是少了一次遍历
            x = x.next;
        return x;
    } else {
        //否则从后面开始检索
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            //同理
            x = x.prev;
        return x;
    }
}

4.remove删除元素

//元素删除也是先找到下标 所以直接贴下标删除的代码
public E remove(int index) {
    checkElementIndex(index);
    //上面看了node方法 就是根据下标找到目标元素node
    return unlink(node(index));
}
//unlink
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;
}

以上就是ArrayList、LinkedList源码分析的全部内容了。
下一篇:Java 集合 – Set接口实现之HashSet、TreeSet源码分析

纸上学来终觉浅,要知此事须躬行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值