ArrayList的非快速失败机制和Vector与ArrayList的(基础)分析

Stella981
• 阅读 486

/**
 * 1、copyOnWriteArrayList  如何实现(non-fastfail)非快速失败机制???
 * 2、Vector底层源码,主要看属性,构造函数、增删改查方法、明白ArrayList与Vector之间的区别与联系
 * (底层数据结构、效率、扩容机制、是否线程安全)
 *
 */

一、 ArrayList概述:  

   1、 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。

   2、 ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。

  3、  ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输:该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。

5、实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问:
E elementData(int index) {
        return (E) elementData[index];
    }

6、实现了Cloneable接口,能被克隆:
public Object clone() {
        try {
            ArrayList v = (ArrayList) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

   7、 ArrayList是线程非安全的;每个ArrayList在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。 注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。(涉及到快速失败机制)

   8、fast-fail机制  ArrayList的自我保护机制
        *********快速失败机制实际上依赖于modCout: 存储结构修改次数(增加和删除、扩容、缩容),和expectedModCount的对照
    modCount定义于AbstractList,(ArrayList继承AbstractList) 
使用迭代器或者foreach遍历时,如果调用集合add/remove方法修改集合的结构 ,则会抛出 ConcurrentModificationexception。    

   8.1、单线程,在iterator遍历的同时,插入新的参数。会导致快速失败

public class FastFailDemo {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
 
        for (Integer integer : list) {
            list.add(integer);
            System.out.println(integer);
        }
 
    }
}

  8.2、单线程,使用randomaccess遍历List,即使在边读边写也不会出现快速失败

public class FastFailDemo {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        for(int i =0 ; i< list.size(); i++) {
            list.add(i);
            System.out.println(i);
        }
 
    }
}

  8.3、多线程,在一个线程读时,另一个线程写入list,读线程会快速失败
public class FastFailDemo {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        new MyThread1(list).start();
        new MyThread2(list).start();
    }
}
 
class MyThread1 extends Thread {
    private List list;
 
    public MyThread1( List list) {
        this.list = list;
    }
 
    @Override
    public void run() {
        for (Integer integer : list) {
            System.out.println("MyThread1" + list.size());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
 
class MyThread2 extends Thread {
    private List list;
 
    public MyThread2( List list) {
        this.list = list;
    }
 
    @Override
    public void run() {
 
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            list.add(i);
            System.out.println("MyThread2 " + list.size());
        }
    }
}

二、arrayLsit的非快速失败机制和与vector底层实现的对比

1、非快速失败机制

1)、fail-fast机制,是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。若在多线程环境下使用fail-fast机制的集合,使用“java.util.concurrent包下的类”去取代“java.util包下的类”。
将代码:
List list = new ArrayList<>();
替换为:
List list = new CopyOnWriteArrayList<>();

2)、
CopyOnWriteArrayList与ArrayList不同:

(01) 和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承于AbstractList,它仅仅只是实现了List接口。
(02) ArrayList的iterator()函数返回的Iterator是在AbstractList中实现的;而CopyOnWriteArrayList是自己实现Iterator。
(03) ArrayList的Iterator实现类中调用next()时,会“调用checkForComodification()比较‘expectedModCount’和‘modCount’的大小”;但是,CopyOnWriteArrayList的Iterator实现类中,没有所谓的checkForComodification(),更不会抛出ConcurrentModificationException异常!
虽然,获取到了更新后的ArrayList的大小;但是,当前迭代的结果并不是更新后的arrayList;
public class FastFailDemo {
    public static void main(String[] args) {
        List list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 5; i++) {
            list.add(i);
        }
        new MyThread1(list).start();
        new MyThread2(list).start();
    }
}
 
class MyThread1 extends Thread {
    private List list;
 
    public MyThread1( List list) {
        this.list = list;
    }
 
    @Override
    public void run() {
        for (Integer integer : list) {
            System.out.println("MyThread1 大小:" + list.size() + " 当前值:" + integer);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
 
class MyThread2 extends Thread {
    private List list;
 
    public MyThread2( List list) {
        this.list = list;
    }
 
    @Override
    public void run() {
 
        for (int i = 5; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            list.add(i);
            System.out.println("MyThread2 大小:" + list.size());
        }
    }
}

2、ArrayList:
     1)、关键字解释:transient。  (序列化里用到)
     Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
    public class UserInfo implements Serializable {  
     private static final long serialVersionUID = 996890129747019948L;  
     private String name;  
     private transient String psw;  
   
     public UserInfo(String name, String psw) {  
         this.name = name;  
         this.psw = psw;  
     }  
   
     public String toString() {  
         return "name=" + name + ", psw=" + psw;  
     }  
 }  
   
 public class TestTransient {  
     public static void main(String[] args) {  
         UserInfo userInfo = new UserInfo("张三", "123456");  
         System.out.println(userInfo);  
         try {  
             // 序列化,被设置为transient的属性没有被序列化  
             ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(  
                     "UserInfo.out"));  
             o.writeObject(userInfo);  
             o.close();  
         } catch (Exception e) {  
             // TODO: handle exception  
             e.printStackTrace();  
         }  
         try {  
             // 重新读取内容  
             ObjectInputStream in = new ObjectInputStream(new FileInputStream(  
                     "UserInfo.out"));  
             UserInfo readUserInfo = (UserInfo) in.readObject();  
             //读取后psw的内容为null  
             System.out.println(readUserInfo.toString());  
         } catch (Exception e) {  
             // TODO: handle exception  
             e.printStackTrace();  
         }  
     }  
 }
     2)、构造方法: 
   ArrayList提供了三种方式的构造器,可以构造一个默认初始容量为10的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。
    // ArrayList带容量大小的构造函数。    
    public ArrayList(int initialCapacity) {    
        super();    
        if (initialCapacity < 0)    
            throw new IllegalArgumentException("Illegal Capacity: "+    
                                               initialCapacity);    
        // 新建一个数组    
        this.elementData = new Object[initialCapacity];    
    }    
   
    // ArrayList无参构造函数。默认容量是10。    
    public ArrayList() {    
        this(10);    
    }    
   
    // 创建一个包含collection的ArrayList    
    public ArrayList(Collection<? extends E> c) {    
        elementData = c.toArray();    
        size = elementData.length;    
        if (elementData.getClass() != Object[].class)    
            elementData = Arrays.copyOf(elementData, size, Object[].class);    
    }
    3)、 元素存储:

ArrayList提供了set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)这些添加元素的方法。

    4)、元素直接读取
     5)、元素删除E remove(int index); boolean  remove(Object o); void fastRemove(int index);removeRange(int fromIndex,int toIndex)

   
2、对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。其实,Vector也基本上是对数组的操作;
1)、相似之处:
   (1)、Vector和ArrayList的实现方式可以看出非常的类似(底层是数组),增删改查方法相似,同样有快速失败机制
   (2)、扩充容量的方法ensureCapacityHelper。
         与ArrayList相同,Vector在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当容量不足以容纳当前的元素个数时,就先看构造方法中传入的容量增长量参数CapacityIncrement是否为0,如果不为0,就设置新的容量为就容量加上容量增长量,如果为0,就设置新的容量为旧的容量的2倍,如果设置后的新容量还不够,则直接新容量设置为传入的参数(也就是所需的容量),而后同样用Arrays.copyof()方法将元素拷贝到新的数组。
    (3)、同样在查找给定元素索引值等的方法中,两者都将该元素的值分为null和不为null两种情况处理,Vector中也允许元素为null。

2)、关于ArrayList和Vector区别如下:
(1)、ArrayList在内存不够时默认是扩展0.5倍,Vector是默认扩展1倍。
(2)、Vector提供indexOf(obj, start)接口,ArrayList没有。
(3)、Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为线程安全需要更大的系统开销(很多方法加了synchronized同步语句,来保证线程安全)。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
java 面试
1、ArrayList、Vector、LinkedList之间的区别?ArrayList:底层数组,查询快,增删慢,线程不安全,效率高Vector:底层数组,查询快(由于线程安全,其实查询也不快),增删慢,线程安全,效率低LinkedList:底层双重链表,查询慢,增删快,线程不安全,效率高。3、列举Co
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这