此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

5、删除元素

public boolean remove(Object o)

使用方法:

list.remove("hello")

源代码:

    /**
     * 删除list中的第一个o
     * 1)获取锁、上锁
     * 2)获取旧数组、旧数组的长度len
     * 3)如果旧数组长度为0,返回false
     * 4)如果旧数组有值,创建新数组,容量为len-1
     * 5)从0开始遍历数组中除了最后一个元素的所有元素
     * 5.1)将旧数组中将被删除元素之前的元素复制到新数组中,
     * 5.2)将旧数组中将被删除元素之后的元素复制到新数组中
     * 5.3)将新数组赋给全局array
     * 6)如果是旧数组的最后一个元素要被删除,则
     * 6.1)将旧数组中将被删除元素之前的元素复制到新数组中
     * 6.2)将新数组赋给全局array
     */
    public boolean remove(Object o) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();//获取原数组
            int len = elements.length;//获取原数组长度
            if (len != 0) {//如果有数据
                // Copy while searching for element to remove
                // This wins in the normal case of element being present
                int newlen = len - 1;//新数组长度为原数组长度-1
                Object[] newElements = new Object[newlen];//创建新数组                 for (int i = 0; i < newlen; ++i) {//遍历新数组(不包含最后一个元素)
                    if (eq(o, elements[i])) {
                        // 将旧数组中将被删除元素之后的元素复制到新数组中
                        for (int k = i + 1; k < len; ++k)
                            newElements[k - 1] = elements[k];
                        setArray(newElements);//将新数组赋给全局array
                        return true;
                    } else
                        newElements[i] = elements[i];//将旧数组中将被删除元素之前的元素复制到新数组中
                }                 if (eq(o, elements[newlen])) {//将要删除的元素时旧数组中的最后一个元素
                    setArray(newElements);
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

判断两个对象是否相等:

    /**
     * 判断o1与o2是否相等
     */
    private static boolean eq(Object o1, Object o2) {
        return (o1 == null ? o2 == null : o1.equals(o2));
    }

注意点:

  • 需要加锁

  • ArrayList的remove使用了System.arraycopy(这是一个native方法),而这里没使用,所以理论上这里的remove的性能要比ArrayList的remove要低

6、遍历所有元素

iterator()  hasNext()  next()

使用方法:

讲解用的:

        Iterator<String> itr = list.iterator();
        while(itr.hasNext()){
            System.out.println(itr.next());
        }

实际中使用的:

        for(String str : list){
            System.out.println(str);
        }

源代码:

    public Iterator<E> iterator() {
        return new COWIterator<E>(getArray(), 0);
    }
    private static class COWIterator<E> implements ListIterator<E> {
        private final Object[] snapshot;//数组快照
        private int cursor;//可看做数组索引         private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;//将实际数组赋给数组快照
        }
        
        public boolean hasNext() {
            return cursor < snapshot.length;//0~snapshot.length-1
        }
        
        public E next() {
            if (!hasNext())
                throw new NoSuchElementException();
            return (E) snapshot[cursor++];
        }

说明:这一块儿代码非常简单,看看代码注释就好。

注意:

由于遍历的只是全局数组的一个副本,即使全局数组发生了增删改变化,副本也不会变化,所以不会发生并发异常。但是,可能在遍历的过程中读到一些刚刚被删除的对象。

注意点:

总结:

  • 线程安全,读操作时无锁的ArrayList

  • 底层数据结构是一个Object[],初始容量为0,之后每增加一个元素,容量+1,数组复制一遍

  • 增删改上锁、读不上锁

  • 遍历过程由于遍历的只是全局数组的一个副本,即使全局数组发生了增删改变化,副本也不会变化,所以不会发生并发异常

  • 读多写少且脏数据影响不大的并发情况下,选择CopyOnWriteArrayList

疑问:

  • 每增加一个新元素,都要进行一次数组的复制消耗,那为什么每次不将数组的元素设大(比如说像ArrayList那样,设置为原来的1.5倍+1),这样就会大大减少因为数组元素复制所带来的消耗?

  • get(int)操作会发生脏读,为什么?

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 【译文】抽象漏洞法则

CopyOnWriteArrayList源码解析(2)的更多相关文章

  1. ArrayList、CopyOnWriteArrayList源码解析(JDK1.8)

    本篇文章主要是学习后的知识记录,存在不足,或许不够深入,还请谅解. 目录 ArrayList源码解析 ArrayList中的变量 ArrayList构造函数 ArrayList中的add方法 Arra ...

  2. CopyOnWriteArrayList源码解析

    Java并发包提供了很多线程安全的集合,有了他们的存在,使得我们在多线程开发下,可以和单线程一样去编写代码,大大简化了多线程开发的难度,但是如果不知道其中的原理,可能会引发意想不到的问题,所以知道其中 ...

  3. CopyOnWriteArrayList源码解析(1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. ht ...

  4. 第三章 CopyOnWriteArrayList源码解析

    注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5102342.html ...

  5. CopyOnWriteArraySet源码解析

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOn ...

  6. 第四章 CopyOnWriteArraySet源码解析

    注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOnWriteArrayList源码解析. http://www.cnblogs.com/jav ...

  7. EventBus源码解析 源码阅读记录

    EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...

  8. EventBus3.0源码解析

    本文主要介绍EventBus3.0的源码 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递. EventBus使用简单,并将事件发布和订阅充 ...

  9. Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例

    概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解Arra ...

随机推荐

  1. Flex 得到一个对象的所有属性

    var obj:Object =..... ///需要处理的对象 fieldname:Array = ObjectUtil.getClassInfo(obj)["properties&quo ...

  2. [C#.NET]最简单的实现文本框的水印效果

    C#项目开发中在设计登录界面时,经常会遇到TextBox的水印提示要求.这里简单描述一下项目在实现水印提示的过程设置.如下图图1所示. 图1  窗体布局 一.窗体布局(如图1所示) 1.  在窗体中放 ...

  3. NOIP水题测试(2017082401)

    哈,水题测试又来了! 上次的水题简单吧! 答案是以单题形式发布的(旅行家的预算随后发布). 下面来看今天的题,还是水题. 时间限制:5小时 题目一:看上去就很水 题目二:比上面一题还水 题目三:数的划 ...

  4. MySQL open_files_limit相关设置

    背景:      数据库链接不上,报错: root@localhost:/var/log/mysql# mysql -uzjy -p -h192.168.1.111 --default-charact ...

  5. kbmmw 中JSON 操作入门

    现在各种系统中JSON 用的越来越多.delphi 也自身支持JSON 处理. 今天简要说一下kbmmw 内部如何使用和操作JSON. kbmmw 中json的操作是以TkbmMWJSONStream ...

  6. Enjoy Markdown!

    有一个神奇的语言,比HTML简单,它巧妙地将内容与格式结合在一起,它就是Markdown! 下面是一个用C语言写的四则运算小测试~ #include <stdio.h> #include ...

  7. 2018.11.03 NOIP模拟 图(bfs/最短路)

    传送门 显然如果AAA到BBB或者CCC到DDD走的不是最短路一定是有一段路径重合了,于是可以O(n2)bfsO(n^2)bfsO(n2)bfs出两点之间的最短距离然后枚举两个点作为重合的端点来更新答 ...

  8. scrapy知识积累

    Scrapy 中文文档https://scrapy-chs.readthedocs.io/zh_CN/latest/intro/overview.html 创建项目 scrapy startproje ...

  9. 【MVC】bootstrap-paginator 分页

    [MVC]bootstrap-paginator 分页http://www.cnblogs.com/stoneniqiu/p/4000041.htmlhttp://www.cnblogs.com/Le ...

  10. 第27章:MongoDB-索引--唯一索引

    ①唯一索引 唯一索引的目的是为了让数据库的某个字段的值唯一,为了确保数据的都是合法的,但是唯一索引在插入数据时会对数据进行检查,一旦重复会抛出异常,效率会比较低,唯一索引只是保证数据库数据唯一的最后一 ...