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

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- 序列化
- 1、将一个Java对象变成字节序列,方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失,
- 2、变换成字节序列也更便于网络运输和传播。
- 3、序列化机制从某种意义上来说也弥补了平台化的一些差异,毕竟转换后的字节流可以在其他平台上进行反序列化来恢复对象,因此可以实现跨平台存储。
对象 的数据写入到文件(写对象)对象 的数据读取出来写对象示例 注意必须传一个对象,序列化也指的就是序列化对象 @NoArgsConstructor
@AllArgsConstructor
@Data
public class ApeEntity {
private String id;
private String sbmc;
private String sblx;
private String ipdz;
}
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();
}
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)
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ApeEntity implements Serializable {
private String id;
private String sbmc;
private String sblx;
private String ipdz;
}

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());
}
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'
一个类实现CloneAble 接口来指示Object.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);
}
}
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);
}
示例结果
Student(name=xiaoming1, sex=man, age=0)
Student(name=xiaoming2, sex=man, age=0)
Student(name=xiaoming3, sex=man, age=0)
false
案例分析:
{姓名:小明,性别 男,年龄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());
}
@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();
}
}
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);
false
Student(name=小明, sex=男, age=10)
Student(name=小明, sex=男, age=10)
Student(name=小明, sex=男, age=100)
Student(name=小明, sex=男, age=10)
以上实例简单使用浅克隆,完全可以满足需求,如果说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();
}
}
public class Skill {
private String name;
}
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);
}
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=武术))
当修改学生的值时,影响到了克隆出来学生的值 ,此时浅克隆已经满足不了需求,这时我们需要用到深克隆
public class Skill implements Cloneable{
private String name;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.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;
}
}
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=跆拳道))
相互之间已经做到了互不影响
for(int i = 0 , n = list.size(); i< n ;i++)
list.get(i);
for(Iterator i=list.iterator(); i.hasNext();)
i.next();
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));
}
ArrayList-随机访问列表耗时:1
ArrayList-顺序访问列表耗时:3
因为ArrayList 是实现了RandomAccess 接口的,所以它会比Iterator 快;
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));
}
LinkedList-随机访问列表耗时:4581
LinkedList-顺序访问列表耗时:1
因为LinkedList 是没有RandomAccess 接口的,所以它会比随机访问更快;还有需要值 得注意的时候,在添加数据的时候我们很明显能看出来linkedList 添加数据是比较慢的:
linkedList 添加数据是比较慢原因:当数据量大时,ArrayList每次扩容都能得到很大的新空间,解决了前期频繁扩容的劣势,而LinkedList虽然有尾指针,但是每次add都要将对象new成一个Node(而ArrayList直接数组对应位置元素赋值)
//模拟从数据库查询出数据结果
List<User> list = dao.query(example);
//判断
if(list.instanceof RandomAccess){
//随机访问
for(int i = 0; i < list.size(); i++){
……
}
}else{
//顺序访问
for(User user:List){
……
}
}
ArrayList extends AbstractList
AbstractList extends AbstractCollection
AbstractList是一个抽象类,实现了List接口,List定义了一些List通用方法,而AbstractList抽象类中可以有抽象方法,还可以有具体的实现方法, AbstractList实现接口中一些通用的方法,实现了基础的add/get/indexOf/iterator/subList/RandomAccessSubList方法,ArrayList再继承AbstractList,拿到通用基础的方法,然后自己在实现一些自己特有的方法,这样的好处是:让代码更简洁,继承结构最底层的类中通用的方法,减少重复代码。