Java中如何使用Comparator进行排序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2839137/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 13:28:22  来源:igfitidea点击:

How to use Comparator in Java to sort

javasortingcomparator

提问by Dan

I learned how to use the comparable but I'm having difficulty with the Comparator. I am having a error in my code:

我学会了如何使用可比较,但我在使用比较器时遇到了困难。我的代码中有错误:

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)

Here is my code:

这是我的代码:

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));
        }
    }
}

I believe it has to do something with the casting in the compare method but I was playing around with it and still could not find the solution

我相信它必须对比较方法中的转换做一些事情,但我一直在玩它,但仍然找不到解决方案

回答by polygenelubricants

Use People implements Comparable<People>instead; this defines the natural ordering for People.

使用People implements Comparable<People>代替; 这定义了 的自然排序People

A Comparator<People>can also be defined in addition, but People implements Comparator<People>is not the right way of doing things.

AComparator<People>也可以另外定义,但People implements Comparator<People>不是正确的做事方式。

The two overloads for Collections.sortare different:

的两个重载Collections.sort是不同的:

  • <T extends Comparable<? super T>> void sort(List<T> list)
    • Sorts Comparableobjects using their natural ordering
  • <T> void sort(List<T> list, Comparator<? super T> c)
    • Sorts whatever using a compatible Comparator
  • <T extends Comparable<? super T>> void sort(List<T> list)
    • 排序Comparable利用其自然排序的对象
  • <T> void sort(List<T> list, Comparator<? super T> c)
    • 使用兼容的排序 Comparator

You're confusing the two by trying to sort a Comparator(which is again why it doesn't make sense that Person implements Comparator<Person>). Again, to use Collections.sort, you need one of these to be true:

您通过尝试对 a 进行排序而混淆了两者Comparator(这也是为什么它没有意义Person implements Comparator<Person>)。同样,要使用Collections.sort,您需要满足以下条件之一:

  • The type must be Comparable(use the 1-arg sort)
  • A Comparatorfor the type must be provided (use the 2-args sort)
  • 类型必须是Comparable(使用 1-arg sort
  • Comparator必须提供类型的A (使用 2-args sort

Related questions

相关问题



Also, do not use raw types in new code. Raw types are unsafe, and it's provided only for compatibility.

另外,不要在新代码中使用原始类型。原始类型是不安全的,提供它只是为了兼容性。

That is, instead of this:

也就是说,而不是这样:

ArrayList peps = new ArrayList(); // BAD!!! No generic safety!

you should've used the typesafe generic declaration like this:

你应该像这样使用类型安全的泛型声明:

List<People> peps = new ArrayList<People>(); // GOOD!!!

You will then find that your code doesn't even compile!!That would be a good thing, because there IS something wrong with the code (Persondoes not implements Comparable<Person>), but because you used raw type, the compiler didn't check for this, and instead you get a ClassCastExceptionat run-time!!!

然后你会发现你的代码甚至无法编译!!这将是一件好事,因为代码有问题(Person没有implements Comparable<Person>),但是因为您使用了原始类型,编译器没有检查 this,而是ClassCastException在运行时得到了一个!

This should convince you to always use typesafe generic types in new code. Always.

这应该说服您在新代码中始终使用类型安全的泛型类型。总是。

See also

也可以看看

回答by Will Hartung

You want to implement Comparable, not Comparator. You need to implement the compareTo method. You're close though. Comparator is a "3rd party" comparison routine. Comparable is that this object can be compared with another.

您想要实现 Comparable,而不是 Comparator。您需要实现 compareTo 方法。虽然你很接近。比较器是“第 3 方”比较例程。可比较的是,这个对象可以与另一个对象进行比较。

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;
 }

Note, you may want to check for nulls in here for getId..just in case.

请注意,您可能需要在此处检查 getId 是否为空值……以防万一。

回答by Bart Kiers

There are a couple of awkward things with your example class:

您的示例类有一些尴尬的事情:

  • it's called People while it has a priceand info(more something for objects, not people);
  • when naming a class as a plural of something, it suggests it is an abstraction of more than one thing.
  • 它被称为 People,而它有一个priceand info(更多的是对象,而不是人);
  • 当将一个类命名为某事物的复数形式时,它表明它是对多个事物的抽象。

Anyway, here's a demo of how to use a Comparator<T>:

无论如何,这是如何使用 a 的演示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);
    }
}

EDIT

编辑

And an equivalent Java 8 demo would look like this:

一个等效的 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);
    }
}

回答by android developer

Here's a super short template to do the sorting right away :

这是一个可以立即进行排序的超短模板:

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 (meaning the order stays the same)
     }
 });

if it's hard to remember, try to just remember that it's similar (in terms of the sign of the number) to:

如果很难记住,请尝试记住它类似于(就数字的符号而言):

 lhs-rhs 

That's in case you want to sort in ascending order : from smallest number to largest number.

如果您想按升序排序:从最小数字到最大数字。

回答by NumberFour

For the sake of completeness, here's a simple one-liner comparemethod:

为了完整起见,这里有一个简单的单行compare方法:

Collections.sort(people, new Comparator<Person>() {
    @Override
    public int compare(Person lhs, Person rhs) {  
        return Integer.signum(lhs.getId() - rhs.getId());  
    }
});

回答by michal

You should use the overloaded sort(peps, new People()) method

您应该使用重载的 sort(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;
               }
            }
       }
    }

回答by QuadBiker

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;
            }
        };

回答by Akhil Gupta

The solution can be optimized in following way: Firstly, use a private inner class as the scope for the fields is to be the enclosing class TestPeople so as the implementation of class People won't get exposed to outer world. This can be understood in terms of creating an APIthat expects a sorted list of people Secondly, using the Lamba expression(java 8) which reduces the code, hence development effort

可以通过以下方式优化解决方案:首先,使用私有内部类作为字段的范围是封闭类TestPeople,这样类People的实现就不会暴露于外部世界。这可以通过创建一个期望排序的人员列表的 API 来理​​解其次,使用 Lamba 表达式(java 8)减少了代码,从而减少了开发工作

Hence code would be as below:

因此代码如下:

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;
        }
    }
}

回答by rince

Java 8 added a new way of making Comparators that reduces the amount of code you have to write, Comparator.comparing. Also check out Comparator.reversed

Java 8 添加了一种制作 Comparator 的新方法,可以减少您必须编写的代码量Comparator.comparing。另请查看Comparator.reversed

Here's a sample

这是一个示例

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;
        }
    }



}

回答by John

Here's an example of a Comparator that will work for any zero arg method that returns a Comparable. Does something like this exist in a jdk or library?

这是一个 Comparator 示例,它适用于任何返回 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);
        }
    }

}