[java] 다른 시간에 다른 매개 변수로 목록을 정렬하는 방법

Person예를 들어 여러 속성으로 명명 된 클래스가 있습니다 .

public class Person {
    private int id;
    private String name, address;
    // Many more properties.
}

많은 Person-object가 ArrayList<Person>. 이 목록을 여러 개의 정렬 매개 변수로 정렬하고 싶고, 때때로 다릅니다. 예를 들어 한 번은 name오름차순과 address내림차순 으로 정렬 하고 다른 시간에는 내림차순 으로 정렬하고 싶을 수 있습니다 id.

그리고 나만의 정렬 방법을 만들고 싶지 않습니다 (예 :를 사용하고 싶습니다 Collections.sort(personList, someComparator).이를 달성하는 가장 우아한 솔루션은 무엇입니까?



답변

enum 접근 방식은 기본적으로 건전하다고 생각하지만 switch 문에는 실제로 더 많은 객체 지향 접근 방식이 필요합니다. 치다:

enum PersonComparator implements Comparator<Person> {
    ID_SORT {
        public int compare(Person o1, Person o2) {
            return Integer.valueOf(o1.getId()).compareTo(o2.getId());
        }},
    NAME_SORT {
        public int compare(Person o1, Person o2) {
            return o1.getFullName().compareTo(o2.getFullName());
        }};

    public static Comparator<Person> decending(final Comparator<Person> other) {
        return new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                return -1 * other.compare(o1, o2);
            }
        };
    }

    public static Comparator<Person> getComparator(final PersonComparator... multipleOptions) {
        return new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                for (PersonComparator option : multipleOptions) {
                    int result = option.compare(o1, o2);
                    if (result != 0) {
                        return result;
                    }
                }
                return 0;
            }
        };
    }
}

사용 예 (정적 가져 오기 사용).

public static void main(String[] args) {
    List<Person> list = null;
    Collections.sort(list, decending(getComparator(NAME_SORT, ID_SORT)));
}


답변

정렬하려는 각 속성에 대한 비교기를 만든 다음 다음과 같이 “비교 자 연결”을 시도 할 수 있습니다. 🙂

public class ChainedComparator<T> implements Comparator<T> {
    private List<Comparator<T>> simpleComparators; 
    public ChainedComparator(Comparator<T>... simpleComparators) {
        this.simpleComparators = Arrays.asList(simpleComparators);
    }
    public int compare(T o1, T o2) {
        for (Comparator<T> comparator : simpleComparators) {
            int result = comparator.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}


답변

한 가지 방법은 Comparator이 예제에서 볼 수 있듯이 정렬 할 속성 목록을 인수로 취하는를 만드는 것 입니다.

public class Person {
    private int id;
    private String name, address;

    public static Comparator<Person> getComparator(SortParameter... sortParameters) {
        return new PersonComparator(sortParameters);
    }

    public enum SortParameter {
        ID_ASCENDING, ID_DESCENDING, NAME_ASCENDING,
        NAME_DESCENDING, ADDRESS_ASCENDING, ADDRESS_DESCENDING
    }

    private static class PersonComparator implements Comparator<Person> {
        private SortParameter[] parameters;

        private PersonComparator(SortParameter[] parameters) {
            this.parameters = parameters;
        }

        public int compare(Person o1, Person o2) {
            int comparison;
            for (SortParameter parameter : parameters) {
                switch (parameter) {
                    case ID_ASCENDING:
                        comparison = o1.id - o2.id;
                        if (comparison != 0) return comparison;
                        break;
                    case ID_DESCENDING:
                        comparison = o2.id - o1.id;
                        if (comparison != 0) return comparison;
                        break;
                    case NAME_ASCENDING:
                        comparison = o1.name.compareTo(o2.name);
                        if (comparison != 0) return comparison;
                        break;
                    case NAME_DESCENDING:
                        comparison = o2.name.compareTo(o1.name);
                        if (comparison != 0) return comparison;
                        break;
                    case ADDRESS_ASCENDING:
                        comparison = o1.address.compareTo(o2.address);
                        if (comparison != 0) return comparison;
                        break;
                    case ADDRESS_DESCENDING:
                        comparison = o2.address.compareTo(o1.address);
                        if (comparison != 0) return comparison;
                        break;
                }
            }
            return 0;
        }
    }
}

그런 다음 다음과 같은 코드에서 사용할 수 있습니다.

cp = Person.getComparator(Person.SortParameter.ADDRESS_ASCENDING,
                          Person.SortParameter.NAME_DESCENDING);
Collections.sort(personList, cp);


답변

한 가지 접근 방식은 Comparators 를 작성하는 것 입니다. 이것은 라이브러리 메서드 일 수 있습니다 (저기 어딘가에 존재한다고 확신합니다).

public static <T> Comparator<T> compose(
    final Comparator<? super T> primary,
    final Comparator<? super T> secondary
) {
    return new Comparator<T>() {
        public int compare(T a, T b) {
            int result = primary.compare(a, b);
            return result==0 ? secondary.compare(a, b) : result;
        }
        [...]
    };
}

사용하다:

Collections.sort(people, compose(nameComparator, addressComparator));

또는 Collections.sort안정적인 정렬입니다. 성능이 절대적으로 중요하지 않은 경우 기본 순서보다 2 차 순서로 정렬합니다.

Collections.sort(people, addressComparator);
Collections.sort(people, nameComparator);


답변

비교기를 사용하면 매우 쉽고 자연스럽게 할 수 있습니다. Person 클래스 자체 또는 필요와 관련된 Service 클래스에서 비교기의 단일 인스턴스를 만들 수 있습니다.
익명 내부 클래스를 사용하는 예 :

    public static final Comparator<Person> NAME_ASC_ADRESS_DESC
     = new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
         int nameOrder = p1.getName().compareTo(p2.getName);
         if(nameOrder != 0) {
           return nameOrder;
         }
         return -1 * p1.getAdress().comparedTo(p2.getAdress());
         // I use explicit -1 to be clear that the order is reversed
      }
    };

    public static final Comparator<Person> ID_DESC
     = new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
         return -1 * p1.getId().comparedTo(p2.getId());
         // I use explicit -1 to be clear that the order is reversed
      }
    };
    // and other comparator instances as needed... 

많은 경우 원하는 방식으로 비교기 코드를 구성 할 수도 있습니다 . 예를 들어 다음과 같이 할 수 있습니다.

  • 다른 비교기에서 상속,
  • 기존 비교기를 집계하는 CompositeComparator가 있습니다.
  • null 케이스를 처리하는 NullComparator가 있고 다른 비교기에 위임
  • 기타…


답변

나는 당신의 대답과 같이 분류기를 Person 클래스에 결합하는 것은 좋은 생각이 아니라고 생각합니다. 왜냐하면 그것은 비교 (일반적으로 비즈니스 중심)와 모델 객체를 서로 가깝게 결합하기 때문입니다. 분류기에서 무언가를 변경 / 추가하고 싶을 때마다 사람 클래스를 터치해야합니다. 일반적으로 원하지 않는 일입니다.

KLE가 제안한 것과 같은 Comparator 인스턴스를 제공하는 서비스 또는 이와 유사한 것을 사용하면 훨씬 유연하고 확장 할 수 있습니다.


답변

내 접근 방식은 Yishai를 기반으로합니다. 주된 차이는 속성에 대해 먼저 오름차순으로 정렬하고 그 후에 다른 속성에 대해 내림차순으로 정렬 할 수있는 방법이 없다는 것입니다. 열거 형으로는 수행 할 수 없습니다. 그것을 위해 나는 수업을 사용했습니다. SortOrder는 내부 클래스로 구현하기를 선호하는 유형에 크게 의존하기 때문입니다.

내부 클래스가 ‘SortOrder’인 ‘Person’클래스 :

import java.util.Comparator;

public class Person {
    private int id;
    private String firstName;
    private String secondName;

    public Person(int id, String firstName, String secondName) {
        this.id = id;
        this.firstName = firstName;
        this.secondName = secondName;
    }

    public abstract static class SortOrder implements Comparator<Person> {
        public static SortOrder PERSON_ID = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return Integer.valueOf(p1.getId()).compareTo(p2.getId());
            }
        };
        public static SortOrder PERSON_FIRST_NAME = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return p1.getFirstName().compareTo(p2.getFirstName());
            }
        };
        public static SortOrder PERSON_SECOND_NAME = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return p1.getSecondName().compareTo(p2.getSecondName());
            }
        };

        public static SortOrder invertOrder(final SortOrder toInvert) {
            return new SortOrder() {
                public int compare(Person p1, Person p2) {
                    return -1 * toInvert.compare(p1, p2);
                }
            };
        }

        public static Comparator<Person> combineSortOrders(final SortOrder... multipleSortOrders) {
            return new Comparator<Person>() {
                public int compare(Person p1, Person p2) {
                    for (SortOrder personComparator: multipleSortOrders) {
                        int result = personComparator.compare(p1, p2);
                        if (result != 0) {
                            return result;
                        }
                    }
                    return 0;
                }
            };
        }
    }

    public int getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getSecondName() {
        return secondName;
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();

        result.append("Person with id: ");
        result.append(id);
        result.append(" and firstName: ");
        result.append(firstName);
        result.append(" and secondName: ");
        result.append(secondName);
        result.append(".");

        return result.toString();
    }
}

Person 클래스와 그 SortOrder를 사용하는 예 :

import static multiplesortorder.Person.SortOrder.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import multiplesortorder.Person;

public class Application {

    public static void main(String[] args) {
        List<Person> listPersons = new ArrayList<Person>(Arrays.asList(
                 new Person(0, "...", "..."),
                 new Person(1, "...", "...")
             ));

         Collections.sort(listPersons, combineSortOrders(PERSON_FIRST_NAME, invertOrder(PERSON_ID)));

         for (Person p: listPersons) {
             System.out.println(p.toString());
         }
    }
}

오 루무