年轻人不讲武德,一起聊聊List集合(四)

文章目录

  • 前言
  • 一、List类图
  • 二、源码剖析
    • 1. CopyOnWriteArrayList(此篇详解)
    • 2. ArrayList
    • 3. LinkedList
    • 4. Vector
  • ~~   码上福利


前言

业精于勤荒于嬉,行成于思毁于随;

在码农的大道上,唯有自己强才是真正的强者,求人不如求己,静下心来,开始思考…

今天一起来聊一聊 List集合,看到这里,笔者懂,大家莫慌,先来宝图镇楼 ~
年轻人不讲武德,一起聊聊List集合(四)_第1张图片
这年轻人,不讲武德,

来,骗!

来,偷袭!

我69岁的老同志。

这好吗?这不好。

我劝这位年轻人,耗子尾汁。

咳咳… 相信大家满脑子的ArrayList已被保国爷爷经典的画面以及台词冲淡了,那么,目的已达到,那我们言归正传,对于屏幕前帅气的猿友们来说,ArrayList,LinkedList,Vector,CopyOnWriteArrayList… 张口就来,闭眼能写,但是呢,我相信大部分的猿友们并没有刨根问底真正去看过其源码,此时,笔者帅气的脸庞似有似无洋溢起一抹微笑,毕竟是查看过源码的猿,就是那么的不讲武德,吃我一记闪电五连鞭,话不多说,来吧,展示…



一、List类图

List全家桶



二、源码剖析


1. CopyOnWriteArrayList(此篇详解)


  • 构造函数
    //  CopyOnWriteArrayList底层为数组 transient关键字:当前数组不能被序列化
    private transient volatile Object[] array;

    /**
     * 入口
     */
    public CopyOnWriteArrayList() {
     
        // 数组初始化
        setArray(new Object[0]);
    }

    // 数组初始化
    final void setArray(Object[] a) {
     
        // 初始化长度为0的数组,array = new Object[0];
        array = a;
    }

从源码中可以看出,构造只为底层数组进行初始化,默认长度为0的空数组;

  • add() - 添加元素方法
    // lock锁
    final transient ReentrantLock lock = new ReentrantLock();

    /**
     * 入口 - ReentrantLock锁:保证线程安全
     */
    public boolean add(E e) {
     
        // 设置锁
        final ReentrantLock lock = this.lock;
        // 加锁
        lock.lock();
        try {
     
            // 获取源数组
            Object[] elements = getArray();
            // 获取源数组长度
            int len = elements.length;
            // copy (源数组长度+1)的新数组
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            // 添加元素
            newElements[len] = e;
            // 赋值给源数组
            setArray(newElements);
            return true;
        } finally {
     
            // 释放锁
            lock.unlock();
        }
    }

    // 获取源数组
    final Object[] getArray() {
     
        return array;
    }
    

从源码中可以看出,添加元素时每次都会先复制一个(源数组长度+1)的新数组,添加元素后再赋值给源数组;

知识点:
每次添加元素,扩容+1;
ReentrantLock锁,保证add()方法线程安全;


  • get() - 获取元素方法
    /**
     * 入口 - 线程不安全
     */
    public E get(int index) {
     
        // 获取元素
        return get(getArray(), index);
    }

    // 通过下标获取元素
    private E get(Object[] a, int index) {
     
        return (E) a[index];
    }
 

从源码中可以看出,获取元素时就是获取数组元素,通过下标直接获取即可;

知识点:
无ReentrantLock锁,get()方法线程不安全;


  • remove() - 删除元素方法
    /**
     * 入口 - ReentrantLock锁:保证线程安全
     */
    public E remove(int index) {
     
        // 设置锁
        final ReentrantLock lock = this.lock;
        // 加锁
        lock.lock();
        try {
     
            // 获取源数组
            Object[] elements = getArray();
            // 获取源数组长度
            int len = elements.length;
            // 获取要删除元素
            E oldValue = get(elements, index);
            // 计算需移动的个数
            int numMoved = len - index - 1;
            // 0:删除的元素为数组最后一个
            if (numMoved == 0)
                // 直接复制(源数组长度-1)的新数组
                setArray(Arrays.copyOf(elements, len - 1));
            else {
     
                /**
                 * public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
                 *  说明此方法参数作用:
                 *      src:源数组
                 *      srcPos:源数组要复制的起始位置
                 *      dest:目的数组
                 *      destPos:目的数组放置的起始位置
                 *      length:复制的长度
                 */
                // 创建(源数组长度-1)的新数组
                Object[] newElements = new Object[len - 1];
                // 看过前几篇文章的猿友应该知道,这里的源数组和目标数组是两个数组,所以这里用的是复制数组,而不是移动覆盖
                // 第一次copy:将数组中删除元素前的所有元素复制到目标数组
                System.arraycopy(elements, 0, newElements, 0, index);
                // 第二次copy:将数组中删除元素后的所有元素复制到目标数组
                System.arraycopy(elements, index + 1, newElements, index,
                        numMoved);

                // 将目标数组赋值给源数组
                setArray(newElements);
            }

            // 返回删除元素值
            return oldValue;
        } finally {
     
            // 释放锁
            lock.unlock();
        }

    }
    

从源码中可以看出,删除元素实则为数组复制copy的过程;

注意:
相信对于有经验的开发猿友来说,已经看出来了,CopyOnWriteArrayList删除元素时与ArrayList删除元素时不同,ArrayList删除元素时,源数组与目标数组是同一数组,所以用的是移动覆盖的过程,而CopyOnWriteArrayList删除元素时,参考上述代码我们发现,源数组与目标数组非同一数组,故用的是copy复制新数组的方式,并且复制了2次;

  • 过程演示:

  • 1.定义源数组:
    源数组

  • 2.定义目标数组:length=(源数组-1)
    年轻人不讲武德,一起聊聊List集合(四)_第2张图片

  • 3.删除下标为1的元素(讲)

    • 3-1.第一次copy:
      年轻人不讲武德,一起聊聊List集合(四)_第3张图片
    • 3-2.第二次copy:
      年轻人不讲武德,一起聊聊List集合(四)_第4张图片

  • CopyOnWriteArrayList总结:
  1. 底层为数组;
  2. 构造初始化,数组长度为0的空数组,集合size为0,数组length为0;
    每次新增元素,扩容为(源数组长度+1);
  3. 通过下标去获取元素,故查询效率高,增删效率低;
  4. (新增/删除)元素方法-线程安全,获取元素方法-线程不安全;
  5. 无modCount;


2. ArrayList

不讲武德,一起聊聊List集合之ArrayList


3. LinkedList

不讲武德,一起聊聊List集合之LinkedList


4. Vector

不讲武德,一起聊聊List集合之Vector



~~   码上福利


大家好,我是猿医生:

在码农的大道上,唯有自己强才是真正的强者,求人不如求己,静下心来,扫码一起学习吧…
https://marketing.csdn.net/poster/145?utm_source=765669642

你可能感兴趣的:(集合源码系列,java)