arraylist源码解读

Posted 嘿boom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

以上是关于arraylist源码解读的主要内容,如果未能解决你的问题,请参考以下文章

源码阅读Java集合 - ArrayList深度源码解读

源码解读:ArrayList源码解析(JDK8)

ArrayList源码解读(jdk1.8)

ArrayList源码解读(部分)

arraylist源码解读

ArrayList源码解读