Item 12 考虑实现Comparable接口
2015-03-29 15:53 ttylinux 阅读(460) 评论(0) 编辑 收藏 举报1.Comparable接口,用来做什么。
2.判定类实现的Comparable接口是否正确的方法。
3.不要扩展一个已经实现了Comparable接口的类来增加用于比较的值组件。
1.Comparable接口,用来做什么?
---Comparable接口----- public interface Comparable<T> { int compareTo(T t); } ---Comparable接口-----
Comparable接口用来做什么?实现了Comparable接口的类,具备什么功能?
“This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method.
Lists (and arrays) of objects that implement this interface can be sorted automatically by
Collections.sort
(and Arrays.sort
). Objects that implement this interface can be used as keys in a sorted map or as elements in a sorted set, without the need to specify a comparator.”
实现了Comparable接口的类,它的实例就具备了排序功能。然后,把实现了Comparable接口的类的实例,添加到List或者array集合中,调用Collections.sort或者Arrays.sort就可以得到一个排好序的集合。
一个类实现Comparable接口时,要遵守如下约定:
1.将当前对象与指定对象进行比较。当该对象小于、等于、或大于指定对象的时候,分别返回一个负整数、零或者正整数。如果由于指定对象的类型而无法与该对象进行比较,则抛出ClassCastException异常。
------这是在实现时,首要考虑的约定。按照该约定来实现Comparable接口。
例子:
选定要比较的是类中的哪个属性,下面比较的是Sal属性。
public class Employee implements Comparable<Employee> { private int EmpID ; private String Ename; private double Sal ; private static int i; public Employee() { EmpID = i++; Ename = "dont know"; Sal = 0.0; } public Employee(String ename, double sal) { EmpID = i++; Ename = ename; Sal = sal; } public String toString() { return "EmpID " + EmpID + "\n" + "Ename " + Ename + "\n" + "Sal " + Sal ; } public int compareTo(Employee arg0) { // TODO Auto-generated method stub if (Sal < arg0. Sal) return -1; else if (Sal == arg0.Sal) return 0; else return 1; } }
然后就可以使用该类了,结合List<T>,Array集合实现对集合中的元素进行排序,如下:
public class ComparableDemo { public static void main(String[] args) { List<Employee> ts1 = new ArrayList<Employee>(); ts1.add(new Employee( "Tom", 40000.00)); ts1.add(new Employee( "Harry", 20000.00)); ts1.add(new Employee( "Maggie", 50000.00)); ts1.add(new Employee( "Chris", 70000.00)); Collections.sort(ts1); Iterator<Employee> itr = ts1.iterator(); System.out.println( "------List--------"); while (itr.hasNext()) { Object element = itr.next(); System. out.println(element + "\n" ); } System.out.println( "-----Array--------"); Employee[] oneArray = { new Employee("Peter" , 30000.00), new Employee( "Harry", 20000.00), new Employee("Maggie" , 50000.00)}; Arrays.sort(oneArray); for (Employee one : oneArray) { System. out.println(one + "\n" ); } } }
输出结果:是一个升序排序,从小到大。
------List--------
EmpID 1
Ename Harry
Sal 20000.0
EmpID 0
Ename Tom
Sal 40000.0
EmpID 2
Ename Maggie
Sal 50000.0
EmpID 3
Ename Chris
Sal 70000.0
-----Array--------
EmpID 5
Ename Harry
Sal 20000.0
EmpID 4
Ename Peter
Sal 30000.0
EmpID 6
Ename Maggie
Sal 50000.0
2.判定类实现的Comparable接口是否正确的方法
根据《effective Java>,在判定类实现的Comparable接口实现是否正确时,使用下列通用约定来判定:
1)实现者必须确保所有的x和y都满足sgn(x.compareTo(y)) == -sgn(y.compareTo(x))。(这也暗示着,当且仅当y.compareTo(x)抛出异常时,x.compareTo(y)才必须抛出异常。
使用上述的例子,可以这样写个单元测试:
Employee one = new Employee( "Tom", 40000.00); Employee two = new Employee("Chris", 70000.00); if (one.compareTo( two) == -two .compareTo(one)) { System. out.println("satisfy rule one." ); }
2)实现者还必须确保这个关系时可传递的:(x.compareTo(y) > 0 && y.compareTo(z)>0),暗示着x.compareTo(z) > 0。---传递性。
使用上述的例子,可以这样写个单元测试:
Employee x = new Employee( "Tom", 40000.00); Employee y = new Employee( "ChrisY", 30000.00); Employee z = new Employee( "Chris", 20000.00); if (x.compareTo(y) > 0 && y.compareTo(z) > 0 && x.compareTo(z) > 0) { System. out.println("satisfy rule No.two" ); }
3)实现者必须确保x.compareTo(y) == 0暗示着所有的z都满足sgn(x.compareTo(z)) == sgn(y.compareTo(z))。
使用上述的例子,可以这样写个单元测试:
Employee x = new Employee( "Tom", 40000.00); Employee y = new Employee( "ChrisY", 40000.00); Employee z = new Employee( "Chris", 20000.00); if (x.compareTo(y) == 0) { if (x.compareTo(z) == y.compareTo(z)) { System. out.println("satisfy rule No.threee" ); } }
总的来说,这三条通用约定,是用来指导你编写单元测试的。如果,你实现的Compareable接口违反了这三条约定,那么,将不能完好的与List,Array等集合协作。
3.不要扩展一个已经实现了Comparable接口的类来增加用于比较的值组件
如果你想为一个实现了Comparable接口的类增加值组件,请不要扩展这个类;而是要编写一个不相关的类,其中包含第一个类的一个实例。然后提供一个“视图(view)"方法返回这个实例。这样既可以让你自由地在第二个类上实现compareTo方法,同时也允许它的客户端在必要的时候,把第二个类的实例视同第一个类的实例。
例子:假设,在比较职员的时候,还考虑到这个职员当前的级别。当两个职员的级别相等时,才比较他们的销售额;否则,则比较职员的级别,职员级别高的,排在后面,按照升序排列。
public class NewEmployee implements Comparable<NewEmployee> { public static final int Low = 10; public static final int Middle = 20; public static final int High = 30; private int EmpID ; private String Ename; private double Sal ; private static int i; private Grade oneGrade; public NewEmployee() { EmpID = i++; Ename = "dont know"; Sal = 0.0; oneGrade = new Grade( Low ); } public NewEmployee(String ename, double sal, Grade grade) { EmpID = i++; Ename = ename; Sal = sal; oneGrade = grade; } public String toString() { return "EmpID " + EmpID + "\n" + "Ename " + Ename + "\n" + "Sal " + Sal + "\n" + "Grade " + getGradeName( oneGrade.getGrade()); } private String getGradeName( int grade) { switch (grade) { case Low : return "Low" ; case Middle : return "Middle" ; case High : return "High" ; default: return "Null" ; } } public int compareTo(NewEmployee arg0) { // TODO Auto-generated method stub if ( oneGrade.compareTo(arg0. oneGrade) == 0) { if ( Sal < arg0. Sal) { return -1; } else if (Sal == arg0. Sal) { return 0; } else { return 1; } } else { return oneGrade.compareTo(arg0. oneGrade); } } } -------- public class NewComparableDemo { public static void main(String[] args) { List<NewEmployee> ts1 = new ArrayList<NewEmployee>(); ts1.add( new NewEmployee( "Tom" , 40000.00, new Grade(NewEmployee. Middle))); ts1.add( new NewEmployee( "Harry" , 20000.00, new Grade(NewEmployee. Low))); ts1.add( new NewEmployee( "Maggie" , 50000.00, new Grade(NewEmployee. High))); ts1.add( new NewEmployee( "Chris" , 70000.00, new Grade(NewEmployee. Low))); Collections. sort(ts1); Iterator<NewEmployee> itr = ts1.iterator(); while (itr.hasNext()) { Object element = itr.next(); System. out .println(element + "\n" ); } NewEmployee x = new NewEmployee( "Tom" , 40000.00, new Grade(NewEmployee.Middle )); NewEmployee y = new NewEmployee( "Harry" , 20000.00, new Grade(NewEmployee.Low )); if (x.compareTo(y) == -y.compareTo(x)) { System. out .println("satisfy rule No.one" ); } NewEmployee x1 = new NewEmployee( "Tom" , 40000.00, new Grade(NewEmployee.Middle )); NewEmployee y1 = new NewEmployee( "Harry1" , 70000.00, new Grade(NewEmployee.Low )); NewEmployee z1 = new NewEmployee( "Harry2" , 30000.00, new Grade(NewEmployee.Low )); if (x1.compareTo(y1) > 0 && y1.compareTo(z1) > 0 && x1.compareTo(z1) > 0) { System. out .println("satisfy rule No.two" ); } NewEmployee x2 = new NewEmployee( "Tom2" , 70000.00, new Grade(NewEmployee.Middle )); NewEmployee y2 = new NewEmployee( "Harry2" , 70000.00, new Grade(NewEmployee.Middle )); NewEmployee z2 = new NewEmployee( "Harry2" , 30000.00, new Grade(NewEmployee.Low )); if (x2.compareTo(y2) == 0) { if (x2.compareTo(z2) == y2.compareTo(z2)) { System. out .println("satisfy rule No.three" ); } } } }
输出结果:
EmpID 1
Ename Harry
Sal 20000.0
Grade Low
EmpID 3
Ename Chris
Sal 70000.0
Grade Low
EmpID 0
Ename Tom
Sal 40000.0
Grade Middle
EmpID 2
Ename Maggie
Sal 50000.0
Grade High
satisfy rule No.one
satisfy rule No.two
satisfy rule No.three
版权声明:
作者:ttylinux
本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。