• ArrayList源码解读


    1.可以先看这一篇

    https://www.cnblogs.com/zhangyinhua/p/7687377.html

    2.arraylist.get()有两种,一种通过索引,一种通过对象。注意:

      ArrayList<Integer> alist=new ArrayList<>();

      alist.add(1);alist.add(2);

       alist.get(1);  //默认是通过索引,所以取出来的是2

    3.arraylist的删除

      3.1.for循环使用remove()删除坐标来删除元素,会使得忽略下一个被连续删除的节点。如下面这段代码,想删除b,c却只删除了b。因为删除了b之后,数组数据向前移动了一位,而i还要继续增大,所以就跳过了c

      解决方法是:a.remove后i--   b.改为从后向前遍历。注意:for()里面i的初值为alist.size()-1

      

     1 public class ArrayListTest {
     2     public static void main(String[] args) {
     3         ArrayList<String> alist=new ArrayList<>();
     4         alist.add("a");
     5         alist.add("b");
     6         alist.add("c");
     7         alist.add("d");
     8         System.out.println(alist.toString());
     9         for(int i=0;i<alist.size();i++) {
    10             if(alist.get(i).equals("b") || alist.get(i).equals("c")) {
    11                 alist.remove(i);    //应该是alist.reomove(i--);
    12             }
    13         }
    14         System.out.println(alist.toString());
    15     }
    16 }
    17 
    18 
    19 //结果:
    20 [a, b, c, d]
    21 [a, c, d]

      

      3.2 for each,iterator,listiterator遍历时候,不能用arraylist的修改方法同时修改,因为iterator在遍历时会检查modCount,而arraylist修改方法会增加modCount

        解决方法:①iterator,listiterator要使用迭代器自身的修改方法

             ②使用CopyOnWriteArrayList

    4.CopyOnWriteArrayList

      

    1 //for each  这种方法不报错
    2 
    3         List<String> list=new CopyOnWriteArrayList<>();
    4         list.add("a");
    5         list.add("b");
    6         list.add("c");
    7         for(String s:list) {
    8             list.remove(s);
    9         }
     1 // 迭代器中,list的remove,也不报错
     2 
     3 
     4         List<String> list=new CopyOnWriteArrayList<>();
     5         list.add("a");
     6         list.add("b");
     7         list.add("c");
     8 
     9         Iterator itr=list.iterator();
    10         while(itr.hasNext()) {
    11             list.remove(itr.next());
    12         }

      https://blog.csdn.net/linsongbin1/article/details/54581787

       

      

      4.1,什么是写时复制(Copy-On-Write)容器?

    写时复制是指:在并发访问的情景下,当需要修改JAVA中Containers的元素时,不直接修改该容器,而是先复制一份副本,在副本上进行修改。修改完成之后,将指向原来容器的引用指向新的容器(副本容器)。

      4.2,写时复制带来的影响

    ①由于不会修改原始容器,只修改副本容器。因此,可以对原始容器进行并发地读。其次,实现了读操作与写操作的分离,读操作发生在原始容器上,写操作发生在副本容器上。

    ②数据一致性问题:读操作的线程可能不会立即读取到新修改的数据,因为修改操作发生在副本上。但最终修改操作会完成并更新容器,因此这是最终一致性。

      适用场景(cyc总结)

        CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,因此很适合读多写少的应用场景。

        但是 CopyOnWriteArrayList 有其缺陷:

        •     内存占用:在写操作时需要复制一个新的数组,使得内存占用为原来的两倍左右;
        •     数据不一致:读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中。

        所以 CopyOnWriteArrayList 不适合内存敏感以及对实时性要求很高的场景。

      4.3,在JDK中提供了CopyOnWriteArrayList类和CopyOnWriteArraySet类,

     5.clear()与new ArrayList<>()

       clear()方法会将指向的内存中的数组清空,置为null,等待垃圾回收机制回收

      但是new ArrayList<>(),会重新开辟一块内存,不影响之前的。

    Arraylist复习思路:

    1.继承的类和实现的接口,各接口代表什么意思(LIst, RandomAccess, Cloneable, Serializable)。处于框架图中的哪个位置

    2.底层结构(数组)

    3.变量的初始设置(容量10)

    4.增,  add()(数组初始化,扩容。  多种add方法)

    5.删,clear() (数组各个位置置为null, 等待GC)/  remove() 

    6.改,  set()

    7.查,  get()  (两种方式,默认按索引取值,也可以取对象)

    8.遍历,

      1)常见循环for(;;)等

      2)Iterator迭代器(注意for-each是本质上也是iterator)

      a.ListIterator的常用方法

      b.modCount和快速失败(不仅不允许多线程更改,单线程更改也有限制)/fail-fast在迭代器模式下起作用,因为迭代器要判断modCount,而list自带的增删改查会修改这一值,导致ConcurrentModificationException

      c.遍历的同时如何增删改查(推荐用迭代器自带的)

    9.RandomAccess,随机访问的含义

    10,cloneable

    11.序列化,elementData为什么是transient以及writeObject()与readObject()

    12. CopyOnWriteArrayList没理解  https://www.jianshu.com/p/59d146148fdd

    https://juejin.im/post/5aaa2ba8f265da239530b69e   

     13. Arrays.asList(T... a)出来的 ArrayList为什么不能add https://www.2cto.com/kf/201806/751606.html

    fail-fast/fail-safe:

    https://www.nowcoder.com/questionTerminal/95e4f9fa513c4ef5bd6344cc3819d3f7

    一:快速失败(fail—fast)

              在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。

              原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

          注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。

          场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。使用 Collections.synchronizedXXX() 创建的线程安全的集合也是 Fail-fast。

    二:安全失败(fail—safe)

          采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

          原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。

          缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

              场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

  • 相关阅读:
    SVN——Jenkins自动发布
    IIS之虚拟目录学习
    SVN迁移
    通过配置host,自定义域名让本地访问
    比较两个时间的大小 举例:CompareDate("12:00","11:15")
    [转]SQL Server 批量完整备份
    js前台编码,asp.net后台解码 防止前台传值到后台为乱码
    前端将图片二进制流显示在html端
    【转】解析<button>和<input type="button"> 的区别
    利用bat批处理——实现数据库的自动备份和删除
  • 原文地址:https://www.cnblogs.com/heyboom/p/9042968.html
Copyright © 2020-2023  润新知