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);
}
}
运行结果:












