Cara membandingkan objek dengan beberapa bidang

237

Asumsikan Anda memiliki beberapa objek yang memiliki beberapa bidang yang dapat dibandingkan dengan:

public class Person {

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

    /* Constructors */

    /* Methods */

}

Jadi dalam contoh ini, ketika Anda bertanya apakah:

a.compareTo(b) > 0

Anda mungkin bertanya apakah nama belakang a datang sebelum b, atau jika a lebih tua dari b, dll ...

Apa cara paling bersih untuk memungkinkan beberapa perbandingan antara objek-objek semacam ini tanpa menambahkan kekacauan atau overhead yang tidak perlu?

  • java.lang.Comparable antarmuka memungkinkan perbandingan dengan satu bidang saja
  • Menambahkan berbagai metode membandingkan (yaitu compareByFirstName(), compareByAge(), dll ...) adalah berantakan menurut saya.

Jadi apa cara terbaik untuk melakukan ini?

Yuval Adam
sumber
3
mengapa ini cw? Ini pertanyaan pemrograman yang sangat valid.
Elie
2
Apakah Anda sadar bahwa Sebanding memungkinkan perbandingan dengan bidang sebanyak yang Anda suka?
DJClayworth

Jawaban:

81

Anda bisa mengimplementasikan Comparatoryang membandingkan dua Personobjek, dan Anda bisa memeriksa sebanyak mungkin bidang yang Anda inginkan. Anda bisa memasukkan variabel ke komparator Anda yang memberitahu bidang mana yang harus dibandingkan, meskipun mungkin lebih mudah untuk hanya menulis beberapa komparator.

Elie
sumber
5
Sebenarnya saya lebih suka ide menggunakan pembanding tunggal. Saya tidak berpikir jawaban ini salah, tetapi siapa pun yang membacanya pasti harus memeriksa jawaban Steve Kuo di bawah ini.
Felipe Leão
Beberapa komparator hanya jika Anda menginginkan metode perbandingan berbeda yang bukan merupakan fungsi dari data itu sendiri - yaitu kadang-kadang Anda ingin membandingkan berdasarkan nama, waktu lain berdasarkan usia, dll. Untuk membandingkan dengan beberapa bidang sekaligus, hanya satu komparator akan diperlukan.
Elie
399

Dengan Java 8:

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

Jika Anda memiliki metode accessor:

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

Jika sebuah kelas mengimplementasikan Comparable maka pembanding tersebut dapat digunakan dalam metode compareTo:

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}
Enigo
sumber
5
Terutama para pemain (Person p)penting untuk pembanding dirantai.
membersound
5
Seberapa efisien ketika membandingkan sejumlah besar objek, misalnya saat menyortir? Apakah harus membuat Comparatorinstance baru di setiap panggilan?
jjurm
4
Saya mendapatkan NullPointerException ketika salah satu bidang yang saya bandingkan adalah nol, seperti sebuah String. Apakah ada cara untuk menyimpan format perbandingan ini tetapi membiarkannya menjadi nol aman?
rveach
3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- pemilih bidang pertama, lalu pembanding
gavenkoa
2
@jjurm ketika Anda menggunakannya compareToseperti yang ditunjukkan di atas Comparatordibuat setiap kali metode dipanggil. Anda bisa mencegah ini dengan menyimpan komparator dalam bidang final statis pribadi.
Gandalf
165

Anda harus menerapkan Comparable <Person>. Dengan asumsi semua bidang tidak akan menjadi nol (untuk kesederhanaan), usia itu adalah int, dan membandingkan peringkat adalah pertama, terakhir, usia, compareTometode ini cukup sederhana:

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);
}
Steve Kuo
sumber
10
jika Anda mengimplementasikan Comparable <Person> maka metodenya adalah compareTo (Person p) .. sepertinya jawaban ini tercampur dengan metode komparator bandingkan <T o1, T o2>
Mike
5
Ini tidak disarankan. Gunakan Pembanding ketika Anda memiliki beberapa bidang.
indika
1
itulah solusi terbaik saat ini, (lebih baik daripada lebih banyak pembanding)
Vasile Surdu
4
@indika, saya ingin tahu: mengapa ini tidak disarankan? Membandingkan menggunakan lebih dari satu properti tampaknya baik-baik saja bagi saya.
ars-longa-vita-brevis
4
@ ars-longa-vita-brevis, Jika Anda menggunakan Sebanding maka pengurutan logika harus berada di kelas yang sama yang objeknya sedang diurutkan sehingga ini disebut pengurutan objek secara alami. Dengan menggunakan Comparator Anda dapat menulis logika penyortiran khusus di luar kelas Person. Jika Anda ingin membandingkan objek Orang hanya dengan nama depan atau nama belakangnya, Anda tidak dapat menggunakan logika ini. Anda harus menulisnya lagi,
indika
78

(dari Cara untuk mengurutkan daftar objek di Jawa berdasarkan beberapa bidang )

Kode yang digunakan dalam inti ini

Menggunakan Java 8 lambda's (ditambahkan 10 April 2019)

Java 8 menyelesaikan ini dengan baik oleh lambda (meskipun Guava dan Apache Commons mungkin masih menawarkan lebih banyak fleksibilitas):

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

Terima kasih atas jawaban @ gaoagong di bawah ini .

Berantakan dan berbelit-belit: Menyortir dengan tangan

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

Ini membutuhkan banyak pengetikan, pemeliharaan, dan rawan kesalahan.

Cara reflektif: Menyortir dengan BeanComparator

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

Collections.sort(pizzas, chain);  

Jelas ini lebih ringkas, tetapi bahkan lebih rentan kesalahan saat Anda kehilangan referensi langsung ke bidang dengan menggunakan Strings sebagai gantinya (tidak ada typesafety, auto refactoring). Sekarang jika bidang diubah namanya, kompiler bahkan tidak akan melaporkan masalah. Selain itu, karena solusi ini menggunakan refleksi, penyortiran jauh lebih lambat.

Cara ke sana: Menyortir dengan ComparisonChain 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(); 
        */  
    }  
});  

Ini jauh lebih baik, tetapi membutuhkan beberapa kode pelat ketel untuk kasus penggunaan paling umum: nilai null harus dinilai lebih rendah secara default. Untuk bidang-nol, Anda harus memberikan arahan ekstra untuk Jambu apa yang harus dilakukan dalam kasus itu. Ini adalah mekanisme yang fleksibel jika Anda ingin melakukan sesuatu yang spesifik, tetapi sering kali Anda menginginkan case default (mis. 1, a, b, z, null).

Mengurutkan dengan Apache Commons CompareToBuilder

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

Seperti Guava's ComparisonChain, kelas pustaka ini mengurutkan dengan mudah pada beberapa bidang, tetapi juga mendefinisikan perilaku default untuk nilai-nilai nol (mis. 1, a, b, z, null). Namun, Anda tidak dapat menentukan hal lain juga, kecuali jika Anda menyediakan Pembanding Anda sendiri.

Jadi

Pada akhirnya itu turun ke rasa dan kebutuhan akan fleksibilitas (Guava's ComparisonChain) vs kode ringkas (Apache's CompareToBuilder).

Metode bonus

Saya menemukan solusi yang bagus yang menggabungkan beberapa komparator dengan urutan prioritas pada CodeReview di 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));
    }
}

Koleksi Apache Commons Ofcourse sudah memiliki kegunaan untuk ini:

ComparatorUtils.chainedComparator (comparatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Benny Bottema
sumber
22

@ Patrick Untuk mengurutkan lebih dari satu bidang secara berurutan, coba ComparatorChain

ComparatorChain adalah Comparator yang membungkus satu atau lebih Comparator secara berurutan. ComparatorChain memanggil masing-masing Comparator secara berurutan sampai 1) Comparator tunggal mengembalikan hasil yang tidak nol (dan hasilnya kemudian dikembalikan), atau 2) ComparatorChain habis (dan nol dikembalikan). Jenis penyortiran ini sangat mirip dengan penyortiran multi-kolom dalam SQL, dan kelas ini memungkinkan kelas Java untuk meniru perilaku semacam itu saat menyortir Daftar.

Untuk lebih memudahkan penyortiran seperti SQL, urutan Pembanding tunggal dalam daftar dapat dibalik.

Memanggil metode yang menambahkan Comparator baru atau mengubah sort ascend / descend setelah dibandingkan (Object, Object) telah dipanggil akan menghasilkan UnsupportedOperationException. Namun, berhati-hatilah untuk tidak mengubah Daftar Pembanding atau BitSet yang mendasari yang menentukan urutan pengurutan.

Contoh dari ComparatorChain tidak disinkronkan. Kelas tidak aman untuk thread pada waktu konstruksi, tetapi aman untuk melakukan beberapa perbandingan setelah semua operasi pengaturan selesai.

Nigel_V_Thomas
sumber
20

Opsi lain yang selalu bisa Anda pertimbangkan adalah Apache Commons. Ini menyediakan banyak pilihan.

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

Ex:

public int compare(Person a, Person b){

   return new CompareToBuilder()
     .append(a.getName(), b.getName())
     .append(a.getAddress(), b.getAddress())
     .toComparison();
}
Xeroiris
sumber
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();
    }

}
Ran Adler
sumber
1
Saya sangat suka strategi ini. Terima kasih!
Tn. Polywhirl
Cara paling efektif! Terima kasih
Zakaria Bouazza
8

Bagi mereka yang dapat menggunakan Java 8 streaming API, ada pendekatan yang lebih rapi yang didokumentasikan dengan baik di sini: Lambdas dan pengurutan

Saya mencari yang setara dengan C # LINQ:

.ThenBy(...)

Saya menemukan mekanisme di Java 8 di Comparator:

.thenComparing(...)

Jadi di sini adalah potongan yang menunjukkan algoritme.

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

Lihatlah tautan di atas untuk cara yang lebih rapi dan penjelasan tentang bagaimana inferensi tipe Java membuatnya sedikit lebih kikuk untuk didefinisikan dibandingkan dengan LINQ.

Berikut ini adalah unit test lengkap untuk referensi:

@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);
    }
}
Luke Machowski
sumber
7

Menulis secara Comparatormanual untuk use case seperti itu adalah solusi IMO yang mengerikan. Pendekatan ad hoc semacam itu memiliki banyak kelemahan:

  • Tidak ada kode yang digunakan kembali. Melanggar KERING.
  • Pelat boiler.
  • Peningkatan kemungkinan kesalahan.

Jadi apa solusinya?

Pertama beberapa teori.

Mari kita menyatakan proposisi "ketik Amendukung perbandingan" oleh Ord A. (Dari perspektif program, Anda dapat menganggap Ord Asebagai objek yang berisi logika untuk membandingkan dua A. Ya, sama seperti Comparator.)

Sekarang, jika Ord Adan Ord B, maka komposit mereka (A, B)juga harus mendukung perbandingan. yaitu Ord (A, B). Jika Ord A,, Ord Bdan Ord C, lalu Ord (A, B, C).

Kami dapat memperluas argumen ini ke arity sewenang-wenang, dan mengatakan:

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

Sebut pernyataan ini 1.

Perbandingan komposit akan berfungsi seperti yang Anda jelaskan dalam pertanyaan Anda: perbandingan pertama akan dicoba terlebih dahulu, kemudian yang berikutnya, kemudian yang berikutnya, dan seterusnya.

Itu bagian pertama dari solusi kami. Sekarang bagian kedua.

Jika Anda tahu bahwa Ord A, dan tahu bagaimana mengubah Bke A(panggilan itu fungsi transformasi f), maka Anda juga dapat memiliki Ord B. Bagaimana? Nah, ketika dua Bcontoh tersebut akan dibandingkan, Anda pertama-tama mengubahnya untuk Amenggunakan fdan kemudian menerapkanOrd A .

Di sini, kami memetakan transformasi B → Ake Ord A → Ord B. Ini dikenal sebagai pemetaan contravarian (atau comapsingkatnya).

Ord A, (B → A)comap Ord B

Sebut saja pernyataan ini 2.


Sekarang mari kita terapkan ini pada contoh Anda.

Anda memiliki tipe data yang bernama Personyang terdiri dari tiga bidang tipe String.

  • Kami tahu itu Ord String. Dengan pernyataan 1 Ord (String, String, String),.

  • Kita dapat dengan mudah menulis fungsi dari Personke (String, String, String). (Cukup kembalikan tiga bidang.) Karena kita tahu Ord (String, String, String)dan Person → (String, String, String), dengan pernyataan 2, kita bisa gunakan comapuntuk mendapatkannya Ord Person.

QED.


Bagaimana saya menerapkan semua konsep ini?

Berita baiknya adalah Anda tidak harus melakukannya. Sudah ada perpustakaan yang mengimplementasikan semua ide yang dijelaskan dalam posting ini. (Jika Anda ingin tahu bagaimana ini diterapkan, Anda dapat melihat di bawah tenda .)

Ini adalah bagaimana kode akan terlihat dengannya:

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

Penjelasan:

  • stringOrdadalah objek bertipe Ord<String>. Ini sesuai dengan proposisi "mendukung perbandingan" asli kami.
  • p3Ordadalah metode yang mengambil Ord<A>, Ord<B>, Ord<C>, dan kembali Ord<P3<A, B, C>>. Ini sesuai dengan pernyataan 1. ( P3singkatan dari produk dengan tiga elemen. Produk adalah istilah aljabar untuk komposit.)
  • comapberkorespondensi dengan baik comap,.
  • F<A, B>mewakili fungsi transformasi A → B.
  • p adalah metode pabrik untuk membuat produk.
  • Seluruh ekspresi berhubungan dengan pernyataan 2.

Semoga itu bisa membantu.

missingfaktor
sumber
5

Alih-alih metode perbandingan, Anda mungkin ingin mendefinisikan beberapa jenis subkelas "Pembanding" di dalam kelas Person. Dengan begitu Anda bisa meneruskannya ke metode pengurutan Koleksi standar.

Marc Novakowski
sumber
3

Saya pikir akan lebih membingungkan jika algoritma perbandingan Anda "pintar". Saya akan menggunakan banyak metode perbandingan yang Anda sarankan.

Satu-satunya pengecualian bagi saya adalah kesetaraan. Untuk pengujian unit, ini berguna bagi saya untuk menimpa .Equals (dalam .net) untuk menentukan apakah beberapa bidang sama antara dua objek (dan bukan bahwa referensi sama).

Michael Haren
sumber
3

Jika ada beberapa cara pengguna dapat memesan orang, Anda juga bisa memiliki beberapa pengaturan Pembanding sebagai konstanta di suatu tempat. Sebagian besar operasi pengurutan dan koleksi yang diurutkan menggunakan pembanding sebagai parameter.

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

    }

}
Pawan
sumber
3

Implementasi kode yang sama ada di sini jika kita harus mengurutkan objek Person berdasarkan beberapa bidang.

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

}
Sachchit Bansal
sumber
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);
Pradeep Singh
sumber
Saya sampai pada pertanyaan ini karena kode saya mulai menyukai jawaban Anda;)
jan groth
0

Jika Anda mengimplementasikan antarmuka Sebanding , Anda ingin memilih satu properti sederhana untuk dipesan. Ini dikenal sebagai pemesanan alami. Anggap saja sebagai default. Itu selalu digunakan ketika tidak ada komparator khusus yang disediakan. Biasanya ini adalah nama, tetapi use case Anda mungkin meminta sesuatu yang berbeda. Anda bebas menggunakan sejumlah Komparator lain yang dapat Anda suplai ke berbagai koleksi API untuk mengesampingkan pemesanan alami.

Perhatikan juga bahwa biasanya jika a.compareTo (b) == 0, maka a.equals (b) == true. Tidak apa-apa jika tidak tetapi ada efek samping yang harus diperhatikan. Lihat javadocs yang sangat baik di antarmuka Sebanding dan Anda akan menemukan banyak informasi hebat tentang ini.

Mark Renouf
sumber
0

Mengikuti blog diberi contoh Pembanding berantai yang baik

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

Pembanding Panggilan:

Collections.sort(listEmployees, new EmployeeChainedComparator(
                new EmployeeJobTitleComparator(),
                new EmployeeAgeComparator(),
                new EmployeeSalaryComparator())
        );
vaquar khan
sumber
0

Mulai dari jawaban Steve, operator ternary dapat digunakan:

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);
}
Gerold Broser
sumber
0

Sangat mudah untuk membandingkan dua objek dengan metode kode hash di 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()));

}
Keluaran
sumber
Tolong jangan lakukan itu. Kode Hash tidak boleh digunakan untuk perbandingan kesetaraan tetapi untuk pengindeksan yang dapat hashtable. Tabrakan hash dapat menghasilkan kesetaraan untuk dua objek yang berbeda. Bahkan hashtable bergantung pada kesetaraan nyata jika tabrakan hash terjadi.
Noel Widmer
0

Biasanya saya menimpa compareTo()metode saya seperti ini setiap kali saya harus melakukan penyortiran bertingkat.

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

Di sini preferensi pertama diberikan untuk nama film kemudian ke artis dan terakhir ke songLength. Anda hanya perlu memastikan bahwa pengganda itu cukup jauh untuk tidak melewati batas satu sama lain.

jayanth
sumber