여러 필드로 개체를 비교하는 방법


237

다음과 같이 비교할 수있는 여러 필드가있는 개체가 있다고 가정합니다.

public class Person {

    private String firstName;
    private String lastName;
    private String age;

    /* Constructors */

    /* Methods */

}

따라서이 예에서는 다음과 같은 질문을합니다.

a.compareTo(b) > 0

a의 성이 b보다 앞에 오거나 a가 b보다 오래된 지 등을 묻는 것일 수 있습니다.

불필요한 클러 터나 오버 헤드를 추가하지 않고 이러한 종류의 객체를 여러 번 비교할 수있는 가장 깨끗한 방법은 무엇입니까?

  • java.lang.Comparable 하나의 필드로만 비교할 수있는 인터페이스
  • (즉, 다수의 비교 방법을 추가 compareByFirstName(), compareByAge()등) 제 생각에 어수선하게됩니다.

그렇다면 가장 좋은 방법은 무엇입니까?


3
왜 이것이 CW입니까? 완벽하게 유효한 프로그래밍 질문입니다.
Elie

2
Comparable을 사용하면 원하는만큼 많은 필드를 비교할 수 있다는 것을 알고 있습니까?
DJClayworth

답변:


81

ComparatorPerson객체 를 비교하는를 구현할 수 있으며 원하는 만큼 많은 필드를 검사 할 수 있습니다. 여러 비교기를 작성하는 것이 더 간단하지만 비교할 필드를 알려주는 변수를 비교기에 넣을 수 있습니다.


5
실제로 단일 비교기를 사용하는 아이디어를 선호합니다. 나는이 답변이 잘못되었다고 생각하지 않지만 그것을 읽는 사람은 아래의 Steve Kuo 답변을 반드시 확인해야합니다.
Felipe Leão

여러 비교기는 데이터 자체의 기능이 아닌 다른 비교 방법을 원할 때만 사용되었습니다. 즉, 이름, 나이, 시간 등으로 비교하려는 경우가 있습니다. 여러 필드를 동시에 비교하려면 하나의 비교기 만 필요할 것입니다.
Elie

399

자바 8 :

Comparator.comparing((Person p)->p.firstName)
          .thenComparing(p->p.lastName)
          .thenComparingInt(p->p.age);

접근 자 메서드가있는 경우 :

Comparator.comparing(Person::getFirstName)
          .thenComparing(Person::getLastName)
          .thenComparingInt(Person::getAge);

클래스가 Comparable을 구현하면 compareTo 메소드에서 이러한 비교기를 사용할 수 있습니다.

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}

5
특히 캐스트 (Person p)는 체인 비교기에서 중요합니다.
membersound 2009 년

5
정렬 할 때와 같이 많은 수의 객체를 비교할 때 얼마나 효율적입니까? Comparator모든 통화에서 새 인스턴스 를 만들어야합니까 ?
jjurm

4
비교하는 필드 중 하나가 문자열과 같이 null 일 때 NullPointerException이 발생합니다. 어쨌든이 비교 형식을 유지하지만 null 안전을 허용해야합니까?
rveach

3
@jjurm- .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))첫 번째 필드 선택기, 그리고 비교기
gavenkoa

2
위와 compareTo같이 사용할 때 @jjurm Comparator은 메소드가 호출 될 때마다 작성됩니다. 비교기를 개인 정적 최종 필드에 저장하여이를 방지 할 수 있습니다.
간달프

165

구현해야합니다 Comparable <Person>. 모든 필드가 널 (null)이 아니라고 (간단하게하기 위해) 나이가 정수이고, 순위가 첫 번째, 마지막, 나이 인 경우, compareTo방법은 매우 간단합니다.

public int compareTo(Person other) {
    int i = firstName.compareTo(other.firstName);
    if (i != 0) return i;

    i = lastName.compareTo(other.lastName);
    if (i != 0) return i;

    return Integer.compare(age, other.age);
}

10
Comparable <Person>을 구현하면이 메서드는 compareTo (Person p)입니다.이 답변은 Comparator의 compare <T o1, T o2> 메서드와 혼합 된 것 같습니다
Mike

5
권장하지 않습니다. 필드가 여러 개인 경우 비교기를 사용하십시오.
indika

1
현재로서는 최상의 솔루션입니다 (비교 업체보다 낫습니다)
Vasile Surdu

4
@indika, 궁금합니다. 왜 권장하지 않습니까? 하나 이상의 속성을 사용하는 것은 나에게 완벽하게 보입니다.
ars-longa-vita-brevis

4
@ ars-longa-vita-brevis, Comparable을 사용하는 경우 정렬 논리는 객체가 정렬되는 동일한 클래스에 있어야하므로 객체의 자연스러운 순서라고합니다. Comparator를 사용하면 Person 클래스 외부에 사용자 정의 정렬 논리를 작성할 수 있습니다. Person 객체를 이름이나 성으로 만 비교하려면이 논리를 사용할 수 없습니다. 다시 써야합니다.
indika

78

( 여러 필드를 기반으로 Java에서 객체 목록을 정렬 하는 방법부터 )

작업 코드 이 요지의

Java 8 람다 사용 (2019 년 4 월 10 일 추가)

Java 8은 람다에 의해 이것을 잘 해결합니다 (구아바와 Apache Commons는 여전히 더 많은 유연성을 제공 할 수 있습니다)

Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
            .thenComparing(Report::getStudentNumber)
            .thenComparing(Report::getSchool));

아래 @gaoagong의 답변 덕분에 .

지저분하고 복잡한 : 손으로 정렬

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        int sizeCmp = p1.size.compareTo(p2.size);  
        if (sizeCmp != 0) {  
            return sizeCmp;  
        }  
        int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);  
        if (nrOfToppingsCmp != 0) {  
            return nrOfToppingsCmp;  
        }  
        return p1.name.compareTo(p2.name);  
    }  
});  

이 작업에는 많은 타이핑, 유지 관리가 필요하며 오류가 발생하기 쉽습니다.

반사 방식 : BeanComparator를 사용한 정렬

ComparatorChain chain = new ComparatorChain(Arrays.asList(
   new BeanComparator("size"), 
   new BeanComparator("nrOfToppings"), 
   new BeanComparator("name")));

Collections.sort(pizzas, chain);  

분명히 이것은 더 간결하지만 문자열을 대신 사용하여 필드에 대한 직접 참조를 잃을수록 오류가 발생하기 쉽습니다 (유형 안전, 자동 리팩토링 없음). 이제 필드 이름이 바뀌면 컴파일러는 문제를보고하지도 않습니다. 또한이 솔루션은 리플렉션을 사용하므로 정렬 속도가 훨씬 느립니다.

도착 방법 : Google Guava의 ComparisonChain으로 정렬

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();  
        // or in case the fields can be null:  
        /* 
        return ComparisonChain.start() 
           .compare(p1.size, p2.size, Ordering.natural().nullsLast()) 
           .compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast()) 
           .compare(p1.name, p2.name, Ordering.natural().nullsLast()) 
           .result(); 
        */  
    }  
});  

이 방법이 훨씬 나아지지만 가장 일반적인 사용 사례에는 보일러 플레이트 코드가 필요합니다. 기본적으로 null 값의 값을 줄여야합니다. null 필드의 경우 Guava에이 경우 수행 할 작업에 대한 추가 지시문을 제공해야합니다. 이것은 특정 작업을 원하지만 기본 사례 (예 : 1, a, b, z, null)를 원하는 경우 유연한 메커니즘입니다.

Apache Commons CompareToBuilder를 사용한 정렬

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();  
    }  
});  

Guava의 ComparisonChain과 마찬가지로이 라이브러리 클래스는 여러 필드를 쉽게 정렬 할 수 있지만 null 값 (예 : 1, a, b, z, null)의 기본 동작도 정의합니다. 그러나 자체 비교기를 제공하지 않으면 다른 것을 지정할 수 없습니다.

그러므로

궁극적으로 그것은 맛과 유연성 (Guava 's ComparisonChain) 대 간결한 코드 (Apache 's CompareToBuilder)의 필요성으로 귀결됩니다.

보너스 방법

CodeReview 의 우선 순위 따라 여러 비교기를 결합한 멋진 솔루션을 찾았 습니다 MultiComparator.

class MultiComparator<T> implements Comparator<T> {
    private final List<Comparator<T>> comparators;

    public MultiComparator(List<Comparator<? super T>> comparators) {
        this.comparators = comparators;
    }

    public MultiComparator(Comparator<? super T>... comparators) {
        this(Arrays.asList(comparators));
    }

    public int compare(T o1, T o2) {
        for (Comparator<T> c : comparators) {
            int result = c.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }

    public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
        Collections.sort(list, new MultiComparator<T>(comparators));
    }
}

물론 Apache Commons Collections에는 이미 다음과 같은 유틸리티가 있습니다.

ComparatorUtils.chainedComparator (comparatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));

22

@Patrick 여러 필드를 연속적으로 정렬하려면 ComparatorChain을 시도하십시오.

ComparatorChain은 하나 이상의 Comparator를 순서대로 래핑하는 Comparator입니다. ComparatorChain은 1) 단일 Comparator가 0이 아닌 결과를 반환하고 그 결과가 반환 될 때까지 또는 2) ComparatorChain이 소진되고 0이 반환 될 때까지 각 Comparator를 순차적으로 호출합니다. 이 유형의 정렬은 SQL의 다중 열 정렬과 매우 유사하며이 클래스를 사용하면 Java 클래스가 List를 정렬 할 때 이러한 종류의 동작을 에뮬레이트 할 수 있습니다.

SQL과 같은 정렬을 더욱 용이하게하기 위해 목록의 단일 비교기 순서를 반대로 바꿀 수 있습니다.

compare (Object, Object)가 호출 된 후 새 Comparators를 추가하거나 오름차순 / 내림차순 정렬을 변경하는 메소드를 호출하면 UnsupportedOperationException이 발생합니다. 그러나 정렬 목록을 정의하는 기본 비교기 목록 또는 BitSet을 변경하지 않도록주의하십시오.

ComparatorChain 인스턴스는 동기화되지 않습니다. 클래스는 생성시 스레드로부터 안전하지 않지만 모든 설정 작업이 완료된 후 다중 비교를 수행하는 것은 안전합니다.


20

항상 고려할 수있는 또 다른 옵션은 Apache Commons입니다. 많은 옵션을 제공합니다.

import org.apache.commons.lang3.builder.CompareToBuilder;

전의:

public int compare(Person a, Person b){

   return new CompareToBuilder()
     .append(a.getName(), b.getName())
     .append(a.getAddress(), b.getAddress())
     .toComparison();
}


10
import com.google.common.collect.ComparisonChain;

/**
 * @author radler
 * Class Description ...
 */
public class Attribute implements Comparable<Attribute> {

    private String type;
    private String value;

    public String getType() { return type; }
    public void setType(String type) { this.type = type; }

    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }

    @Override
    public String toString() {
        return "Attribute [type=" + type + ", value=" + value + "]";
    }

    @Override
    public int compareTo(Attribute that) {
        return ComparisonChain.start()
            .compare(this.type, that.type)
            .compare(this.value, that.value)
            .result();
    }

}

1
나는이 전략을 많이 좋아한다. 감사!
Mr. Polywhirl

가장 효과적인 방법! 감사합니다
Zakaria Bouazza

8

Java 8 스트리밍 API를 사용할 수있는 사람들을 위해 여기에 잘 정리 된 깔끔한 접근 방식이 있습니다 : Lambdas 및 정렬

C # LINQ와 동등한 것을 찾고있었습니다.

.ThenBy(...)

비교기에서 Java 8의 메커니즘을 찾았습니다.

.thenComparing(...)

여기 알고리즘을 보여주는 스 니펫이 있습니다.

    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

더 깔끔한 방법과 위의 링크를 확인하여 Java의 유형 유추가 LINQ와 비교하여 정의하기가 조금 더 어색한 방법에 대한 설명을 확인하십시오.

참조를위한 전체 단위 테스트는 다음과 같습니다.

@Test
public void testChainedSorting()
{
    // Create the collection of people:
    ArrayList<Person> people = new ArrayList<>();
    people.add(new Person("Dan", 4));
    people.add(new Person("Andi", 2));
    people.add(new Person("Bob", 42));
    people.add(new Person("Debby", 3));
    people.add(new Person("Bob", 72));
    people.add(new Person("Barry", 20));
    people.add(new Person("Cathy", 40));
    people.add(new Person("Bob", 40));
    people.add(new Person("Barry", 50));

    // Define chained comparators:
    // Great article explaining this and how to make it even neater:
    // http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

    // Sort the stream:
    Stream<Person> personStream = people.stream().sorted(comparator);

    // Make sure that the output is as expected:
    List<Person> sortedPeople = personStream.collect(Collectors.toList());
    Assert.assertEquals("Andi",  sortedPeople.get(0).name); Assert.assertEquals(2,  sortedPeople.get(0).age);
    Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
    Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
    Assert.assertEquals("Bob",   sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
    Assert.assertEquals("Bob",   sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
    Assert.assertEquals("Bob",   sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
    Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
    Assert.assertEquals("Dan",   sortedPeople.get(7).name); Assert.assertEquals(4,  sortedPeople.get(7).age);
    Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3,  sortedPeople.get(8).age);
    // Andi     : 2
    // Barry    : 20
    // Barry    : 50
    // Bob      : 40
    // Bob      : 42
    // Bob      : 72
    // Cathy    : 40
    // Dan      : 4
    // Debby    : 3
}

/**
 * A person in our system.
 */
public static class Person
{
    /**
     * Creates a new person.
     * @param name The name of the person.
     * @param age The age of the person.
     */
    public Person(String name, int age)
    {
        this.age = age;
        this.name = name;
    }

    /**
     * The name of the person.
     */
    public String name;

    /**
     * The age of the person.
     */
    public int age;

    @Override
    public String toString()
    {
        if (name == null) return super.toString();
        else return String.format("%s : %d", this.name, this.age);
    }
}

7

Comparator이러한 사용 사례에 대해 수동으로 작성하는 것은 끔찍한 솔루션 IMO입니다. 이러한 임시 접근 방식에는 많은 단점이 있습니다.

  • 코드 재사용이 없습니다. 건조를 위반합니다.
  • 보일러 플레이트.
  • 오류 가능성 증가.

그래서 해결책은 무엇입니까?

먼저 몇 가지 이론.

로 "유형 A지원 비교"라는 명제를로 표현하자 Ord A. (프로그램 관점에서 볼 때, Ord AAs 를 비교하기위한 논리를 포함하는 객체로 생각할 수 있습니다 . 예, Comparator)

이제 경우 Ord AOrd B, 그들의 복합은 (A, B)또한 비교를 지원해야한다. 즉 Ord (A, B). 만약 Ord A, Ord B, 그리고 Ord C다음,Ord (A, B, C) .

우리는이 주장을 임의의 성실로 확장하고 말할 수 있습니다.

Ord A, Ord B, Ord C, ..., Ord ZOrd (A, B, C, .., Z)

이 진술을 1이라고합시다.

복합 재료의 비교는 질문에 설명 된대로 작동합니다. 첫 번째 비교가 먼저 시도되고 다음 비교가 다음에 수행됩니다.

이것이 우리 솔루션의 첫 번째 부분입니다. 이제 두 번째 부분입니다.

당신이 알고 있다면 Ord A, 그리고 변환하는 방법을 알고 BA(변환 함수가 호출 f), 당신도 할 수 있습니다 Ord B. 어떻게? 두 B인스턴스를 비교할 때는 먼저이를 A사용 하도록 변환 f한 다음 적용하십시오.Ord A .

여기에서는 변환 B → A을 로 매핑합니다 Ord A → Ord B. 이것을 반 변형 매핑이라고 comap합니다.

Ord A, (B → A)코프 Ord B

이 진술을 2라고합시다.


이제 이것을 예제에 적용 해 봅시다.

유형이 Person세 개의 필드로 구성된 데이터 유형이 String있습니다.

  • 우리는 알고 Ord String있습니다. 문 1으로 Ord (String, String, String).

  • 에서 Person까지 함수를 쉽게 작성할 수 있습니다 (String, String, String). 우리가 알고 있기 때문에 (그냥. 세 개의 필드를 반환) Ord (String, String, String)하고 Person → (String, String, String)우리가 사용할 수있는, 문 2, comap얻을 Ord Person.

QED.


이 모든 개념을 어떻게 구현합니까?

좋은 소식은 당신이 할 필요가 없다는 것입니다. 이 게시물에 설명 된 모든 아이디어를 구현 하는 라이브러리 가 이미 있습니다 . (이들이 어떻게 구현되는지 궁금하다면 , 후드를 살펴보십시오. .)

이것이 코드와 함께 보일 것입니다 :

Ord<Person> personOrd = 
 p3Ord(stringOrd, stringOrd, stringOrd).comap(
   new F<Person, P3<String, String, String>>() {
     public P3<String, String, String> f(Person x) {
       return p(x.getFirstName(), x.getLastname(), x.getAge());
     }
   }
 );

설명:

  • stringOrd유형의 객체입니다 Ord<String>. 이것은 원래의 "비교 지원"제안에 해당합니다.
  • p3Ord걸리는 방법이다 Ord<A>, Ord<B>, Ord<C>, 및 반환 Ord<P3<A, B, C>>. 이는 설명 1에 해당합니다. ( P3세 가지 요소가있는 제품을 나타냅니다. 제품은 복합재의 대수적 용어입니다.)
  • comap잘 대응합니다 comap.
  • F<A, B>변환 함수를 나타냅니다 A → B.
  • p 제품을 만드는 팩토리 방법입니다.
  • 전체 표현은 문장 2에 해당합니다.

희망이 도움이됩니다.


5

비교 메소드 대신 Person 클래스 내에 여러 유형의 "Comparator"서브 클래스를 정의 할 수도 있습니다. 이렇게하면 표준 컬렉션 정렬 방법으로 전달할 수 있습니다.


3

비교 알고리즘이 "영리한"경우 더 혼란 스러울 것 같습니다. 나는 당신이 제안한 수많은 비교 방법을 사용합니다.

나에게 유일한 예외는 평등입니다. 단위 테스트의 경우 .Equals (.net에서)를 재정 의하여 두 필드간에 여러 필드가 같은지 여부와 참조가 같은지 여부를 확인하는 것이 유용했습니다.


3

사용자가 여러 가지 방법으로 사람을 주문할 수 있다면 어딘가에 상수로 여러 비교기 설정 을 할 수도 있습니다 . 정렬 작업 및 정렬 된 컬렉션의 대부분은 비교자를 매개 변수로 사용합니다.


3
//Following is the example in jdk 1.8
package com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

class User {
    private String firstName;
    private String lastName;
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public User setAge(Integer age) {
        this.age = age;
        return this;
    }

    public String getFirstName() {
        return firstName;
    }

    public User setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public User setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

}

public class MultiFieldsComparision {

    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();

        User u1 = new User().setFirstName("Pawan").setLastName("Singh").setAge(38);
        User u2 = new User().setFirstName("Pawan").setLastName("Payal").setAge(37);
        User u3 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(60);
        User u4 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(43);
        User u5 = new User().setFirstName("Pawan").setLastName("Chamoli").setAge(44);
        User u6 = new User().setFirstName("Pawan").setLastName("Singh").setAge(5);

        users.add(u1);
        users.add(u2);
        users.add(u3);
        users.add(u4);
        users.add(u5);
        users.add(u6);

        System.out.println("****** Before Sorting ******");

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

        System.out.println("****** Aftre Sorting ******");

        users.sort(
                Comparator.comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getAge));

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

    }

}

3

여러 필드를 기반으로 Person 객체를 정렬 해야하는 경우 동일한 코드 구현이 여기에 있습니다.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Person {

private String firstName;
private String lastName;
private int age;

public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return lastName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public Person(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
}


static class PersonSortingComparator implements Comparator<Person> {

    @Override
    public int compare(Person person1, Person person2) {

        // for first name comparison
        int firstNameCompare = person1.getFirstName().compareTo(person2.getFirstName());

        // for last name comparison
        int lastNameCompare = person1.getLastName().compareTo(person2.getLastName());

        // for last name comparison
        int ageCompare = person1.getAge() - person2.getAge();

        // Now comparing
        if (firstNameCompare == 0) {
            if (lastNameCompare == 0) {
                return ageCompare;
            }
            return lastNameCompare;
        }
        return firstNameCompare;
    }
}

public static void main(String[] args) {
    Person person1 = new Person("Ajay", "Kumar", 27);
    Person person2 = new Person("Ajay","Gupta", 23);
    Person person3 = new Person("Ajay","Kumar", 22);


    ArrayList<Person> persons = new ArrayList<>();
    persons.add(person1);
    persons.add(person2);
    persons.add(person3);


    System.out.println("Before Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }

    Collections.sort(persons, new PersonSortingComparator());

    System.out.println("After Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }
}

}

2
//here threshold,buyRange,targetPercentage are three keys on that i have sorted my arraylist 
final Comparator<BasicDBObject> 

    sortOrder = new Comparator<BasicDBObject>() {
                    public int compare(BasicDBObject e1, BasicDBObject e2) {
                        int threshold = new Double(e1.getDouble("threshold"))
                        .compareTo(new Double(e2.getDouble("threshold")));
                        if (threshold != 0)
                            return threshold;

                        int buyRange = new Double(e1.getDouble("buyRange"))
                        .compareTo(new Double(e2.getDouble("buyRange")));
                        if (buyRange != 0)
                            return buyRange;

                        return (new Double(e1.getDouble("targetPercentage")) < new Double(
                                e2.getDouble("targetPercentage")) ? -1 : (new Double(
                                        e1.getDouble("targetPercentage")) == new Double(
                                                e2.getDouble("targetPercentage")) ? 0 : 1));
                    }
                };
                Collections.sort(objectList, sortOrder);

내 코드가 답을 좋아하기 시작했기 때문에이 질문에 왔습니다.)
jan groth

0

Comparable 인터페이스 를 구현하는 경우 주문할 간단한 속성 하나를 선택해야합니다. 이것을 자연 순서라고합니다. 기본값으로 생각하십시오. 특정 비교기가 제공되지 않을 때 항상 사용됩니다. 일반적으로 이것은 이름이지만 사용 사례는 다른 것을 요구할 수 있습니다. 다양한 컬렉션 API에 제공 할 수있는 다른 비교기를 자유롭게 사용하여 자연 순서를 무시할 수 있습니다.

또한 일반적으로 a.compareTo (b) == 0이면 a.equals (b) == true입니다. 그렇지 않다면 괜찮지 만 알고 있어야 할 부작용이 있습니다. Comparable 인터페이스에서 훌륭한 javadoc을 확인하면 이에 대한 많은 정보를 얻을 수 있습니다.


0

다음은 좋은 체인 비교기 예제를 제공하는 블로그입니다.

http://www.codejava.net/java-core/collections/sorting-a-list-by-multiple-attributes-example

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * This is a chained comparator that is used to sort a list by multiple
 * attributes by chaining a sequence of comparators of individual fields
 * together.
 *
 */
public class EmployeeChainedComparator implements Comparator<Employee> {

    private List<Comparator<Employee>> listComparators;

    @SafeVarargs
    public EmployeeChainedComparator(Comparator<Employee>... comparators) {
        this.listComparators = Arrays.asList(comparators);
    }

    @Override
    public int compare(Employee emp1, Employee emp2) {
        for (Comparator<Employee> comparator : listComparators) {
            int result = comparator.compare(emp1, emp2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

호출 비교기 :

Collections.sort(listEmployees, new EmployeeChainedComparator(
                new EmployeeJobTitleComparator(),
                new EmployeeAgeComparator(),
                new EmployeeSalaryComparator())
        );

0

Steve의 대답 에서 시작 하여 삼항 연산자를 사용할 수 있습니다.

public int compareTo(Person other) {
    int f = firstName.compareTo(other.firstName);
    int l = lastName.compareTo(other.lastName);
    return f != 0 ? f : l != 0 ? l : Integer.compare(age, other.age);
}

0

자바에서 두 객체를 해시 코드 메소드와 쉽게 비교할 수 있습니다`

public class Sample{

  String a=null;
  String b=null;

  public Sample(){
      a="s";
      b="a";
  }
  public Sample(String a,String b){
      this.a=a;
      this.b=b;
  }
  public static void main(String args[]){
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","12");
      //will return true
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

      //will return false
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","13");
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

}

그렇게하지 마십시오. 해시 코드는 동등 비교에는 사용하지 말고 해시 테이블 인덱싱에는 사용하지 않아야합니다. 해시 충돌은 서로 다른 두 객체에 대해 동일 할 수 있습니다. 해시 테이블조차도 해시 충돌이 발생하면 실제 평등 에 의존 합니다.
Noel Widmer

0

일반적으로 compareTo()다중 수준 정렬을 수행해야 할 때마다이 방법을 재정의 합니다.

public int compareTo(Song o) {
    // TODO Auto-generated method stub
    int comp1 = 10000000*(movie.compareTo(o.movie))+1000*(artist.compareTo(o.artist))+songLength;
    int comp2 = 10000000*(o.movie.compareTo(movie))+1000*(o.artist.compareTo(artist))+o.songLength;
    return comp1-comp2;
} 

여기서는 영화 이름이 우선이고 아티스트가 마지막으로 songLength가 우선입니다. 그 승수들이 서로의 경계를 넘지 않을 정도로 먼 거리에 있는지 확인하면됩니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.