Cách sắp xếp Danh sách các đối tượng theo một số thuộc tính


144

Tôi có lớp học đơn giản

public class ActiveAlarm {
    public long timeStarted;
    public long timeEnded;
    private String name = "";
    private String description = "";
    private String event;
    private boolean live = false;
}

List<ActiveAlarm>con. Làm thế nào để sắp xếp theo thứ tự tăng dần theo timeStarted, sau đó bởi timeEnded? Ai có thể giúp đỡ? Tôi biết trong C ++ với thuật toán chung và toán tử quá tải <, nhưng tôi chưa quen với Java.


Trả lời bởi @Yishai trong bài đăng này cho thấy việc sử dụng enum tao nhã để sắp xếp tùy chỉnh và sắp xếp theo nhóm (nhiều đối số) sử dụng chuỗi so sánh.
súng

Câu trả lời:


140

Hoặc thực ActiveAlarmhiện Comparable<ActiveAlarm>hoặc thực hiện Comparator<ActiveAlarm>trong một lớp riêng biệt. Sau đó gọi:

Collections.sort(list);

hoặc là

Collections.sort(list, comparator);

Nói chung, nên thực hiện Comparable<T>nếu có một thứ tự sắp xếp "tự nhiên" duy nhất ... nếu không (nếu bạn tình cờ muốn sắp xếp theo một thứ tự cụ thể, nhưng có thể dễ dàng muốn một thứ khác) thì tốt hơn nên thực hiện Comparator<T>. Thành thật mà nói, tình huống này có thể xảy ra, nhưng có lẽ tôi sẽ gắn bó với Comparator<T>tùy chọn linh hoạt hơn .

EDIT: Thực hiện mẫu:

public class AlarmByTimesComparer implements Comparator<ActiveAlarm> {
  @Override
  public int compare(ActiveAlarm x, ActiveAlarm y) {
    // TODO: Handle null x or y values
    int startComparison = compare(x.timeStarted, y.timeStarted);
    return startComparison != 0 ? startComparison
                                : compare(x.timeEnded, y.timeEnded);
  }

  // I don't know why this isn't in Long...
  private static int compare(long a, long b) {
    return a < b ? -1
         : a > b ? 1
         : 0;
  }
}

1
Hàm so sánh () đó không có khả năng dài bởi vì việc triển khai thậm chí còn tầm thường hơn: return a - b;
vụn giấy

5
@aperscrane: Không, thất bại vì lý do tràn. Hãy xem xét a = Long.MIN_VALUE, b = 1.
Jon Skeet

3
kể từ API 19 (KitKat) Lâu nay có một.compare
Martin Marconcini

123

Sử dụng Comparator

Ví dụ:

class Score {

    private String name;
    private List<Integer> scores;
    // +accessor methods
}

    Collections.sort(scores, new Comparator<Score>() {

        public int compare(Score o1, Score o2) {
            // compare two instance of `Score` and return `int` as result.
            return o2.getScores().get(0).compareTo(o1.getScores().get(0));
        }
    });

Với Java 8 trở đi, bạn chỉ cần sử dụng biểu thức lambda để thể hiện cá thể so sánh.

Collections.sort(scores, (s1, s2) -> { /* compute and return int */ });

1
Không gì compareTo()làm gì? Nó đến từ đâu? Tôi phải xác định nó ở đâu?
Asqiir

1
@Asqiir getScores()là getter cho scoresa List<Integer>. Khi bạn làm getScores().get(0)bạn có được một Integerđối tượng. Integerđã có compareTo(anotherInteger)phương thức triển khai, bạn không phải xác định nó.
walen

nhận được (0) là gì?
Ali Khaki

1
Cảm ơn hoạt động như một bùa mê (tôi sử dụng nó mà không có phần .get (0)). Làm thế nào tôi có thể đảo ngược thứ tự?

51

JAVA 8 trở lên Trả lời (Sử dụng Biểu thức Lambda)

Trong Java 8, các biểu thức Lambda đã được giới thiệu để làm cho điều này thậm chí còn dễ dàng hơn! Thay vì tạo một đối tượng so sánh () với tất cả các giàn giáo của nó, bạn có thể đơn giản hóa nó như sau: (Sử dụng đối tượng của bạn làm ví dụ)

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted);

hoặc thậm chí ngắn hơn:

Collections.sort(list, Comparator.comparingInt(ActiveAlarm ::getterMethod));

Đó là một tuyên bố tương đương như sau:

Collections.sort(list, new Comparator<ActiveAlarm>() {
    @Override
    public int compare(ActiveAlarm a1, ActiveAlarm a2) {
        return a1.timeStarted - a2.timeStarted;
    }
});

Hãy nghĩ về các biểu thức Lambda như chỉ yêu cầu bạn đưa vào các phần có liên quan của mã: chữ ký phương thức và những gì được trả về.

Một phần khác của câu hỏi của bạn là làm thế nào để so sánh với nhiều lĩnh vực. Để làm điều đó với các biểu thức Lambda, bạn có thể sử dụng .thenComparing()hàm để kết hợp hiệu quả hai phép so sánh thành một:

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted             
       .thenComparing ((ActiveAlarm a1, ActiveAlarm a2) -> a1.timeEnded-a2.timeEnded)
);

Đoạn mã trên sẽ sắp xếp danh sách trước timeStarted, sau đó theo timeEnded(đối với những bản ghi có cùng timeStarted).

Một lưu ý cuối cùng: Thật dễ dàng để so sánh các nguyên thủy 'dài' hoặc 'int', bạn chỉ có thể trừ đi cái khác. Nếu bạn đang so sánh các đối tượng ('Dài' hoặc 'Chuỗi'), tôi khuyên bạn nên sử dụng so sánh tích hợp của chúng. Thí dụ:

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.name.compareTo(a2.name) );

EDIT: Cảm ơn Lukas Eder đã chỉ cho tôi .thenComparing()hoạt động.


4
Bạn có thể đánh giá cao API Java 8 mới Comparator.comparing().thenComparing()...
Lukas Eder

1
Xem ra với sự khác biệt trở lại. Trong trường hợp tràn, bạn có thể nhận được kết quả sai.
krzychu

2
Đây là IMHO phương pháp sắp xếp dễ dàng hơn, một lần nữa giả sử bạn không có một trật tự tự nhiên rõ ràng. Bạn cũng không cần phải gọi Collectionsnữa, bạn có thể gọi trực tiếp vào danh sách. Ví dụ:myList.sort(Comparator.comparing(Address::getZipCode).thenComparing(Compartor.comparing(Address::getStreetName));
CeePlusPlus

20

Chúng tôi có thể sắp xếp danh sách theo một trong hai cách:

1. Sử dụng Trình so sánh : Khi được yêu cầu sử dụng logic sắp xếp ở nhiều nơi Nếu bạn muốn sử dụng logic sắp xếp ở một nơi duy nhất, thì bạn có thể viết một lớp bên trong ẩn danh như sau hoặc nếu không thì trích xuất trình so sánh và sử dụng nó ở nhiều nơi

  Collections.sort(arrayList, new Comparator<ActiveAlarm>() {
        public int compare(ActiveAlarm o1, ActiveAlarm o2) {
            //Sorts by 'TimeStarted' property
            return o1.getTimeStarted()<o2.getTimeStarted()?-1:o1.getTimeStarted()>o2.getTimeStarted()?1:doSecodaryOrderSort(o1,o2);
        }

        //If 'TimeStarted' property is equal sorts by 'TimeEnded' property
        public int doSecodaryOrderSort(ActiveAlarm o1,ActiveAlarm o2) {
            return o1.getTimeEnded()<o2.getTimeEnded()?-1:o1.getTimeEnded()>o2.getTimeEnded()?1:0;
        }
    });

Chúng tôi có thể kiểm tra null cho các thuộc tính, nếu chúng tôi có thể đã sử dụng 'Long' thay vì 'long'.

2. Sử dụng so sánh (thứ tự tự nhiên) : Nếu thuật toán sắp xếp luôn bám vào một thuộc tính: hãy viết một lớp thực hiện phương thức 'So sánh' và ghi đè 'so sánh' như được định nghĩa dưới đây

class ActiveAlarm implements Comparable<ActiveAlarm>{

public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;

public ActiveAlarm(long timeStarted,long timeEnded) {
    this.timeStarted=timeStarted;
    this.timeEnded=timeEnded;
}

public long getTimeStarted() {
    return timeStarted;
}

public long getTimeEnded() {
    return timeEnded;
}

public int compareTo(ActiveAlarm o) {
    return timeStarted<o.getTimeStarted()?-1:timeStarted>o.getTimeStarted()?1:doSecodaryOrderSort(o);
}

public int doSecodaryOrderSort(ActiveAlarm o) {
    return timeEnded<o.getTimeEnded()?-1:timeEnded>o.getTimeEnded()?1:0;
}

}

gọi phương thức sắp xếp để sắp xếp dựa trên thứ tự tự nhiên

Collections.sort(list);

7

Trong java8 +, điều này có thể được viết thành một dòng như sau:

collectionObjec.sort(comparator_lamda) hoặc là comparator.comparing(CollectionType::getterOfProperty)

mã:

ListOfActiveAlarmObj.sort((a,b->a.getTimeStarted().compareTo(b.getTimeStarted())))
 

hoặc là

ListOfActiveAlarmObj.sort(Comparator.comparing(ActiveAlarm::getTimeStarted))

5
public class ActiveAlarm implements Comparable<ActiveAlarm> {
    public long timeStarted;
    public long timeEnded;
    private String name = "";
    private String description = "";
    private String event;
    private boolean live = false;

    public int compareTo(ActiveAlarm a) {
        if ( this.timeStarted > a.timeStarted )
            return 1;
        else if ( this.timeStarted < a.timeStarted )
            return -1;
        else {
             if ( this.timeEnded > a.timeEnded )
                 return 1;
             else
                 return -1;
        }
 }

Điều đó sẽ cung cấp cho bạn một ý tưởng sơ bộ. Khi đã xong, bạn có thể gọi Collections.sort()vào danh sách.


4

Kể từ Java8, điều này có thể được thực hiện thậm chí sạch hơn bằng cách sử dụng kết hợp ComparatorLambda expressions

Ví dụ:

class Student{

    private String name;
    private List<Score> scores;

    // +accessor methods
}

class Score {

    private int grade;
    // +accessor methods
}

    Collections.sort(student.getScores(), Comparator.comparing(Score::getGrade);

2

So sánh của ổi :

Collections.sort(list, new Comparator<ActiveAlarm>(){
            @Override
            public int compare(ActiveAlarm a1, ActiveAlarm a2) {
                 return ComparisonChain.start()
                       .compare(a1.timestarted, a2.timestarted)
                       //...
                       .compare(a1.timeEnded, a1.timeEnded).result();
            }});


1

Trong java bạn cần sử dụng Collections.sortphương thức tĩnh . Dưới đây là một ví dụ cho danh sách các đối tượng CompanyRole, được sắp xếp trước theo đầu và sau đó là cuối. Bạn có thể dễ dàng thích nghi cho đối tượng của riêng bạn.

private static void order(List<TextComponent> roles) {

    Collections.sort(roles, new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            int x1 = ((CompanyRole) o1).getBegin();
            int x2 = ((CompanyRole) o2).getBegin();

            if (x1 != x2) {
                return x1 - x2;
            } else {
                int y1 = ((CompanyRole) o1).getEnd();
                int y2 = ((CompanyRole) o2).getEnd();
                return y2 - y1;
            }
        }
    });
}

0

Bạn có thể gọi Collections.sort () và chuyển vào Bộ so sánh mà bạn cần viết để so sánh các thuộc tính khác nhau của đối tượng.


0

Như đã đề cập, bạn có thể sắp xếp theo:

  • Làm cho đối tượng của bạn thực hiện Comparable
  • Hoặc vượt qua một ComparatorđếnCollections.sort

Nếu bạn làm cả hai, Comparablesẽ bị bỏ qua và Comparatorsẽ được sử dụng. Điều này giúp các đối tượng giá trị có logic riêng Comparable, sắp xếp hợp lý nhất cho đối tượng giá trị của bạn, trong khi mỗi trường hợp sử dụng riêng lẻ có cách triển khai riêng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.