Cách so sánh các đối tượng theo nhiều trường


236

Giả sử bạn có một số đối tượng có một số trường chúng có thể được so sánh bằng:

public class Person {

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

    /* Constructors */

    /* Methods */

}

Vì vậy, trong ví dụ này, khi bạn hỏi nếu:

a.compareTo(b) > 0

bạn có thể hỏi nếu họ của họ đến trước b, hoặc nếu a cũ hơn b, v.v ...

Cách sạch nhất để cho phép so sánh nhiều giữa các loại đối tượng này mà không cần thêm sự lộn xộn hoặc chi phí không cần thiết là gì?

  • java.lang.Comparable giao diện chỉ cho phép so sánh bởi một lĩnh vực
  • Thêm nhiều phương pháp so sánh (ví dụ compareByFirstName(), compareByAge(), vv ...) được lộn xộn theo ý kiến của tôi.

Vì vậy, cách tốt nhất để đi về điều này là gì?


3
Tại sao đây là một cw? Đó là một câu hỏi lập trình hoàn toàn hợp lệ.
Elie

2
Bạn có biết rằng so sánh cho phép so sánh theo nhiều lĩnh vực như bạn muốn không?
DJClayworth

Câu trả lời:


81

Bạn có thể thực hiện một Comparatorso sánh hai Personđối tượng và bạn có thể kiểm tra bao nhiêu trường tùy thích. Bạn có thể đặt một biến trong bộ so sánh của mình để cho nó biết trường nào cần so sánh, mặc dù có thể đơn giản hơn khi chỉ viết nhiều bộ so sánh.


5
Tôi thực sự thích ý tưởng sử dụng một Công cụ so sánh duy nhất. Tôi không nghĩ câu trả lời này là sai, nhưng bất cứ ai đọc nó chắc chắn nên kiểm tra câu trả lời của Steve Kuo bên dưới.
Felipe Leão

Nhiều bộ so sánh chỉ khi bạn muốn các phương thức so sánh khác nhau không phải là chức năng của chính dữ liệu - tức là đôi khi bạn muốn so sánh theo tên, thời điểm khác theo độ tuổi, v.v. Để so sánh theo nhiều trường cùng một lúc, chỉ có một công cụ so sánh sẽ là cần thiết
Elie

399

Với Java 8:

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

Nếu bạn có phương thức truy cập:

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

Nếu một lớp thực hiện So sánh thì bộ so sánh đó có thể được sử dụng trong phương thức so sánh:

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

5
Đặc biệt là dàn diễn viên (Person p)rất quan trọng đối với các bộ so sánh xích.
viên

5
Làm thế nào hiệu quả khi so sánh một số lượng lớn các đối tượng, ví dụ như khi sắp xếp? Nó có phải tạo phiên bản mới Comparatortrong mỗi cuộc gọi không?
jjurm

4
Tôi nhận được một NullPulumException khi một trong các trường tôi đang so sánh là null, như Chuỗi. Có cách nào để giữ định dạng so sánh này nhưng cho phép nó an toàn không?
rveach

3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- bộ chọn trường đầu tiên, sau đó là bộ so sánh
gavenkoa

2
@jjurm khi bạn sử dụng nó compareTonhư được hiển thị ở trên Comparatorđược tạo mỗi khi phương thức được gọi. Bạn có thể ngăn chặn điều này bằng cách lưu trữ bộ so sánh trong trường cuối cùng tĩnh riêng.
Gandalf

165

Bạn nên thực hiện Comparable <Person>. Giả sử tất cả các trường sẽ không có giá trị (vì đơn giản), tuổi đó là một số nguyên và so sánh xếp hạng là trước, cuối cùng, tuổi, compareTophương pháp này khá đơn giản:

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
nếu bạn triển khai so sánh <Person> thì phương thức là so sánh với (Người p) .. có vẻ như câu trả lời này đã được trộn lẫn với phương pháp so sánh <T o1, T o2> của So sánh
Mike

5
Điều này không được khuyến khích. Sử dụng Trình so sánh khi bạn có nhiều trường.
indika

1
đó là giải pháp tốt nhất vào lúc này, (tốt hơn so với nhiều so sánh hơn)
Vasile Surdu

4
@indika, tôi tò mò: tại sao điều này không được khuyến khích? So sánh sử dụng nhiều hơn một tài sản có vẻ hoàn toàn tốt với tôi.
ars-longa-vita-brevis

4
@ ars-longa-vita-brevis, Nếu bạn đang sử dụng So sánh thì logic sắp xếp phải nằm trong cùng một lớp có các đối tượng đang được sắp xếp để đây được gọi là thứ tự tự nhiên của các đối tượng. Với việc sử dụng Trình so sánh, bạn có thể viết logic sắp xếp tùy chỉnh bên ngoài lớp Người. Nếu bạn muốn so sánh các đối tượng Person chỉ bằng tên hoặc họ của nó, bạn không thể sử dụng logic này. Bạn phải viết lại,
indika

78

(từ Cách sắp xếp danh sách các đối tượng trong Java dựa trên nhiều trường )

Mã làm việc trong ý chính này

Sử dụng lambda's Java 8 (đã thêm ngày 10 tháng 4 năm 2019)

Java 8 giải quyết điều này một cách độc đáo bằng lambda (mặc dù Guava và Apache Commons vẫn có thể cung cấp sự linh hoạt hơn):

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

Cảm ơn câu trả lời của @ gaoagong bên dưới .

Lộn xộn và hỗn độn: Sắp xếp bằng tay

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);  
    }  
});  

Điều này đòi hỏi rất nhiều gõ, bảo trì và dễ bị lỗi.

Cách phản chiếu: Sắp xếp với BeanComparator

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

Collections.sort(pizzas, chain);  

Rõ ràng điều này ngắn gọn hơn, nhưng thậm chí còn dễ bị lỗi hơn khi bạn mất tham chiếu trực tiếp đến các trường bằng cách sử dụng Chuỗi thay thế (không an toàn kiểu, tự động tái cấu trúc). Bây giờ nếu một trường được đổi tên, trình biên dịch thậm chí sẽ không báo cáo vấn đề. Hơn nữa, vì giải pháp này sử dụng sự phản chiếu, việc sắp xếp chậm hơn nhiều.

Đến đó: Sắp xếp với So sánh của Google Guava

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(); 
        */  
    }  
});  

Điều này tốt hơn nhiều, nhưng yêu cầu một số mã tấm nồi hơi cho trường hợp sử dụng phổ biến nhất: giá trị null nên được định giá ít hơn theo mặc định. Đối với các trường null, bạn phải cung cấp thêm một lệnh cho Guava phải làm gì trong trường hợp đó. Đây là một cơ chế linh hoạt nếu bạn muốn làm một cái gì đó cụ thể, nhưng thường thì bạn muốn trường hợp mặc định (ví dụ: 1, a, b, z, null).

Sắp xếp với Apache Commons So sánhToBuilder

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();  
    }  
});  

Giống như so sánh của Guava, lớp thư viện này sắp xếp dễ dàng trên nhiều trường, nhưng cũng xác định hành vi mặc định cho các giá trị null (ví dụ: 1, a, b, z, null). Tuy nhiên, bạn cũng không thể chỉ định bất cứ điều gì khác, trừ khi bạn cung cấp Trình so sánh của riêng bạn.

Như vậy

Cuối cùng, điều này dẫn đến hương vị và nhu cầu linh hoạt (so sánh của Guava) so với mã ngắn gọn (so sánh với Apache của Apache).

Phương thức thưởng

Tôi đã tìm thấy một giải pháp hay, kết hợp nhiều bộ so sánh theo thứ tự ưu tiên trên CodeReview theo 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));
    }
}

Tất nhiên Bộ sưu tập Apache Commons đã được sử dụng cho việc này:

Công cụ so sánhUtils.chainedComparator (công cụ so sánh)

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

22

@Patrick Để sắp xếp nhiều hơn một lĩnh vực liên tục thử so sánh

Một so sánhChain là một so sánh bao bọc một hoặc nhiều so sánh theo trình tự. Trình so sánh gọi mỗi Trình so sánh theo trình tự cho đến khi 1) bất kỳ Trình so sánh đơn lẻ nào trả về kết quả khác không (và kết quả đó được trả về) hoặc 2) Công cụ so sánh bị cạn kiệt (và trả về 0). Kiểu sắp xếp này rất giống với sắp xếp nhiều cột trong SQL và lớp này cho phép các lớp Java mô phỏng loại hành vi đó khi sắp xếp Danh sách.

Để tiếp tục tạo điều kiện cho việc sắp xếp giống như SQL, thứ tự của bất kỳ Trình so sánh đơn lẻ nào trong danh sách có thể được đảo ngược.

Gọi một phương thức có thêm Trình so sánh mới hoặc thay đổi sắp xếp tăng / giảm sau khi so sánh (Đối tượng, Đối tượng) đã được gọi sẽ dẫn đến một UnceptionedOperationException. Tuy nhiên, hãy cẩn thận để không thay đổi Danh sách so sánh cơ bản hoặc Bitset xác định thứ tự sắp xếp.

Trường hợp của so sánhChain không được đồng bộ hóa. Lớp này không an toàn cho luồng khi xây dựng, nhưng nó an toàn cho luồng để thực hiện nhiều so sánh sau khi tất cả các hoạt động thiết lập hoàn tất.


20

Một tùy chọn khác mà bạn luôn có thể xem xét là Apache Commons. Nó cung cấp rất nhiều lựa chọn.

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

Ví dụ:

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
Tôi thích chiến lược này rất nhiều. Cảm ơn!
Ông Polywhirl

Cách hiệu quả nhất! Cảm ơn
Zakaria Bouazza

8

Đối với những người có thể sử dụng API phát trực tuyến Java 8, có một cách tiếp cận gọn gàng hơn được ghi lại ở đây: Lambdas và sắp xếp

Tôi đang tìm kiếm tương đương với C # LINQ:

.ThenBy(...)

Tôi đã tìm thấy cơ chế trong Java 8 trên Trình so sánh:

.thenComparing(...)

Vì vậy, đây là đoạn trích thể hiện thuật toán.

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

Kiểm tra liên kết ở trên để biết cách gọn gàng hơn và giải thích về cách suy luận kiểu Java làm cho nó khó hiểu hơn một chút so với LINQ.

Dưới đây là bài kiểm tra đơn vị đầy đủ để tham khảo:

@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

Viết một Comparatorcách thủ công cho trường hợp sử dụng như vậy là một giải pháp khủng khiếp IMO. Cách tiếp cận ad hoc như vậy có nhiều nhược điểm:

  • Không sử dụng lại mã. Vi phạm KHÔ.
  • Bản mẫu.
  • Tăng khả năng xảy ra lỗi.

Vậy giải pháp là gì?

Đầu tiên một số lý thuyết.

Hãy để chúng tôi biểu thị các đề xuất "loại Ahỗ trợ so sánh" bằng Ord A. (Từ góc độ chương trình, bạn có thể nghĩ về Ord Amột đối tượng chứa logic để so sánh hai As. Vâng, giống như Comparator.)

Bây giờ, nếu Ord AOrd B, sau đó tổng hợp của họ (A, B)cũng nên hỗ trợ so sánh. tức Ord (A, B). Nếu Ord A, Ord BOrd C, sau đó Ord (A, B, C).

Chúng ta có thể mở rộng đối số này thành tính độc đoán và nói:

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

Hãy gọi câu này là 1.

Việc so sánh các vật liệu tổng hợp sẽ hoạt động giống như bạn mô tả trong câu hỏi của bạn: so sánh đầu tiên sẽ được thử trước, sau đó là tiếp theo, sau đó tiếp theo, v.v.

Đó là phần đầu tiên trong giải pháp của chúng tôi. Bây giờ là phần thứ hai.

Nếu bạn biết điều đó Ord Avà biết cách chuyển đổi Bthành A(gọi hàm biến đổi đó f), thì bạn cũng có thể có Ord B. Làm sao? Chà, khi hai Btrường hợp được so sánh, trước tiên bạn chuyển đổi chúng thành Asử dụng fvà sau đó áp dụng Ord A.

Ở đây, chúng tôi đang ánh xạ chuyển đổi B → Asang Ord A → Ord B. Điều này được gọi là ánh xạ chống chỉ định (hoặc comapviết tắt).

Ord A, (B → A)COMAP Ord B

Hãy gọi câu này là 2.


Bây giờ hãy áp dụng điều này vào ví dụ của bạn.

Bạn có một loại dữ liệu được đặt tên Personbao gồm ba trường loại String.

  • Chúng tôi biết rằng Ord String. Bằng câu 1 , Ord (String, String, String).

  • Chúng ta có thể dễ dàng viết một hàm từ Personđến (String, String, String). (Chỉ cần trả về ba trường.) Vì chúng ta biết Ord (String, String, String)Person → (String, String, String), bằng câu 2, chúng ta có thể sử dụng comapđể lấy Ord Person.

QED.


Làm thế nào để tôi thực hiện tất cả các khái niệm này?

Tin tốt là bạn không cần phải làm vậy. Hiện đã có một thư viện thực hiện tất cả các ý tưởng được mô tả trong bài viết này. (Nếu bạn tò mò về cách thức thực hiện chúng, bạn có thể nhìn vào bên dưới mui xe .)

Đây là cách mã sẽ nhìn với nó:

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());
     }
   }
 );

Giải trình:

  • stringOrdlà một đối tượng của loại Ord<String>. Điều này tương ứng với đề xuất "hỗ trợ so sánh" ban đầu của chúng tôi.
  • p3Ordlà một phương pháp mà có Ord<A>, Ord<B>, Ord<C>, và lợi nhuận Ord<P3<A, B, C>>. Điều này tương ứng với câu 1. ( P3viết tắt của sản phẩm có ba yếu tố. Sản phẩm là thuật ngữ đại số cho vật liệu tổng hợp.)
  • comaptương ứng với tốt , comap.
  • F<A, B>đại diện cho một hàm biến đổi A → B.
  • p là một phương pháp nhà máy để tạo ra sản phẩm.
  • Toàn bộ biểu thức tương ứng với câu 2.

Mong rằng sẽ giúp.


5

Thay vì các phương thức so sánh, bạn có thể chỉ muốn xác định một số loại lớp con "Trình so sánh" bên trong lớp Người. Bằng cách đó bạn có thể chuyển chúng vào các phương pháp sắp xếp Bộ sưu tập tiêu chuẩn.


3

Tôi nghĩ sẽ khó hiểu hơn nếu thuật toán so sánh của bạn là "thông minh". Tôi sẽ đi với nhiều phương pháp so sánh mà bạn đề xuất.

Ngoại lệ duy nhất đối với tôi sẽ là bình đẳng. Đối với thử nghiệm đơn vị, đối với tôi, việc ghi đè .Equals (in .net) là rất hữu ích để xác định xem một số trường có bằng nhau giữa hai đối tượng không (và các tham chiếu có bằng nhau không).


3

Nếu có nhiều cách người dùng có thể đặt hàng người, bạn cũng có thể có nhiều thiết lập Bộ so sánh như các hằng số ở đâu đó. Hầu hết các hoạt động sắp xếp và các bộ sưu tập được sắp xếp đều lấy một bộ so sánh làm tham số.


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

Mã thực hiện tương tự ở đây nếu chúng ta phải sắp xếp đối tượng Person dựa trên nhiều trường.

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);

Tôi đã đến câu hỏi này bởi vì mã của tôi bắt đầu thích câu trả lời của bạn;)
jan groth

0

Nếu bạn triển khai giao diện So sánh , bạn sẽ muốn chọn một thuộc tính đơn giản để đặt hàng theo. Điều này được gọi là trật tự tự nhiên. Hãy nghĩ về nó như mặc định. Nó luôn được sử dụng khi không có bộ so sánh cụ thể nào được cung cấp. Thông thường đây là tên, nhưng trường hợp sử dụng của bạn có thể gọi cho một cái gì đó khác nhau. Bạn có thể tự do sử dụng bất kỳ số lượng Bộ so sánh nào khác mà bạn có thể cung cấp cho các API bộ sưu tập khác nhau để ghi đè thứ tự tự nhiên.

Cũng lưu ý rằng thông thường nếu a.compareTo (b) == 0, thì a.equals (b) == true. Không sao nếu không nhưng có những tác dụng phụ cần phải biết. Xem các javadocs tuyệt vời trên giao diện So sánh và bạn sẽ tìm thấy nhiều thông tin tuyệt vời về điều này.


0

Theo dõi blog đưa ra ví dụ so sánh chuỗi tốt

http://www.codejava.net/java-core/collections/sorting-a-list-by-multipl-attribut-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;
    }
}

Gọi so sánh:

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

0

Bắt đầu từ câu trả lời của Steve, toán tử ternary có thể được sử dụng:

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

Thật dễ dàng để so sánh hai đối tượng với phương thức mã băm trong java`

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()));

}

Xin đừng làm vậy. Mã băm không nên được sử dụng để so sánh bằng nhưng để lập chỉ mục băm. Va chạm băm có thể dẫn đến sự bình đẳng cho hai đối tượng khác nhau. Ngay cả các hashtag cũng dựa vào sự bình đẳng thực sự nếu xảy ra va chạm băm.
Noel Widmer

0

Thông thường tôi ghi đè compareTo()phương thức của mình như thế này bất cứ khi nào tôi phải thực hiện sắp xếp đa cấp độ.

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;
} 

Ở đây ưu tiên đầu tiên được trao cho tên phim sau đó cho nghệ sĩ và cuối cùng là bài hát. Bạn chỉ cần đảm bảo rằng các số nhân đó đủ xa để không vượt qua ranh giới của nhau.


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.