如何在Java中使用Comparator进行排序
我学会了如何使用可比较的,但是我对比较器有困难。 我在我的代码中有一个错误:
Exception in thread "main" java.lang.ClassCastException: New.People cannot be cast to java.lang.Comparable at java.util.Arrays.mergeSort(Unknown Source) at java.util.Arrays.sort(Unknown Source) at java.util.Collections.sort(Unknown Source) at New.TestPeople.main(TestPeople.java:18)
这是我的代码:
import java.util.Comparator; public class People implements Comparator { private int id; private String info; private double price; public People(int newid, String newinfo, double newprice) { setid(newid); setinfo(newinfo); setprice(newprice); } public int getid() { return id; } public void setid(int id) { this.id = id; } public String getinfo() { return info; } public void setinfo(String info) { this.info = info; } public double getprice() { return price; } public void setprice(double price) { this.price = price; } public int compare(Object obj1, Object obj2) { Integer p1 = ((People) obj1).getid(); Integer p2 = ((People) obj2).getid(); if (p1 > p2) { return 1; } else if (p1 < p2){ return -1; } else { return 0; } } }
import java.util.ArrayList; import java.util.Collections; public class TestPeople { public static void main(String[] args) { ArrayList peps = new ArrayList(); peps.add(new People(123, "M", 14.25)); peps.add(new People(234, "M", 6.21)); peps.add(new People(362, "F", 9.23)); peps.add(new People(111, "M", 65.99)); peps.add(new People(535, "F", 9.23)); Collections.sort(peps); for (int i = 0; i < peps.size(); i++){ System.out.println(peps.get(i)); } } }
我相信在比较的方法中,我们必须做一些事情,但是我正在玩弄它,仍然找不到解决方案
你的示例类有几个尴尬的东西:
- 它被称为人,而它有一个
price
和info
(更多东西的对象,而不是人); - 当把一个类命名为复数的东西时,它表明它是一个以上的抽象。
无论如何,这里是一个演示如何使用Comparator<T>
:
public class ComparatorDemo { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Joe", 24), new Person("Pete", 18), new Person("Chris", 21) ); Collections.sort(people, new LexicographicComparator()); System.out.println(people); Collections.sort(people, new AgeComparator()); System.out.println(people); } } class LexicographicComparator implements Comparator<Person> { @Override public int compare(Person a, Person b) { return a.name.compareToIgnoreCase(b.name); } } class AgeComparator implements Comparator<Person> { @Override public int compare(Person a, Person b) { return a.age < b.age ? -1 : a.age == b.age ? 0 : 1; } } class Person { String name; int age; Person(String n, int a) { name = n; age = a; } @Override public String toString() { return String.format("{name=%s, age=%d}", name, age); } }
编辑
而一个等效的Java 8演示将如下所示:
public class ComparatorDemo { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Joe", 24), new Person("Pete", 18), new Person("Chris", 21) ); Collections.sort(people, (a, b) -> a.name.compareToIgnoreCase(b.name)); System.out.println(people); Collections.sort(people, (a, b) -> a.age < b.age ? -1 : a.age == b.age ? 0 : 1); System.out.println(people); } }
这是一个超级简短的模板,可以马上进行排序:
Collections.sort(people,new Comparator<Person>(){ @Override public int compare(final Person lhs,Person rhs) { //TODO return 1 if rhs should be before lhs // return -1 if lhs should be before rhs // return 0 otherwise } });
如果很难记住,试着记住它是相似的(就数字的符号而言):
lhs-rhs
如果您想按升序排序:从最小的数字到最大的数字。
使用People implements Comparable<People>
; 这定义了人的自然排序。
另外还可以定义一个Comparator<People>
,但People implements Comparator<People>
不是正确的做事方式。
Collections.sort
的两个重载是不同的:
-
<T extends Comparable<? super T>> void sort(List<T> list)
- 使用自然排序对
Comparable
对象进行排序
- 使用自然排序对
-
<T> void sort(List<T> list, Comparator<? super T> c)
- 使用兼容的
Comparator
任何东西进行排序
- 使用兼容的
你通过尝试对Comparator
进行排序来混淆两者(这也是为什么Person implements Comparator<Person>
)。 再一次,要使用Collections.sort
,你需要其中的一个是正确的:
- 类型必须是
Comparable
(使用1-argsort
) - 必须提供类型的
Comparator
器(使用2-argssort
)
相关问题
- 何时使用Comparable vs Comparator
- 排序联系人的ArrayList
另外, 不要在新代码中使用原始类型 。 原始类型是不安全的,只是为了兼容性而提供的。
那就是,而不是这个:
ArrayList peps = new ArrayList(); // BAD!!! No generic safety!
你应该使用这样的类型安全的通用声明:
List<People> peps = new ArrayList<People>(); // GOOD!!!
你会发现你的代码甚至没有编译! 这将是一件好事,因为代码有问题( Person
没有implements Comparable<Person>
), 但是因为使用了raw类型,所以编译器没有检查这个 ,而是在运行时得到一个ClassCastException
-时间!!!
这应该说服你在新的代码中总是使用类型安全的泛型类型。 总是。
也可以看看
- 什么是原始类型,为什么我们不应该使用它?
为了完整起见,下面是一个简单的单行compare
方法:
Collections.sort(people,new Comparator<Person>() { @Override public int compare(Person lhs,Person rhs) { return Integer.signum(lhs.getId()-rhs.getId()); } }
你想实现Comparable,而不是Comparator。 你需要实现compareTo方法。 你虽然接近。 比较器是一个“第三方”比较例程。 可比的是,这个对象可以与另一个对比。
public int compareTo(Object obj1) { People that = (People)obj1; Integer p1 = this.getId(); Integer p2 = that.getid(); if (p1 > p2 ){ return 1; } else if (p1 < p2){ return -1; } else return 0; }
请注意,你可能想检查在这里为getId的空值,以防万一。
Java 8增加了一种新的比较器方式,可以减少需要编写的代码数量, Comparator.comparing 。 还要检查Comparator.reversed
这是一个示例
import org.junit.Test; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import static org.junit.Assert.assertTrue; public class ComparatorTest { @Test public void test() { List<Person> peopleList = new ArrayList<>(); peopleList.add(new Person("A", 1000)); peopleList.add(new Person("B", 1)); peopleList.add(new Person("C", 50)); peopleList.add(new Person("Z", 500)); //sort by name, ascending peopleList.sort(Comparator.comparing(Person::getName)); assertTrue(peopleList.get(0).getName().equals("A")); assertTrue(peopleList.get(peopleList.size() - 1).getName().equals("Z")); //sort by name, descending peopleList.sort(Comparator.comparing(Person::getName).reversed()); assertTrue(peopleList.get(0).getName().equals("Z")); assertTrue(peopleList.get(peopleList.size() - 1).getName().equals("A")); //sort by age, ascending peopleList.sort(Comparator.comparing(Person::getAge)); assertTrue(peopleList.get(0).getAge() == 1); assertTrue(peopleList.get(peopleList.size() - 1).getAge() == 1000); //sort by age, descending peopleList.sort(Comparator.comparing(Person::getAge).reversed()); assertTrue(peopleList.get(0).getAge() == 1000); assertTrue(peopleList.get(peopleList.size() - 1).getAge() == 1); } class Person { String name; int age; Person(String n, int a) { name = n; age = a; } public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } } }
下面是比较器的一个例子,它可以用于任何返回一个Comparable的零参数方法。 像这样的事情存在于JDK或图书馆?
import java.lang.reflect.Method; import java.util.Comparator; public class NamedMethodComparator implements Comparator<Object> { // // instance variables // private String methodName; private boolean isAsc; // // constructor // public NamedMethodComparator(String methodName, boolean isAsc) { this.methodName = methodName; this.isAsc = isAsc; } /** * Method to compare two objects using the method named in the constructor. */ @Override public int compare(Object obj1, Object obj2) { Comparable comp1 = getValue(obj1, methodName); Comparable comp2 = getValue(obj2, methodName); if (isAsc) { return comp1.compareTo(comp2); } else { return comp2.compareTo(comp1); } } // // implementation // private Comparable getValue(Object obj, String methodName) { Method method = getMethod(obj, methodName); Comparable comp = getValue(obj, method); return comp; } private Method getMethod(Object obj, String methodName) { try { Class[] signature = {}; Method method = obj.getClass().getMethod(methodName, signature); return method; } catch (Exception exp) { throw new RuntimeException(exp); } } private Comparable getValue(Object obj, Method method) { Object[] args = {}; try { Object rtn = method.invoke(obj, args); Comparable comp = (Comparable) rtn; return comp; } catch (Exception exp) { throw new RuntimeException(exp); } } }
你应该使用重载排序(peps,new People())方法
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class Test { public static void main(String[] args) { List<People> peps = new ArrayList<>(); peps.add(new People(123, "M", 14.25)); peps.add(new People(234, "M", 6.21)); peps.add(new People(362, "F", 9.23)); peps.add(new People(111, "M", 65.99)); peps.add(new People(535, "F", 9.23)); Collections.sort(peps, new People().new ComparatorId()); for (int i = 0; i < peps.size(); i++) { System.out.println(peps.get(i)); } } } class People { private int id; private String info; private double price; public People() { } public People(int newid, String newinfo, double newprice) { setid(newid); setinfo(newinfo); setprice(newprice); } public int getid() { return id; } public void setid(int id) { this.id = id; } public String getinfo() { return info; } public void setinfo(String info) { this.info = info; } public double getprice() { return price; } public void setprice(double price) { this.price = price; } class ComparatorId implements Comparator<People> { @Override public int compare(People obj1, People obj2) { Integer p1 = obj1.getid(); Integer p2 = obj2.getid(); if (p1 > p2) { return 1; } else if (p1 < p2){ return -1; } else { return 0; } } } }
该解决方案可以通过以下方式进行优化:首先,使用私有内部类作为字段的范围是封闭类TestPeople,以便类People的实现不会暴露给外部世界。 这可以从创建一个API的角度来理解,期望一个排序的人员列表其次,使用减少代码的Lamba表达式(java 8),因此开发工作
因此代码如下:
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; public class TestPeople { public static void main(String[] args) { ArrayList<People> peps = new ArrayList<>();// Be specific, to avoid // classCast Exception TestPeople test = new TestPeople(); peps.add(test.new People(123, "M", 14.25)); peps.add(test.new People(234, "M", 6.21)); peps.add(test.new People(362, "F", 9.23)); peps.add(test.new People(111, "M", 65.99)); peps.add(test.new People(535, "F", 9.23)); /* * Collections.sort(peps); * * for (int i = 0; i < peps.size(); i++){ * System.out.println(peps.get(i)); } */ // The above code can be replaced by followin: peps.sort((People p1, People p2) -> p1.getid() - p2.getid()); peps.forEach((p) -> System.out.println(" " + p.toString())); } private class People { private int id; @Override public String toString() { return "People [id=" + id + ", info=" + info + ", price=" + price + "]"; } private String info; private double price; public People(int newid, String newinfo, double newprice) { setid(newid); setinfo(newinfo); setprice(newprice); } public int getid() { return id; } public void setid(int id) { this.id = id; } public String getinfo() { return info; } public void setinfo(String info) { this.info = info; } public double getprice() { return price; } public void setprice(double price) { this.price = price; } } }
public static Comparator<JobSet> JobEndTimeComparator = new Comparator<JobSet>() { public int compare(JobSet j1, JobSet j2) { int cost1 = j1.cost; int cost2 = j2.cost; return cost1-cost2; } };