Comparable+Comparator+Cloneable接口

Posted 爱敲代码的三毛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Comparable+Comparator+Cloneable接口相关的知识,希望对你有一定的参考价值。

文章目录


Comparable

当我们需要对一个自己写的类进行排序(Collections.sortArrays.sort)的时候,,就要使用到Comparable接口。

该接口中有一个compareTo方法,该方法其实就是一比较规则。

public interface Comparable<T> 
    public int compareTo(T o);

然后比较当前对象和参数对象的大小关系(按分数来算).

  • 如果当前对象应排在参数对象之前, 返回小于 0 的数字;
  • 如果当前对象应排在参数对象之后, 返回大于 0 的数字;
  • 如果当前对象和参数对象不分先后, 返回 0

所以如果自己的类需要进行排序的时候,就需要实现Comparable该接口,并重写compareTo方法,该法就可以通过指定字段进行排序,比如年龄。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Student implements Comparable<Student> 
    int id;
    String name;
    int age;

    public Student(int id, String name, int age) 
        this.id = id;
        this.name = name;
        this.age = age;
    

    @Override
    public String toString() 
        return "Student" +
                "id=" + id +
                ", name='" + name + '\\'' +
                ", age=" + age +
                '';
    
    // 谁调用的compareTo,谁就是this
    @Override
    public int compareTo(Student o) 
        return this.age -o.age;
    

public class TestDemo 
    public static void main(String[] args) 
        List<Student> students = new ArrayList<>();
        students.add(new Student(2,"张三",20));
        students.add(new Student(3,"李四",18));
        students.add(new Student(1,"王五",15));

        System.out.println(students);
        Collections.sort(students);
        System.out.println(students);
    

运行结果

[Studentid=2, name='张三', age=20, Studentid=3, name='李四', age=18, Studentid=1, name='王五', age=15]
[Studentid=1, name='王五', age=15, Studentid=3, name='李四', age=18, Studentid=2, name='张三', age=20]

该接口对类的侵入性比较强,但是如果这个比较规则已经被很多人使用,你突然想换个比较规则,如果修改比较规则必然会造成损失,所以这个方方法对类的侵入性太强了,并不是特别好。

Comparator

刚刚我们说 Comparable 对类的侵入性太强了,但有另外一个接口它十分灵活。就是Comparator

代码示例:

我们写了两个类,分别实现了 Comparator 这个接口,一个是用年龄比较,一个是用姓名比较。我们叫做比较器.

import java.util.Comparator;

public class AgeComparator implements Comparator<Student> 
    @Override
    public int compare(Student o1, Student o2) 
        return o1.age-o2.age;
    

import java.util.Comparator;

public class NameComparator implements Comparator<Student> 
    @Override
    public int compare(Student o1, Student o2) 
        return o1.name.compareTo(o2.name);
    

再来看原来的代码,我们的Student并没有实现任何接口,也没有重写compareTo 方法。
只是实例化了刚刚上面两个比较器,在用sort排序时,传过去了两个参数,一个是要排序的集合,一个是我们写的比较器对象。

class Student
    int id;
    String name;
    int age;

    public Student(int id, String name, int age) 
        this.id = id;
        this.name = name;
        this.age = age;
    

    @Override
    public String toString() 
        return "Student" +
                "id=" + id +
                ", name='" + name + '\\'' +
                ", age=" + age +
                '';
    

public class TestDemo 
    public static void main(String[] args) 
        List<Student> students = new ArrayList<>();
        students.add(new Student(3,"李四",18));
        students.add(new Student(1,"王五",15));
        students.add(new Student(2,"张三",20));

        System.out.println(students);
        //按姓名排序
        NameComparator nameComparator = new NameComparator();
        Collections.sort(students,nameComparator);
        System.out.println(students);
        // 按年龄排序
        AgeComparator ageComparator = new AgeComparator();
        Collections.sort(students,ageComparator);
        System.out.println(students);

    


运行结果

[Studentid=3, name='李四', age=18, Studentid=1, name='王五', age=15, Studentid=2, name='张三', age=20]
[Studentid=2, name='张三', age=20, Studentid=3, name='李四', age=18, Studentid=1, name='王五', age=15]
[Studentid=1, name='王五', age=15, Studentid=3, name='李四', age=18, Studentid=2, name='张三', age=20]

Comparator 接口,如果要进行比较只需要根据自己的需要写一个比较器就好了,并不像第一个接口那样直接写死了。

Cloneable

Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法, 必须要先实现Clonable接口

  • 实现Clonable接口
  • 记住要处理异常
  • 重写 Object 的 clone 方法
class Person implements Cloneable 
    String name;
    int age;

    public Person(String name, int age) 
        this.name = name;
        this.age = age;
    

    @Override
    public String toString() 
        return "Person" +
                "name='" + name + '\\'' +
                ", age=" + age +
                '';
    

    @Override
    protected Object clone() throws CloneNotSupportedException 
        return super.clone();
    

public class CloneableDemo 
    public static void main(String[] args) throws CloneNotSupportedException 
        Person std1 = new Person("张三",18);
        Person std2 = (Person) p1.clone();
        System.out.println(std1);
        System.out.println(std2);

    

运行结果

Personname='张三', age=18
Personname='张三', age=18

此时的内存布局

如果一个类里的成员变量是另外一个类,使用clone对其进行拷贝

浅拷贝

class Test 
    int val;
    public Test(int val) 
        this.val = val;
    

    @Override
    public String toString() 
        return "Test" +
                "val=" + val +
                '';
    

class Person implements Cloneable 
    String name;
    int age;
    Test t;
    public Person(String name, int age) 
        this.name = name;
        this.age = age;
    

    @Override
    public String toString() 
        return "Person" +
                "name='" + name + '\\'' +
                ", age=" + age +
                ", t=" + t +
                '';
    

    @Override
    protected Object clone() throws CloneNotSupportedException 
        return super.clone();
    

public class CloneableDemo 
    public static void main(String[] args) throws CloneNotSupportedException 
        Person std1 = new Person("张三",18);
        std1.t = new Test(100);
        Person std2 = (Person) p1.clone();
        std2.t.val = 200;
        System.out.println(std1);
        System.out.println(std2);

    

运行结果

Personname='张三', age=18, t=Testval=200
Personname='张三', age=18, t=Testval=200

当我们对拷贝后的对象也就是p2里的Test示例进行修改时,p1也发生了改变。此时就是一个典型的浅拷贝

此时的内存布局,这个拷贝就是将一个对象原样克隆了一份,它其时两个对象里的Test示例对象还是指向的同一个 对象

深拷贝

要想实现深拷贝,就得修改clone方法,再让其包含的Test类也要实现Cloneable接口重写clone接口,让Person对象在拷贝的同时把Test示例也给拷贝,这样就实现深拷贝。

class Test implements Cloneable 
    int val;
    public Test(int val) 
        this.val = val;
    

    @Override
    public String toString() 
        return "Test" +
                "val=" + val +
                '';
    

    @Override
    protected Object clone() throws CloneNotSupportedException 
        return super.clone();
    

class Person implements Cloneable 
    String name;
    int age;
    Test t;
    public Person(String name, int age) 
        this.name = name;
        this.age = age;
    

    @Override
    public String toString() 
        return "Person" +
                "name='" + name + '\\'' +
                ", age=" + age +
                ", t=" + t +
                '';
    

    @Override
    protected Object clone() throws CloneNotSupportedException 
        Person person = (Person) super.clone();
        person.t = (Test) this.t.clone();

        return person;
    

public class CloneableDemo 
    public static void main(String[] args) throws CloneNotSupportedException 
        Person p1 = new Person("张三",18);
        p1.t = new Test(100);
        Person p2 = (Person) p1.clone();
        p2.t.val = 200;
        System.out.println(p1);
        System.out.println(p2);

    

运行结果

Personname='张三', age=18, t=Testval=100
Personname='张三', age=18, t=Testval=200

此时的内存布局


以上是关于Comparable+Comparator+Cloneable接口的主要内容,如果未能解决你的问题,请参考以下文章

数组对象排序:Comparable和Comparator

Comparable与Comparator的区别

Comparable与Comparator的区别

Comparable与Comparator的区别

comparable 和comparator的区别

Comparable 与 Comparator的区别