사용자 지정 정렬 순서를 사용하여 개체의 ArrayList 정렬


119

내 주소록 응용 프로그램에 대한 정렬 기능을 구현하려고합니다.

나는 ArrayList<Contact> contactArray. Contact이름, 집 번호, 휴대폰 번호 및 주소의 네 가지 필드가 포함 된 클래스입니다. 정렬하고 싶습니다 name.

이를 위해 사용자 지정 정렬 함수를 작성하려면 어떻게해야합니까?

답변:


270

다음은 개체 주문에 대한 자습서입니다.

몇 가지 예를 들겠지만 어쨌든 읽어 보는 것이 좋습니다.


.NET Framework를 정렬하는 방법에는 여러 가지가 있습니다 ArrayList. 자연 (기본) 순서 를 정의 하려면 Contact구현 을 허용해야합니다 Comparable. 기본적으로에서 정렬하고 싶다고 가정하고 name다음을 수행하십시오 (간단 함을 위해 nullchecks 생략).

public class Contact implements Comparable<Contact> {

    private String name;
    private String phone;
    private Address address;

    public int compareTo(Contact other) {
        return name.compareTo(other.name);
    }

    // Add/generate getters/setters and other boilerplate.
}

당신이 할 수 있도록

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

Collections.sort(contacts);

제어 가능한 외부 순서 (자연 순서를 재정의) 를 정의하려면 다음을 생성해야합니다 Comparator.

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Now sort by address instead of name (default).
Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

매번 다시 만드는 대신 재사용 할 수 있도록 자체적으로 Comparators를 정의 Contact할 수도 있습니다.

public class Contact {

    private String name;
    private String phone;
    private Address address;

    // ...

    public static Comparator<Contact> COMPARE_BY_PHONE = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.phone.compareTo(other.phone);
        }
    };

    public static Comparator<Contact> COMPARE_BY_ADDRESS = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.address.compareTo(other.address);
        }
    };

}

다음과 같이 사용할 수 있습니다.

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Sort by address.
Collections.sort(contacts, Contact.COMPARE_BY_ADDRESS);

// Sort later by phone.
Collections.sort(contacts, Contact.COMPARE_BY_PHONE);

그리고 마무리 하기 위해 일반적인 javabean 비교기 를 사용하는 것을 고려할 수 있습니다 .

public class BeanComparator implements Comparator<Object> {

    private String getter;

    public BeanComparator(String field) {
        this.getter = "get" + field.substring(0, 1).toUpperCase() + field.substring(1);
    }

    public int compare(Object o1, Object o2) {
        try {
            if (o1 != null && o2 != null) {
                o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);
                o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]);
            }
        } catch (Exception e) {
            // If this exception occurs, then it is usually a fault of the developer.
            throw new RuntimeException("Cannot compare " + o1 + " with " + o2 + " on " + getter, e);
        }

        return (o1 == null) ? -1 : ((o2 == null) ? 1 : ((Comparable<Object>) o1).compareTo(o2));
    }

}

다음과 같이 사용할 수 있습니다.

// Sort on "phone" field of the Contact bean.
Collections.sort(contacts, new BeanComparator("phone"));

(코드에서 볼 수 있듯이 정렬 중에 NPE를 피하기 위해 null 필드가 이미 포함되어있을 수 있습니다.)


2
여러 비교기를 미리 정의한 다음 이름으로 사용할 가능성을 추가하겠습니다.
Stobor

2
사실, 방금했습니다. 나 자신을 설명하는 것보다 쉽습니다.
Stobor

@BalusC : 문제가 없습니다. 나는 그 아이디어에 대한 공로를 인정할 수없고 String.CASE_INSENSITIVE_ORDER, 친구들 에게서 받았지만 좋아합니다. 결과 코드를 더 쉽게 읽을 수 있습니다.
Stobor

1
그 비교기 정의는 아마도해야 static어쩌면 final너무 ... 또는 그 .. 같은
Stobor

Heheh ... BeanComparator는 굉장한 스틱과 같습니다! :-) (나는 널의 정확한 논리 비교를 기억하지 못하지만 (o1 == null && o2 == null) ? 0 :그 리턴 라인의 시작 부분에 a 를
원합니까

28

이미 게시 된 내용 외에도 Java 8 이후로 코드를 단축하고 다음과 같이 작성할 수 있습니다.

Collection.sort(yourList, Comparator.comparing(YourClass::getFieldToSortOn));

또는 List에 이제 sort메서드 가 있기 때문에

yourList.sort(Comparator.comparing(YourClass::getFieldToSortOn));

설명:

Java 8부터 기능적 인터페이스 (추상 메소드가 하나 뿐인 인터페이스-기본 또는 정적 메소드를 더 많이 가질 수 있음)는 다음을 사용하여 쉽게 구현할 수 있습니다.

Comparator<T>추상 메소드 int compare(T o1, T o2)가 하나뿐 이므로 기능적 인터페이스입니다.

그래서 대신 ( @BalusC 답변의 예 )

Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

이 코드를 다음과 같이 줄일 수 있습니다.

Collections.sort(contacts, (Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress());
});

이 람다를 단순화 할 수 있습니다.

  • 인수 유형 (Java는 메소드 서명을 기반으로 유추합니다)
  • 또는 {return...}

그래서 대신

(Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress();
}

우리는 쓸 수있다

(one, other) -> one.getAddress().compareTo(other.getAddress())

또한 이제 객체의 특정 값을 비교해야하는 비교기를 쉽게 만드는 데 사용할 수있는 또는 Comparator같은 정적 메서드 가 있습니다.comparing(FunctionToComparableValue)comparing(FunctionToValue, ValueComparator)

즉, 위의 코드를 다음과 같이 다시 작성할 수 있습니다.

Collections.sort(contacts, Comparator.comparing(Contact::getAddress)); 
//assuming that Address implements Comparable (provides default order).

8

이 페이지 에서는 ArrayList와 같은 컬렉션 정렬에 대해 알아야 할 모든 정보를 제공합니다.

기본적으로

  • 당신 만들 Contact클래스가 구현 Comparable으로 인터페이스를
    • public int compareTo(Contact anotherContact)안에 메서드를 생성 합니다.
  • 이 작업을 수행하면 Collections.sort(myContactList);,
    • 여기서 myContactListArrayList<Contact>(또는 다른 콜렉션 Contact).

Comparator 클래스를 만드는 다른 방법도 있습니다. 링크 된 페이지에서도 이에 대해 읽을 수 있습니다.

예:

public class Contact implements Comparable<Contact> {

    ....

    //return -1 for less than, 0 for equals, and 1 for more than
    public compareTo(Contact anotherContact) {
        int result = 0;
        result = getName().compareTo(anotherContact.getName());
        if (result != 0)
        {
            return result;
        }
        result = getNunmber().compareTo(anotherContact.getNumber());
        if (result != 0)
        {
            return result;
        }
        ...
    }
}

5

BalusC와 bguiz는 Java의 내장 비교기를 사용하는 방법에 대한 완전한 답변을 이미 제공했습니다.

google-collections 에 표준 비교기보다 "강력한" Ordering 클래스 가 있다는 점을 추가하고 싶습니다 . 체크 아웃 할 가치가 있습니다. 당신은 당신의 객체에 대한 함수의 결과에 따라 순서를 합성하고, 그것들을 뒤집고, 순서를 정하는 것과 같은 멋진 일을 할 수 있습니다.

다음 은 몇 가지 이점을 언급하는 블로그 게시물입니다.


google-collections는 이제 Guava (Google의 공통 자바 라이브러리)의 일부이므로 Ordering 클래스를 사용하려면 Guava (또는 Guava의 컬렉션 모듈)에 의존하는 것이 좋습니다.
Etienne Neveu

4

Contact 클래스가 Comparable 을 구현하도록 한 다음 compareTo(Contact)메서드 를 구현해야 합니다. 이렇게하면 Collections.sort가이를 정렬 할 수 있습니다. 내가 링크 한 페이지에 따라 compareTo '이 개체가 지정된 개체보다 작거나 같거나 크므로 음의 정수, 0 또는 양의 정수를 반환합니다.'

예를 들어 이름 (A에서 Z까지)으로 정렬하려는 경우 클래스는 다음과 같습니다.

public class Contact implements Comparable<Contact> {

    private String name;

    // all the other attributes and methods

    public compareTo(Contact other) {
        return this.name.compareTo(other.name);
    }
}

저와 잘 어울 렸습니다. 감사합니다! 또한 compareToIgnoreCase를 사용하여 대소 문자를 무시했습니다.
라니 Kheir

3

lambdaj 를 사용 하면 다음과 같이 연락처 모음 (예 : 이름순)을 정렬 할 수 있습니다.

sort(contacts, on(Contact.class).getName());

또는 그들의 주소로 :

sort(contacts, on(Contacts.class).getAddress());

등등. 일반적으로 일부 조건에 따라 연락처를 필터링하거나 그룹화하고 일부 속성 값을 집계하는 등 다양한 방법으로 컬렉션에 액세스하고 조작 할 수있는 DSL을 제공합니다.


0

Collections.sort는 좋은 정렬 구현입니다. Contact에 대한 비교 가능한 구현이없는 경우 Comparator 구현 을 전달해야합니다.

참고 :

정렬 알고리즘은 수정 된 병합 정렬 (하위 하위 목록의 가장 높은 요소가 상위 하위 목록의 가장 낮은 요소보다 작은 경우 병합이 생략 됨)입니다. 이 알고리즘은 보장 된 n log (n) 성능을 제공합니다. 지정된 목록은 수정 가능해야하지만 크기를 조정할 필요는 없습니다. 이 구현은 지정된 목록을 배열로 덤프하고 배열을 정렬하며 목록을 반복하여 배열의 해당 위치에서 각 요소를 재설정합니다. 이렇게하면 연결된 목록을 제자리에 정렬하려고 시도 할 때 발생하는 n2 log (n) 성능을 피할 수 있습니다.

병합 정렬은 가능한 대부분의 검색 알고리즘보다 낫습니다.


0

나는 다음과 같은 방법으로 그것을했다. 번호와 이름은 두 개의 arraylist입니다. 나는 이름을 정렬해야합니다. 이름 arralist 순서에 변경 사항이 발생하면 번호 arraylist도 순서를 변경합니다.

public void sortval(){

        String tempname="",tempnum="";

         if (name.size()>1) // check if the number of orders is larger than 1
            {
                for (int x=0; x<name.size(); x++) // bubble sort outer loop
                {
                    for (int i=0; i < name.size()-x-1; i++) {
                        if (name.get(i).compareTo(name.get(i+1)) > 0)
                        {

                            tempname = name.get(i);

                            tempnum=number.get(i);


                           name.set(i,name.get(i+1) );
                           name.set(i+1, tempname);

                            number.set(i,number.get(i+1) );
                            number.set(i+1, tempnum);


                        }
                    }
                }
            }



}

이것을 작성하는 데 더 오래 걸리고, 최적의 정렬 성능이 떨어지고, 더 많은 버그 (그리고 더 많은 테스트)를 작성하고, 코드를 다른 사람에게 전송하기가 더 어려워 질 것입니다. 그래서 그것은 옳지 않습니다. 작동 할 수 있지만 그것이 정확하지 않습니다.
에릭

0

이 방법을 사용하십시오.

private ArrayList<myClass> sortList(ArrayList<myClass> list) {
    if (list != null && list.size() > 1) {
        Collections.sort(list, new Comparator<myClass>() {
            public int compare(myClass o1, myClass o2) {
                if (o1.getsortnumber() == o2.getsortnumber()) return 0;
                return o1.getsortnumber() < o2.getsortnumber() ? 1 : -1;
            }
        });
    }
    return list;
}

`

및 사용 : mySortedlist = sortList(myList); 클래스에서 비교기를 구현할 필요가 없습니다. 당신이 역순으로 교환을 원하는 경우 1-1


0

예, 이것은 오래 전에 답변 된 것을 알고 있습니다 ... 그러나 다음은 몇 가지 새로운 정보입니다.

문제의 Contact 클래스에 Comparable 구현을 통해 이미 정의 된 자연 순서가 있지만 이름으로 해당 순서를 재정의하고 싶다고 가정합니다. 이를 수행하는 현대적인 방법은 다음과 같습니다.

List<Contact> contacts = ...;

contacts.sort(Comparator.comparing(Contact::getName).reversed().thenComparing(Comparator.naturalOrder());

이렇게하면 먼저 이름별로 (역순으로) 정렬 한 다음 이름 충돌의 경우 Contact 클래스 자체에서 구현 한 '자연스러운'순서로 돌아갑니다.


-1

Arrays.sort 함수를 사용하십시오. 포함하는 클래스는 Comparable을 구현해야합니다.


그것이 내가 Arrays라고 말한 이유입니다.
monksy

문제는 OP가 배열이 아닌 ArrayList를 사용하고 있다는 것입니다.
Pshemo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.