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

기억하기 어려운 경우 다음과 유사하다는 것을 기억하십시오 (숫자 부호의 관점에서).

 lhs-rhs 

가장 작은 숫자에서 큰 숫자까지 오름차순으로 정렬하려는 경우입니다.


답변

People implements Comparable<People>대신 사용하십시오 . 이것은의 자연 순서를 정의합니다 People.

A 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-ARG sort)
  • Comparator유형에 대한 A 를 제공해야합니다 (2-args 사용 sort).

관련 질문


또한 새 코드에서 원시 유형을 사용하지 마십시오 . 원시 유형은 안전하지 않으며 호환성을 위해서만 제공됩니다.

즉, 이것 대신에 :

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

다음과 같이 typesafe 일반 선언을 사용해야합니다.

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

그러면 코드가 컴파일되지 않는다는 것을 알게 될 것입니다 !! 코드에 문제가 있기 때문에 ( Person하지 않습니다 implements Comparable<Person>), 그러나 원시 유형을 사용했기 때문에 컴파일러는 이것을 확인하지 않고 대신 ClassCastException런타임에 얻 습니다!

이것은 항상 새로운 코드에서 타입 안전 제네릭 형식을 사용하도록 설득해야합니다. 항상.

또한보십시오


답변

완전성을 위해 간단한 단일 라이너 compare방법이 있습니다.

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


답변

자바 8은 쓰기에있는 코드의 양을 감소 비교기 만드는 새로운 방법이 추가 Comparator.comparing을 . 또한 Comparator를 확인하십시오.

여기 샘플이 있습니다

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



}


답변

Comparator가 아닌 Comparable을 구현하려고합니다. 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 ..에 대한 null을 확인하는 것이 좋습니다.


답변

다음은 Comparable을 반환하는 제로 arg 메서드에서 작동하는 Comparator의 예입니다. 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);
        }
    }

}