Java Collection(5)——比较(数据/对象)&深浅拷贝(对象)
1.比较
1.1 基本数据类型的比较
Java中基本数据类型可以通过 == , > ,
还可以使用其对应的包装类调用equals方法来比较
1.2引用类型的比较
我这里创建了一个自定义类student
Object类自带的equals方法实际上和==差不多,用更是会直接报错
既然如此,该如何实现对象之间的比较呢?
以下会展示三种方法
1.3 对象的比较
1.3.1 重写Object中的equals方法
重写之后就可以调用自己的equals方法
1.3.2 实现Comparable接口
实现Comparable接口,然后在该类中重写compareTo方法
因为在StudengA内部重写compareTo方法,所以以后比较StudentA类型的大小,只能比较它的id大小(除非改变StudentA内部的compareTo方法),对类的侵入性较强
1.3.3 提供比较器(Comparator)
创建一个类来实现Comparator接口,并且重写接口里的compare方法
相较于实现Comparable接口,比较器对类的侵入性没那么强,可以按照提供的比较器来实现不同的比较逻辑
1.4 为什么要实现对象的比较?
因为在优先级队列/TreeMap(举的例子)中,如果要往里面添加元素,则添加的元素必须是可比较的,否则会报错,下面以TreeMap为例
1.5 三个比较方式对比
1.Object.equals:因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与否
2.Comparable.compareTo:需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内部顺序
3.Comparator.compare:需要实现一个比较器对象,对待比较类的侵入性弱
2.深浅拷贝(2025年5月11日新增)
在Java SE(5)——数组中介绍了Arrays工具类,调用其中的copyOf()方法可以完成对数组的拷贝
那么,如果想要对一个对象进行拷贝又应该怎么样实现呢?
public class CopyPerson { public String name; public int age; public int score; // public CopyPerson(String name, int age, int score) { this.name = name; this.age = age; this.score = score; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } } public class Copy { public static void main(String[] args){ CopyPerson person1 = new CopyPerson("张三",18,98); CopyPerson person2 = person1; System.out.println(person2); } }
运行结果:
Person{name=‘张三’, age=18, score=98}
在上述代码中,仅看运行结果貌似是完成了一个对象的拷贝,但实际上只是将原对象的地址赋值给新的引用而已,在堆中仍然只存在一份对象
下面就讲解如何将对象完整地在堆中拷贝一份
2.1 浅拷贝
在Java中有专门拷贝对象的方法:Object类中的clone方法(如下)。
protected native Object clone() throws CloneNotSupportedException;
该clone()方法是由native修饰,表示该方法的具体实现是使用C/C++写的,我们无法查看方法的内部代码。因为Object类默认是所有类的父类,所以如果要使用该方法只需要在子类中重写就行了
修改后代码如下:
public class ShallowPerson implements Cloneable { public String name; public int age; public int score; // public ShallowPerson(String name, int age, int score) { this.name = name; this.age = age; this.score = score; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } public class ShallowClone { //浅拷贝 public static void main(String[] args) throws CloneNotSupportedException { ShallowPerson person1 = new ShallowPerson("张三",18,98); ShallowPerson person2 = (ShallowPerson) person1.clone(); System.out.println(person1); System.out.println(person2); //修改person2的age person2.age = 20; System.out.println(person1); System.out.println(person2); } }
运行结果:
Person{name=‘张三’, age=18, score=98}
Person{name=‘张三’, age=18, score=98}
Person{name=‘张三’, age=18, score=98}
Person{name=‘张三’, age=20, score=98}
根据运行结果可知,修改person2中的age值并没有影响person1中的age值,所以可以判定person1和person2指向的是两个对象
注意:被拷贝的类必须实现Cloneable接口,这个接口中没有任何方法,实现该接口的类也不会重写任何方法,但是会被声明为可拷贝的(Cloneable)。换言之,实现Cloneable接口后会被赋予可拷贝的权限,如果不实现该接口直接重写并调用clone()方法会抛出CloneNotSupportedException异常(不支持拷贝)
问题一:上述代码中,子类重写的clone()方法貌似并没有改变内部逻辑,依然是调用父类的clone()方法,那么重写的意义是什么?
对比子类和Object中的clone()方法,唯一的区别就是访问限定修饰符,那么把protected修改为public的作用是什么呢?
被protected修饰的成员,在同一个包中可以被任意的类访问,在不同的包中只能在子类中访问。在不重写clone()方法的情况下,ShallowPerson只能在自己的内部调用clone()方法,而无法在ShallowClone中通过ShallowPerson的引用来访问clone()方法(protected成员的访问权限是基于类层级结构(继承)和包结构的,而不是对象的引用)
问题二:当被拷贝的类中存在其他引用变量时(如自定义类),上述的拷贝还能顺利完成吗?
要想把Money对象也进行拷贝,仅需要进行深拷贝操作
2.2 深拷贝
public class DeepPerson implements Cloneable { public String name; public int age; public int score; public Money money; // public DeepPerson(String name, int age, int score,int money) { this.name = name; this.age = age; this.score = score; this.money = new Money(money); } @Override public String toString() { return "DeepPerson{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + ", money=" + money + '}'; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } //Money类中也实现Cloneable接口并重写clone()方法 public class Money implements Cloneable { public int money; // public Money(int money) { this.money = money; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Money{" + "money=" + money + '}'; } } public class DeepClone { public static void main(String[] args) throws CloneNotSupportedException { DeepPerson deepPerson1 = new DeepPerson("李四",20,100,25000); DeepPerson deepPerson2 = (DeepPerson) deepPerson1.clone(); System.out.println(deepPerson1); System.out.println(deepPerson2); deepPerson2.money = new Money(26000); System.out.println(deepPerson1); System.out.println(deepPerson2); } }
运行结果: