栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

什么是java集合
  • 对线性表,链表,哈希表这些常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。因此,我们一般的程序员不必自己开发相关的方法
  • 集合(框架):java提供了一组对数组、链表数据结构操作的API,这组API即集合;存在于java.util
  • 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 item;			//元素
    Node next;	//下一个元素链表	
    Node prev;	//前一个元素链表
    
    Node(Node prev, E element, Node 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 last; 赋给 node l
    final Node l = last;
    //new 一个新的node pre传last next传null
    final Node 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 node(int index) {
    // assert isElementIndex(index);
	//对半取法 小于一半从头开始检索到指定index下标
    if (index < (size >> 1)) {
        Node x = first;
        for (int i = 0; i < index; i++)
            //取到目标下标前一个的next,也可以算是少了一次遍历
            x = x.next;
        return x;
    } else {
        //否则从后面开始检索
        Node 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 x) {
    // assert x != null;
    final E element = x.item;
    //分别取出目标元素的上下链
    final Node next = x.next;
    final Node 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源码分析

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/592531.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号