1. equals( ) 方法
举个例子,我们现在创建一个类 Card 表示一张扑克牌的信息,在主函数中,我们创建三个对象,也就是三张扑克牌,那么我们现在尝试着比较一下这两张牌。我们认为:只有当两张的牌的数值和花色都相同的时候,才觉得这两张牌是相同的。
我们利用 IDEA 编译器自动生成一个重写的equals( ) 方法
总结一下 equals( ) 方法:
① 如果指向同一个对象,返回 true
② 如果传入的为 null,返回 false
③ 如果比较的两个对象,一个是 Card 类,一个是 People 类,返回 false,这很好理解,你不能拿一张牌和一个人去比较!
④ 按照类的实现目标完成比较,例如这里只有扑克牌的花色和数值一样,我们才认为是相同的牌,逻辑问题可以自己实现。
⑤ equals( ) 方法主要可以用来比较自定义的引用类型,但equals( ) 方法缺陷是:其只能按照相等进行比较,不能按照大于、小于的方式进行比较。也就是说,使用equals( ) 方法后的输出结果,只有 true 和 false 之分。
class Card { public int rank; // 数值 public String suit; // 花色 public Card(int rank, String suit) { this.rank = rank; this.suit = suit; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Card card = (Card) o; return rank == card.rank && Objects.equals(suit, card.suit); //只有当两个对象的 数值和花色 相同的时候,才会返回 true } @Override public int hashCode() { return Objects.hash(rank, suit); } } public class Test { public static void main(String[] args) { Card card1 = new Card(1,"♥"); Card card2 = new Card(2,"♠"); Card card3 = new Card(1,"♥"); System.out.println(card1.equals(card2)); System.out.println(card1.equals(card3)); } }
输出结果:
2. Comparable 接口
[ Comparable 接口、Comparator 接口 ] 这两个接口和我之前写的C语言博客qsort函数十分相似,思想是一样的,感兴趣的读者可以去翻一翻我的博客,这是链接:
引入:假设我们现在手中有一张学生名单,上面有他们的各种信息,那么我们想什么办法给这些学生进行排序呢?我们可以按名字首字母排序,也可以按年龄排序,当然在初中高中,每次月考完,都是按成绩排序。所以在程序清单1中,我们创建一个类 Student,表示一个学生,类中有其名字、年龄、成绩。此时我们生成一个带三个参数的构造方法。我们先让类实现接口 Comparable,在类 Student 中,我们重写方法 compareTo.
在主函数中,我们创建两个对象,然后通过对象变量 student1 调用这个 compareTo方法,进行比较 student2,我们先来将学生按名字进行排序吧!那么在 compareTo 中,this.age 就等价于 student1.age,o.age 就等价于 student2.age,也就是说:我们拿学生1和学生2的年龄进行排序,而实现这一功能,就需要使用 Comparable 接口。如果学生1的年龄大于学生2,那么就返回正数,反之,返回负数。
在程序清单1中,我们发现,类 Student 实现了 Comparable 接口,那么在类中必须重写 Comparable 接口的方法 compareTo( ). 当然,Comparable 接口就是为了比较而设计的,而其对应的 compareTo( ) 方法也是如此。
程序清单1:
class Student implements Comparable<Student>{ public String name; public int age; public double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } @Override public int compareTo(Student o) { return this.age - o.age; } } public class Test { public static void main(String[] args) { Student student1 = new Student("A",21,90); Student student2 = new Student("C",28,75); System.out.println(student1.compareTo(student2)); } }
输出结果:
在程序清单2中,我们选择让学生以年龄升序:
使用Arrays.sort() 方法的时候,编译器不知道我们是基于什么方式进行排序的, 因为 students 数组中,有三个不同的类型。 那么我们就实现比较接口,告诉编译器,以学生名字进行怕排序。
程序清单2:
class Student implements Comparable<Student>{ public String name; public int age; public double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } @Override public int compareTo(Student o) { return this.age - o.age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } } public class Test { public static void main(String[] args) { Student[] students = new Student[3]; students[0] = new Student("A",21,90); students[1] = new Student("C",28,75); students[2] = new Student("B",24,70); System.out.println(Arrays.toString(students)); Arrays.sort(students); System.out.println(Arrays.toString(students)); } }
输出结果:
如果我们让学生以名字降序,那么我们就改变 compareTo 方法中的代码,代码如下:【 o.age - this.age 】
public int compareTo(Student o) { return o.age - this.age; }
同样地,如果我们让学生以成绩升序,那么代码如下:
public int compareTo(Student o) { return (int)(this.score - o.score); }
如果我们让学生以名字升序,那么代码如下:
public int compareTo(Student o) { return this.name.compareTo(o.name); }
读者可以自己去试一下这些操作,了解不同类型的比较方式。
3. Comparator 接口
我们来看一下 Comparator 接口来排序两个学生年龄的大小,与 Comparable 接口的思想相同,接下来我主要说明一下 Comparator 接口的语法。
在程序清单3中,我们需要额外创建一个类 AgeComparator 来实现 Comparator 接口,并在这个类中,我们创建一个重写方法 compare( ),那么在主函数中,我们传参只需要传两个对象变量 student1 和 student2 就可以了,那么回过头来看方法 compare( ),student1.age 就等价于 o1.age;student2.age 就等价于 o2.age.
与上面的 Comparable 接口思想相同,不管哪个类实现了接口,那么一定要重写对应接口中的抽象方法。接下来,让我们看一下 Comparator 接口是怎么进行实现的吧。
程序清单3:
class Student { public String name; public int age; public double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } } class AgeComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.age - o2.age; } } public class Test { public static void main(String[] args) { Student student1 = new Student("Z",11,85); Student student2 = new Student("Y",18,73); AgeComparator ageComparator = new AgeComparator(); System.out.println(ageComparator.compare(student1, student2)); } }
输出结果:
在程序清单4中,我将一个学生的三个参数分别进行了排列。
程序清单4:
class Student { public String name; public int age; public double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } } //年龄排序 class AgeComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.age - o2.age; } } //成绩排序 class ScoreComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return (int)(o1.score - o2.score); } } //名字首字母排序 class NameComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.name.compareTo(o2.name); } } public class Test { public static void main(String[] args) { Student[] students = new Student[3]; students[0] = new Student("Z",11,85); students[1] = new Student("Y",18,73); students[2] = new Student("X",14,80); System.out.println(Arrays.toString(students)); System.out.println(); AgeComparator ageComparator = new AgeComparator(); Arrays.sort(students, ageComparator); System.out.println(Arrays.toString(students)); System.out.println(); ScoreComparator scoreComparator = new ScoreComparator(); Arrays.sort(students,scoreComparator); System.out.println(Arrays.toString(students)); System.out.println(); NameComparator nameComparator= new NameComparator(); Arrays.sort(students,nameComparator); System.out.println(Arrays.toString(students)); System.out.println(); } }
输出结果:
可以发现 Comparator接口更加“定制化”,需要什么类型的排序,我就额外创建一个类来实现此接口,之后再通过主函数(或其他方法)调用即可。