一、描述
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接口随机访问的速度不是一般的慢