• 探索 ArrayList 原理 - 第二节 ArrayList 继承体系源码分析


    探索 ArrayList 原理

    jdk1.8 API
    黑马教学视频: java进阶教程丨全面深入解析ArrayList原理(源码分析+面试讲解)

    2. ArrayList 继承体系

    • 源码结构图如下
      在这里插入图片描述
    • ArrayList主要实现了List接口,同时标记为可以序列化Serializable、可复制CloneAble、支持随机访问RandomAccess。

    3. ArrayList 继承体系源码分析

    • ArrayList
      public class ArrayList<E> extends AbstractList<E>
           implements List<E>, RandomAccess, Cloneable, java.io.Serializable
      
      • 1
      • 2
    3.1 Serializable 接口
    • 序列化
      • 1、将一个Java对象变成字节序列,方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失,
      • 2、变换成字节序列也更便于网络运输和传播。
      • 3、序列化机制从某种意义上来说也弥补了平台化的一些差异,毕竟转换后的字节流可以在其他平台上进行反序列化来恢复对象,因此可以实现跨平台存储。
    • Serializable 接口,就是序列化接口,实现此接口的类将不会使任何状态序列化或反序列化;
      • 序列化: 将 对象 的数据写入到文件(写对象
      • 反序列化: 将文件中 对象 的数据读取出来
      • 序列化
        • 示例如下:写对象示例 注意必须传一个对象,序列化也指的就是序列化对象
        • ApeEntity 实例
             @NoArgsConstructor
          @AllArgsConstructor
          @Data
          public class ApeEntity {
          
             private String id;
          
             private String sbmc;
          
             private String sblx;
          
             private String ipdz;
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
        • 测试写入文件
              public static void main(String[] args) throws IOException {
                  ApeEntity ape1 = new ApeEntity("1001","天地","天地","192.168.1.2");
                  ApeEntity ape2 = new ApeEntity("1002","海康","海康","192.168.1.2");
                  ApeEntity ape3 = new ApeEntity("1003","大华","大华","192.168.1.2");
                  ArrayList<ApeEntity> list = new ArrayList<>();
                  list.add(ape1);
                  list.add(ape2);
                  list.add(ape3);
                  writeObjStream("D:/temp/exchange_snmp1.log", list);
              }
              public static void writeObjStream(String filepath, ArrayList<ApeEntity> list) throws IOException {
              
              FileOutputStream outputStream = new FileOutputStream(filepath);
              ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
              objectOutputStream.writeObject(list);
              objectOutputStream.close();
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
        • 实体对象 ApeEntity 未实例化 ,执行上面代码报错;
              Exception in thread "main" java.io.NotSerializableException: arrayList.ApeEntity
             	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
             	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
             	at java.util.ArrayList.writeObject(ArrayList.java:766)
             	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
             	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
             	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
             	at java.lang.reflect.Method.invoke(Method.java:498)
             	at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1140)
             	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
             	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
             	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
             	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
             	at arrayList.source.ArrayListSerializableDemo.writeObjStream(ArrayListSerializableDemo.java:77)
             	at arrayList.source.ArrayListSerializableDemo.main(ArrayListSerializableDemo.java:28)
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
        • ApeEntity 实例序列化
              @NoArgsConstructor
             @AllArgsConstructor
             @Data
             public class ApeEntity implements Serializable {
             
                 private String id;
             
                 private String sbmc;
             
                 private String sblx;
             
                 private String ipdz;
             }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 序列化后效果,在目的位置生成了文件
            -
      • 反序列化
      • 示例代码如下:
            public static void main(String[] args) throws IOException, ClassNotFoundException {
                readObjStream("D:/temp/exchange_snmp1.log");
            }
            public static void readObjStream(String filepath) throws IOException, ClassNotFoundException {
                FileInputStream fileInputStream = new FileInputStream(filepath);
                ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
                List<ApeEntity> list = (List<ApeEntity>) objectInputStream.readObject();
                objectInputStream.close();
                System.out.println(list.toString());
            }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 反序列化执行效果,
          Connected to the target VM, address: '127.0.0.1:50838', transport: 'socket'
          [ApeEntity(id=1001, sbmc=天地, sblx=天地, ipdz=192.168.1.2), ApeEntity(id=1002, sbmc=海康, sblx=海康, ipdz=192.168.1.2), ApeEntity(id=1003, sbmc=大华, sblx=大华, ipdz=192.168.1.2)]
          Disconnected from the target VM, address: '127.0.0.1:50838', transport: 'socket'
          
          • 1
          • 2
          • 3
    3.2 CloneAble 接口
    • 一个类实现CloneAble 接口来指示Object.clone() 方法,该方法对于该类的实例进行字段的复制是合法的。

    • 简而言之:克隆就是依据已经有的数据,创造一份新的完全一样的数据拷贝

    • 克隆的前提条件:

      • 1、被克隆对象所在的类必须实现 CloneAble 接口;
      • 2、必须重写clone方法;
    • 源码分析:

          public Object clone() {
              try {
                 // 调用父类(Object)的clone()方法,克隆一个object对象,然后强转为ArrayList
                  ArrayList<?> v = (ArrayList<?>) super.clone();
                    // 调用Arrays类的copyOf,将ArrayList的elementData数组赋值给副本的elementData数组 
                  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);
              }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    • clone 的基本使用

          public static void main(String[] args)  {
              Student student1 = new Student("xiaoming1","man",0);
              Student student2 = new Student("xiaoming2","man",0);
              Student student3 = new Student("xiaoming3","man",0);
      
              ArrayList<Student> stuList = new ArrayList<>();
              stuList.add(student1);
              stuList.add(student2);
              stuList.add(student3);
      
              ArrayList<Student> stuList2  = (ArrayList)stuList.clone();
              for (Student stu:stuList2
                   ) {
                  System.out.println(stu);
              }
      
              System.out.println(stuList == stuList2);
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      示例结果

      	Student(name=xiaoming1, sex=man, age=0)
      	Student(name=xiaoming2, sex=man, age=0)
      	Student(name=xiaoming3, sex=man, age=0)
      	false
      
      • 1
      • 2
      • 3
      • 4
    • 案例分析:

      • 已知学生对象{姓名:小明,性别 男,年龄10},现需要将学生小明复制到另一个对象中去,且两个之间互相不影响
        • 方法一:
              public  static  void test1(){
                  Student stu1 = new Student("小明","男",10,null);
                  Student stu2 = new Student();
                  stu2.setName(stu1.getName());
                  stu2.setSex(stu1.getSex());
                  stu2.setAge(stu1.getAge());
              }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
        • 方法二 克隆:注意student 需要实现 Cloneable,并重写clone方法;
          • studet.java
                    @Data
            		@AllArgsConstructor
            		@NoArgsConstructor
            		public class Student Cloneable{
            		    private String name;
            		    private String sex;
            		    private Integer age;						
            		    @Override
            		    public Object clone() throws CloneNotSupportedException {
            		        return super.clone();
            		    }
            		}
            
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
          • 克隆
                    Student stu1 = new Student("小明","男",10);
                    Object clone = stu1.clone();
                    System.out.println(stu1 == clone);
                    System.out.println(stu1);
                    System.out.println(clone);
                    stu1.setAge(100);
                    System.out.println(stu1);
                    System.out.println(clone);
            
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            示例结果如下
            	false
            	Student(name=小明, sex=, age=10)
            	Student(name=小明, sex=, age=10)
            	Student(name=小明, sex=, age=100)
            	Student(name=小明, sex=, age=10)
            
            • 1
            • 2
            • 3
            • 4
            • 5

          以上实例简单使用浅克隆,完全可以满足需求,如果说student 中还内嵌对象,如学生中还技能对象,那么浅克隆就满足不了了;

          • 如下学生类示例
            public class Student implements Serializable ,Cloneable{
                private String name;
                private String sex;
                private Integer age;
                private Skill skill; //技能,特长
            
                @Override
                public Object clone() throws CloneNotSupportedException {
                    return super.clone();
                }
            }
            
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            技能,特长类
            public class Skill {
                private  String name;
            }
            
            • 1
            • 2
            • 3
          • 示例代码如下
                public static void test4() throws CloneNotSupportedException {
                    Student stu1 = new Student();
                    stu1.setName("小明");
                    stu1.setSex("男");
                    stu1.setAge(10);
                    Skill skill = new Skill("跆拳道");
                    stu1.setSkill(skill);
                    Object clone = stu1.clone();
                    System.out.println(stu1 == clone);
                    System.out.println(stu1);
                    System.out.println(clone);
                    stu1.setAge(100);
            
                    skill.setName("武术");
                    stu1.setSkill(skill);
                    System.out.println(stu1);
                    System.out.println(clone);
                } 
            
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 15
            • 16
            • 17
            • 18
          • 示例代码效果如下
            		false
            		Student(name=小明, sex=, age=10, skill=Skill(name=跆拳道))
            		Student(name=小明, sex=, age=10, skill=Skill(name=跆拳道))
            		Student(name=小明, sex=, age=100, skill=Skill(name=武术))
            		Student(name=小明, sex=, age=10, skill=Skill(name=武术))
            
            • 1
            • 2
            • 3
            • 4
            • 5

            当修改学生的值时,影响到了克隆出来学生的值 ,此时浅克隆已经满足不了需求,这时我们需要用到深克隆

          • 深克隆
            • 技能、特长类实现 Cloneable
              public class Skill implements Cloneable{
                  private  String name;
              
                  @Override
                  protected Object clone() throws CloneNotSupportedException {
                      return super.clone();
                  }
              }
              
              • 1
              • 2
              • 3
              • 4
              • 5
              • 6
              • 7
              • 8
            • 学生类重写 Clone 方法
              	public class Student implements Serializable ,Cloneable{
              	    private String name;
              	    private String sex;
              	    private Integer age;
              	    private Skill skill;
              	
              	    @Override
              	    public Object clone() throws CloneNotSupportedException {
              	        //深克隆不能简单的调用父类的方法
              	
              	        //引用super.clone 先克隆出一个 object,然后向下转型
              	        Student stu = (Student)super.clone();
              	        Skill skill = (Skill)this.skill.clone();
              	        stu.setSkill(skill);
              	        return stu;
              	    }
              	
              	}
              
              • 1
              • 2
              • 3
              • 4
              • 5
              • 6
              • 7
              • 8
              • 9
              • 10
              • 11
              • 12
              • 13
              • 14
              • 15
              • 16
              • 17
              • 18
          • 直接还是用 test4() 示例测试,效果如下
            	false
            	Student(name=小明, sex=, age=10, skill=Skill(name=跆拳道))
            	Student(name=小明, sex=, age=10, skill=Skill(name=跆拳道))
            	Student(name=小明, sex=, age=10, skill=Skill(name=武术))
            	Student(name=小明, sex=, age=10, skill=Skill(name=跆拳道))
            
            • 1
            • 2
            • 3
            • 4
            • 5

            相互之间已经做到了互不影响

    3.3 RandomAccess 接口
    • 标记接口由List实现使用,以表明它们支持快速(通过为恒定时间)随机访问
    • 此接口的主要目的是允许通用算法更改其行为,以便在应用于随机访问列表顺序访问列表时提供良好的性能
    • 需要值得让人注意的是,List 实现了此接口,如果对于类的典型实例,此种方式比迭代器实例(iterator)更快
      • 典型实例
        	for(int i = 0 , n = list.size(); i< n ;i++) 
        	  list.get(i);
        
        • 1
        • 2
      • 迭代器实例
        	for(Iterator i=list.iterator(); i.hasNext();) 
        	  i.next();
        
        • 1
        • 2
    • 案例演示1 ArrayList 随机访问列表顺序访问列表哪种方式更快
          public static void test(){
              ArrayList<Integer> stuList = new ArrayList<>();
              for(int i = 0;i < 100000 ; i++){
                  stuList.add(i);
              }
      		//测试随机访问时间
              long startMillis = System.currentTimeMillis();
              for (int i = 0 ; i< stuList.size();i++) {
                  stuList.get(i);
              }
              long endMillis = System.currentTimeMillis();
              System.out.println("ArrayList-随机访问列表耗时:"+(endMillis - startMillis));
      
      	    //测试顺序访问时间
              Iterator<Integer> iterator = stuList.iterator();
              startMillis = System.currentTimeMillis();
              while (iterator.hasNext()){
                   iterator.next();
              }
              endMillis = System.currentTimeMillis();
              System.out.println("ArrayList-顺序访问列表耗时:"+(endMillis - startMillis));
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      示例结果如下
      ArrayList-随机访问列表耗时:1
      ArrayList-顺序访问列表耗时:3
      
      • 1
      • 2

      因为ArrayList 是实现了RandomAccess 接口的,所以它会比Iterator 快;

    • 案例演示2 LinkList 随机访问列表顺序访问列表哪种方式更快
          public static void test2(){
              LinkedList<Integer> stuList = new LinkedList<>();
              for(int i = 0;i < 100000 ; i++){
                  stuList.add(i);
              }
      
              long startMillis = System.currentTimeMillis();
              for (int i = 0 ; i< stuList.size();i++) {
                  stuList.get(i);
              }
              long endMillis = System.currentTimeMillis();
              System.out.println("LinkedList-随机访问列表耗时:"+(endMillis - startMillis));
      
              Iterator<Integer> iterator = stuList.iterator();
              startMillis = System.currentTimeMillis();
              while (iterator.hasNext()){
                  iterator.next();
              }
              endMillis = System.currentTimeMillis();
              System.out.println("LinkedList-顺序访问列表耗时:"+(endMillis - startMillis));
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      示例结果如下
      	LinkedList-随机访问列表耗时:4581
      	LinkedList-顺序访问列表耗时:1
      
      • 1
      • 2

      因为LinkedList 是没有RandomAccess 接口的,所以它会比随机访问更快;还有需要值 得注意的时候,在添加数据的时候我们很明显能看出来linkedList 添加数据是比较慢的:
      linkedList 添加数据是比较慢原因:当数据量大时,ArrayList每次扩容都能得到很大的新空间,解决了前期频繁扩容的劣势,而LinkedList虽然有尾指针,但是每次add都要将对象new成一个Node(而ArrayList直接数组对应位置元素赋值)

    3.3.1 ArrayList 实际开发应用场景应用
    • 基于实现 RandomAccess接口之后,随机访问列表比顺序访问列表效率更高,所以建议当查询出结果之后进行判断是否实现了接口RandomAccess,然后判断自己使用哪种方式的遍历;
    • 直接上判断代码如下:
        //模拟从数据库查询出数据结果
        List<User> list = dao.query(example);
      
        //判断
        if(list.instanceof RandomAccess){
            //随机访问
           for(int i = 0; i < list.size(); i++){
           	……
           }
        }else{
        	//顺序访问
        	for(User user:List){
        		……
        	}
        }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
    3.4 ArrayList 抽象类
    • ArrayList extends AbstractList

      • AbstractList extends AbstractCollection
          java中所有类都继承 Object,如下图 ArrayList的继承结构。
          在这里插入图片描述
    • AbstractList是一个抽象类,实现了List接口,List定义了一些List通用方法,而AbstractList抽象类中可以有抽象方法,还可以有具体的实现方法, AbstractList实现接口中一些通用的方法,实现了基础的add/get/indexOf/iterator/subList/RandomAccessSubList方法,ArrayList再继承AbstractList,拿到通用基础的方法,然后自己在实现一些自己特有的方法,这样的好处是:让代码更简洁,继承结构最底层的类中通用的方法,减少重复代码。

  • 相关阅读:
    计算机体系结构实验二——DLX/MIPS/RISC-V指令格式
    npm,registry,镜像源,npm切换源,yarn,cnpm,taobao,nrs
    WebGL:基础练习 / 简单学习 / demo / canvas3D
    CCF2022版最新目录已发布,最新变动情况分析
    vue3.0+ts+element ui中如何使用svg图片
    前端入门到入土?
    学习 Axure RP 的不同阶段
    Qt5开发从入门到精通——第九篇三节( Qt5 文件及磁盘处理—— 文件大小及路径获取实例)
    windows搭建elasticsearch和elasticsearch-head/kibana
    沁恒CH9102是一个USB总线的转接芯片,实现USB转高速异步串口
  • 原文地址:https://blog.csdn.net/pymxb1991/article/details/126558853