사용자 지정 정렬 순서를 사용하여 개체의 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);,
    • 여기서 myContactList인 ArrayList<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.