参考了部分《Effective Java》第12条的内容和官方文档,刚好也被面试过,记一笔。
Comparable
一个类一旦实现了Comparable接口,它就可以跟许多泛型算法以及依赖于该接口的集合实现进行写作。如果你编写的类需要进行排序,可以使该类实现Comparable接口。
Comparable定义如下:
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
只包含了一个compareTo方法,将对象与指定的对象进行比较,当对象小于、等于或大于指定对象的时候,分别返回一个负整数、零或者正整数。符号sgn代表signum函数,它根据表达式的值为负值、零、正值分别返回-1,0,1。表达式需要满足的关系如下:
The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y. (This implies that x.compareTo(y) must throw an exception iff y.compareTo(x) throws an exception.)
The implementor must also ensure that the relation is transitive: (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.
Finally, the implementor must ensure that x.compareTo(y)==0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z.
It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is "Note: this class has a natural ordering that is inconsistent with equals."
翻译过来即:
- 实现者必须确保所有的x和y都满足sgn(x.compareTo(y)) ==-sgn(y.compareTo(x))。(这也意味着,当且仅当y.compareTo(x)抛出异常时,x.compareTo(y)才必须抛出异常)
实现者还必须确保这个关系是可传递的:(x.compareTo(y)>0 && y.compareTo(z)>0)意味着x.compareTo(z)>0
最后,实现者必须确保x.compareTo(y)==0暗示着所有的z都满足sgn(x.compareTo(z)) == sgn(y.compareTo(z))
强烈建议(x.compareTo(y)==0) == (x.equals(y)),但这不是绝对必要。一般来说,任何实现了Comparable 接口的类,若违反了这个条件,都应该明确予以说明。推荐使用这样的说法:“注意:该类具有内在的排序功能,但是与equals不一致。”
由compareTo是假方法是假的等同性测试,也他一定遵守相同于equals约定所施加的限制条件:自反性、对称性和传递性。
下面举个例子:ComparableTest 实现了Comparable接口。它有三个属性,name,age,grade,现在需要对ComparableTest 对象实例进行排序,排序规则为先按name升序,再按age升序,最后按grade升序排,实现compareTo函数。
import java.util.Arrays;
public class ComparableTest implements Comparable<ComparableTest>{
private String name ;
private int age;
private int grade;
public ComparableTest(String name,int age,int grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
public static void main(String[] args) {
ComparableTest s1 = new ComparableTest("zfq", 14, 70);
ComparableTest s2 = new ComparableTest("abd", 12, 80);
ComparableTest s3 = new ComparableTest("zfq", 13, 80);
ComparableTest[] comp= {s1,s2,s3};
Arrays.sort(comp);
for(int i = 0;i < comp.length;i ++)
System.out.println(comp[i].name + " " + comp[i].age + " " + comp[i].grade);
}
@Override
public int compareTo(ComparableTest o) {
if(name.compareTo(o.name) < 0)
return -1;
if(age < o.age)
return -1;
if(grade < o.grade)
return -1;
return 0;
}
}
结果如下:
abd 12 80
zfq 13 80
zfq 14 70
Comparator
如果我们需要对某个类进行排序,但是该类没有实现Comparable接口,那么我们可以自定义一个比较器,该比较器实现Comparator接口即可。
Comparator定义如下:
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
该接口定义了两个方法compare(T o1, T o2)和equals(Objec)
可以不重写equals方法,相当于Object.equals(Object)。comp1.equals(comp2) 意味着对于每一个对象引用o1,o2,sgn(comp1.compare(o1, o2))==sgn(comp2.compare(o1, o2))
举个例子:
ComparatorTest类没有实现Comparable接口,现要对ComparatorTest类对象进行排序,有两种排序
(1)按name、age、grade升序排
(2)按name、age、grade降序排
定义了两个比较器AscComparator和DescComparator来实现这两种排序
import java.util.Arrays;
import java.util.Comparator;
public class ComparatorTest {
private String name ;
private int age;
private int grade;
public ComparatorTest(String name,int age,int grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
public static void main(String[] args) {
ComparatorTest s1 = new ComparatorTest("zfq", 14, 70);
ComparatorTest s2 = new ComparatorTest("abd", 12, 80);
ComparatorTest s3 = new ComparatorTest("zfq", 13, 80);
ComparatorTest[] comp= {s1,s2,s3};
//按name,age,grade从小到大排
Arrays.sort(comp, new AscComparator());
System.out.println("升序后的结果:");
for(int i = 0;i < comp.length;i ++)
System.out.println(comp[i].name + " " + comp[i].age + " " + comp[i].grade);
//按name,age,grade从大到小排
Arrays.sort(comp, new DescComparator());
System.out.println("降序后的结果:");
for(int i = 0;i < comp.length;i ++)
System.out.println(comp[i].name + " " + comp[i].age + " " + comp[i].grade);
}
//升序比较器
private static class AscComparator implements Comparator<ComparatorTest> {
@Override
public int compare(ComparatorTest o1, ComparatorTest o2) {
if(o1.name.compareTo(o2.name) < 0)
return -1;
if(o1.age < o2.age)
return -1;
if(o1.grade < o2.grade)
return -1;
return 0;
}
}
//降序比较器
private static class DescComparator implements Comparator<ComparatorTest> {
@Override
public int compare(ComparatorTest o1, ComparatorTest o2) {
if(o1.name.compareTo(o2.name) > 0)
return -1;
if(o1.age > o2.age)
return -1;
if(o1.grade > o2.grade)
return -1;
return 0;
}
}
}
结果如下:
升序后的结果:
abd 12 80
zfq 13 80
zfq 14 70
降序后的结果:
zfq 14 70
zfq 13 80
abd 12 80