首次分析源码List集合源码并没有这么难

前言:本文结合源码整体观察List集合的实现方式,学习交流使用,理解有误希望指正

List整体优缺点

ArrayList

优点: 底层数据结构是数组,查询快,增删慢。

缺点: 线程不安全,效率高

Vector

优点: 底层数据结构是数组,查询快,增删慢。

缺点: 线程安全,效率低。

LinkedList

优点: 底层数据结构是链表,查询慢,增删快。

缺点: 线程不安全,效率高

ArrayList

ArrayList在方法内部作为局部变量使用时,方法使用时在线程私有的虚拟机栈中创建栈帧,ArrayList就存储在栈帧的局部变量表中。方法执行完毕栈帧弹出,资源释放,因此多线程访问时ArrayList也安全。

ArrayList的扩容
ArrayList的扩容并没有这么难,由于ArrayList有数组实现。ArrayList扩容的实质就是创建新的数组将原数组元素进行复制。关键点就是判断是否需扩容(原数组长度不够),通过一定规则确定扩容的大小。

添加元素方法

//size为数组中已有元素的个数
 public boolean add(E e) {
		 //调用扩容方法,进行判断是否进行扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
 //添加集合
  public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

ArrayList源码扩容片段

//minCapacity为添加元素之后所需要的的数组最小长度
private void ensureCapacityInternal(int minCapacity) {
		//判断是否ArrayList中第一次添加元素
		
		//是:将传入值与默认值长度进行比较,两者的最大值进行扩容。
		//由此可见,ArrayList第一次添加需要扩容,添加长度最小值为10
		
		//否:直接进行下一步(添加元素之后elementData数组重新指向新的数组,见下文)
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

	//判断添加元素之后需要的长度,是否超出原数组长度
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
		//如果添加元素之后的长度大于数组长度,进行扩容
		//如果添加之后长度小于数组长度,不需要扩容
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
     
     //扩容函数,对ArrayList进行扩容
    private void grow(int minCapacity) {
        // overflow-conscious code
        //原数组长度
        int oldCapacity = elementData.length;
        //新数组长度,为原数组长度的3/2.这里注意位运算的优先级小于+ - * /
        //还需注意此时可能会发生溢出,两个正数相加结果为负数,下边有判断
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //取新长度与添加元素所需最小长度的最大值作为新长度的值
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //整形最大正数Integer.MAX_VALUE=2^31-1 
        //MAX_ARRAY_SIZE=2^31-1 -8
        //如果扩容长度超过了此值,执行hugeCapacity调整容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //通过返回的新扩容长度进行扩容,调用系统方法。
        //实际就是新创建一个数组,把旧的复制进去。此时elementData对象改变(指向新定义的数组)。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

   //解决溢出
    private static int hugeCapacity(int minCapacity) {
    //溢出判断,两正数相加结果为负数,抛出异常,
    //此时的溢出判断判断的是扩容传入的参数是否溢出。
    //由于minCapacity=size+添加长度,此时有可能传入数据为负数
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        //长度不溢出的情况下且大于 MAX_ARRAY_SIZE返回Integer.MAX_VALUE 
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

ArrayList总结:

1,添加元素首先会判断,是不是第一次添加,如果第一次添加,添加的最小值为10。

2,然后进行判断添加所需最小容量是否大于原本数组的长度(不区分第一次,或其他次都执行),如果添加长度大于原本长度则进行扩容,否则不扩容。
3,扩容的实质就是得到扩容的长度创建新的数组,将原来的复制进去。

4,其中包含位运算,判断是否溢出为重点

LinkedList

对于LinkedList底层由双向链表实现,并不是普通单向链表或者循环链表
节点数据类型:

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

由此数据类型明显可以看到,节点中包含node类型的next和prev,prev就是此节点中的前驱节点,由此可见为双向链表。

再看添加节点方法


    /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;
        //新建一个节点,传入前驱和值和后继
        final Node<E> newNode = new Node<>(l, e, null);
        //新的last
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

这是尾插发,同样有头插法。从尾插发中可以清晰的看到,将新的节点的next为null。因此不是循环链表。
对于链表,和普通的链表操作相同,没什么特殊的,链表的特点就是删除插入容易,但是查找慢需要遍历链表,采用双向链表占用空间大但是操作效率快。

Vector

这个实现也是通过数组实现的,观看源码会发现和ArrayList内部没什么区别同样的方式实现功能。之所以安全是由于内部的方法大部分为synchronized修饰,对方法加锁控制线程安全。

首次查看源码,如有错误或者不足请指正 ,谢谢。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值