ArrayList底层学习01 -- 实现RandomAccess、Cloneable、java.io.Serializable接口深层理解

本文详细介绍了ArrayList类的特性,包括实现Serializable接口支持序列化,实现Cloneable接口允许对象复制,以及实现RandomAccess接口提高随机查询效率。通过实例展示了序列化过程、浅拷贝与深拷贝的区别,并通过性能测试对比了ArrayList与LinkedList的随机访问速度差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、描述
ArrayList类继承了List集合实现了RandomAccess、Cloneable、java.io.Serializable接口
这些接口里面什么都没有,就相当于一个标记类
二、对这些标记类的认识
1、实现了java.io.Serializable接口,就表示ArrayList对象支持序列化
什么是序列化??我用代码说明下

  • UserInfo:一个没有实现序列化接口的对象
public class UserInfo {
  private String id;
  private String name;
  private String status;
  public UserInfo(){

  }
}
 UserInfo info = new UserInfo();
 info.setId("i");
 info.setName("cmf");
 //创建对象操作流 -- 序列化
 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("d:\\object.txt"));
 //调用对象操作流写对象的方法,将对象中数据写到文件
 out.writeObject(info);
 out.close();
 //创建对象操作流 -- 反序列化(将数据从文件中读出来)
 ObjectInputStream in = new ObjectInputStream(new FileInputStream("d:\\object.txt"));
 UserInfo result = (UserInfo)in.readObject();
 in.close();
 System.out.println(result);
  • 执行上面代码会报以下错误:
    没有序列化异常
  • 在UserInfo对象中实现Serializable接口,重新运行就不会有问题
    成功打印结果
  • 同样的ArrayList实现了Serializable接口,使用arrayList来进行文件流的读取操作也是可以的
ArrayList arrayList = new ArrayList();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
try {
  //创建对象操作流 -- 序列化
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("d:\\object.txt"));
  //调用对象操作流写对象的方法,将对象中数据写到文件
  out.writeObject(arrayList);
  out.close();
  //创建对象操作流 -- 反序列化(将数据从文件中读出来)
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("d:\\object.txt"));
  ArrayList result = (ArrayList)in.readObject();
  in.close();
  System.out.println(result);
} catch (IOException e) {
  e.printStackTrace();
} catch (ClassNotFoundException e) {
  e.printStackTrace();
}

成功打印结果
2、实现Cloneable接口表示可以使用clone()拷贝方法。

  • ArrayList实现拷贝
 ArrayList arrayList = new ArrayList();
 arrayList.add("1");
 arrayList.add("2");
 arrayList.add("3");
 ArrayList cloneList = (ArrayList)arrayList.clone();
 System.out.println(arrayList == cloneList);
 System.out.println(arrayList);
 System.out.println(cloneList);

结果
ps: 拷贝的数据是不同地址的,数据值完全一致

  • 顺便说下浅拷贝(克隆)和深拷贝(克隆)
    注: 主要是针对对象中引用了别的对象的情况;浅拷贝的话,对于内部引用的对象,并不能完全单独拷贝出来,内部引用对象发生改变的话会影响后期新拷贝出来的新对象,可能说的话比较抽象,我用代码描述下

  • 浅拷贝代码展示:
    写一个Student类实现Cloneable接口
    首先实现这个方法,需要重写clone方法,返回对象可以不是Object,这里我直接返回了Student对象

public class Student implements Cloneable{
  private String id;
  private String name;
  private UserInfo info;
  public Student(){

  }
  public Student(String id,String name,UserInfo info){
    this.id = id;
    this.name = name;
    this.info = info;
  }

  @Override
  public Student clone() throws CloneNotSupportedException {
    Student stu = (Student) super.clone();
    return stu;
  }
}

测试类:

//浅克隆
UserInfo info = new UserInfo();
info.setId("11");
info.setName("cmcm");
Student student = new Student("1","ccc",info);
Student student1 = student.clone();
System.out.println(student);
System.out.println(student1);
info.setName("修改名称");
System.out.println(student);
System.out.println(student1);

结果
ps: 修改了info对象的名称,克隆前后的对象内部的名称发生了改变,说明内部对象只是引用了,并没有完全拷贝出来(没有产生新的内存地址)

  • 深拷贝(克隆)代码展示
    即使是内部引用对象克隆的时候也单独拷贝到新的内存地址,原来的改动不影响新的克隆对象
    测试类和上面浅克隆一样,需要修改student类里面的clone方法,已经对UserInfo对象也实现一下clone方法
    UserInfo.class
public class UserInfo implements Cloneable{
  private String id;
  private String name;
  private String status;
  public UserInfo(){

  }
  public UserInfo(UserEntity entity){
    this.id = entity.getUserId();
    this.name = entity.getUserName();
    this.status = entity.getUserStatus();
  }

  @Override
  public UserInfo clone() throws CloneNotSupportedException {
    return (UserInfo)super.clone();
  }

Student.class

@Override
public Student clone() throws CloneNotSupportedException {
  Student stu = (Student) super.clone();
  UserInfo userInfo = this.info.clone();
  stu.setInfo(userInfo);
  return stu;
}

在重写clone方法时,需要对引用对象也进行clone然后再重新赋值,这样就是两个完全不一样的对象;
结果
3、实现RandomAccess接口,代表随机查询时效率会高于顺序查询

public static void main(String[] args) {
   //设置一个有100w条数据的集合
    List<String> list = new ArrayList<>();
    for(int i = 0 ; i<1000000 ; i ++){
      list.add(i+"a");
    }
    //测试随机访问的时间
    long startTime = System.currentTimeMillis();
    for(int i = 0 ;i<list.size();i++){
      list.get(i);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("随机访问时间:"+ (endTime-startTime));

    //测试顺序访问的时间
    startTime = System.currentTimeMillis();
    Iterator iterator = list.iterator();
    while (iterator.hasNext()){
      iterator.next();
    }
    endTime = System.currentTimeMillis();
    System.out.println("顺序访问时间:"+ (endTime-startTime));

  }

往集合设置了100w条数据,为了更明显的看见效率差别;
结果
可以看见,随机访问时间比顺序访问时间要快,最为对比的可以再看下我用LinkedList来实现访问时间;

  • LinkedList没有实现RandomAccess接口
//2、设置一个有10w条数据的集合 -- LinkedList
 List<String> list1 = new LinkedList<>();
 for(int i = 0 ; i<100000 ; i ++){
   list1.add(i+"i");
 }
 //测试随机访问的时间
 long startTime1 = System.currentTimeMillis();
 for(int i = 0 ;i<list1.size();i++){
   list1.get(i);
 }
 long endTime1 = System.currentTimeMillis();
 System.out.println("LinkedList随机访问时间:"+ (endTime1-startTime1));

 //测试顺序访问的时间
 startTime1 = System.currentTimeMillis();
 Iterator iterator1 = list1.iterator();
 while (iterator1.hasNext()){
   iterator1.next();
 }
 endTime1 = System.currentTimeMillis();
 System.out.println("LinkedList顺序访问时间:"+ (endTime1-startTime1));

结果
这里就可以很明显的看出来,没有实现RandomAccess接口的LinkedList接口随机访问的速度不是一般的慢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值