Nesneleri birden çok alana göre karşılaştırma


237

Şunlarla karşılaştırılabilecek birkaç alana sahip bazı nesneleriniz olduğunu varsayın:

public class Person {

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

    /* Constructors */

    /* Methods */

}

Bu örnekte, şunu sorarsanız:

a.compareTo(b) > 0

a'nın soyadının b'den önce gelip gelmediğini veya a'nın b'den büyük olup olmadığını soruyor olabilirsiniz ...

Bu tür nesneler arasında gereksiz karmaşa veya ek yük eklemeden çoklu karşılaştırmayı etkinleştirmenin en temiz yolu nedir?

  • java.lang.Comparable arayüz sadece bir alanla karşılaştırmaya izin verir
  • Çok sayıda karşılaştırma yöntemi (yani compareByFirstName(), compareByAge()vb.) Eklemek bence karmaşık.

Peki, bununla ilgili en iyi yol nedir?


3
neden bu bir cw? Bu tamamen geçerli bir programlama sorusu.
Elie

2
Comparable'ın istediğiniz kadar alanla karşılaştırmaya izin verdiğinin farkında mısınız?
DJClayworth

Yanıtlar:


81

Comparatorİki Personnesneyi karşılaştıran bir uygulayabilir ve istediğiniz kadar alanı inceleyebilirsiniz. Karşılaştırıcınıza hangi alanın karşılaştırılacağını söyleyen bir değişken koyabilirsiniz, ancak muhtemelen birden fazla karşılaştırıcı yazmak daha basit olacaktır.


5
Aslında tek bir Karşılaştırıcı kullanma fikrini tercih ediyorum. Bu cevabın yanlış olduğunu düşünmüyorum, ancak okuyan herkes kesinlikle Steve Kuo cevabını kontrol etmelidir.
Felipe Leão

Çoklu karşılaştırıcılar yalnızca verilerin kendisinin bir fonksiyonu olmayan farklı karşılaştırma yöntemleri istiyorsanız - yani bazen isme göre, diğer zamanlarda yaşa göre vb. Karşılaştırmak istersiniz. Aynı anda birden fazla alana göre karşılaştırmak için yalnızca bir karşılaştırıcı gerekli olurdu.
Elie

399

Java 8 ile:

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

Erişimci yöntemleriniz varsa:

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

Bir sınıf Karşılaştırılabilir uygularsa, böyle bir karşılaştırıcı CompareTo yönteminde kullanılabilir:

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

5
Özellikle döküm (Person p)zincirleme karşılaştırıcılar için önemlidir.
membersound

5
Çok sayıda nesneyi karşılaştırırken, örneğin sıralama yaparken ne kadar etkilidir? ComparatorHer çağrıda yeni örnekler oluşturması gerekiyor mu?
jjurm

4
Karşılaştığım alanlardan biri boş, bir dize gibi olduğunda bir NullPointerException alıyorum. Bu karşılaştırma biçimini korumak, ancak null güvenli olmasını sağlamak için yine de var mı?
reacheach

3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- ilk alan seçici, sonra karşılaştırıcı
gavenkoa

2
@jjurm, compareToyukarıda gösterildiği gibi kullandığınızda Comparator, yöntem her çağrıldığında oluşturulur. Karşılaştırıcıyı özel bir statik son alanda saklayarak bunu önleyebilirsiniz.
Gandalf

165

Uygulamalısın Comparable <Person>. Tüm alanların boş kalmayacağını varsayalım (basitlik uğruna), bu yaş bir int ve sıralamayı ilk, son, yaş olarak karşılaştırmak, compareToyöntem oldukça basittir:

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
Karşılaştırılabilir <Person> uygularsanız yöntem karşılaştırılırTo (Kişi p) .. Bu cevap Karşılaştırıcı'nın karşılaştırma <T o1, T o2> yöntemi ile karıştırılmış gibi görünüyor
Mike

5
Bu önerilmez. Birden fazla alanınız olduğunda Karşılaştırıcı'yı kullanın.
indika

1
şu anda en iyi çözüm bu (daha fazla karşılaştırıcıdan daha iyi)
Vasile Surdu

4
@indika, merak ediyorum: neden önerilmez? Birden fazla mülk kullanarak karşılaştırmak bana mükemmel geliyor.
ars-longa-vita-brevis

4
@ ars-longa-vita-brevis, Karşılaştırılabilir kullanıyorsanız, sıralama mantığı, nesnelerin sıralandığı sınıfta olmalıdır, böylece nesnelerin doğal sıralaması denir. Karşılaştırıcıyı kullanarak, Person sınıfı dışında özel sıralama mantığı yazabilirsiniz. Kişi nesnelerini yalnızca adıyla veya soyadıyla karşılaştırmak istiyorsanız, bu mantığı kullanamazsınız. Tekrar yazmak zorundasın,
Indika

78

( Java'daki nesne listelerini birden çok alana göre sıralamanın yolları )

Bu özette çalışma kodu

Java 8 lambda's'ı kullanma (10 Nisan 2019'da eklendi)

Java 8 bunu lambdalar tarafından güzel bir şekilde çözüyor (Guava ve Apache Commons yine de daha fazla esneklik sunabilir):

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

@ Gaoagong'un cevabı için teşekkürler .

Dağınık ve kıvrımlı: Elle sıralama

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

Bu çok fazla yazma, bakım gerektirir ve hataya açıktır.

Yansıtıcı yol: BeanComparator ile sıralama

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

Collections.sort(pizzas, chain);  

Açıkçası bu daha özlüdür, ancak bunun yerine Dizeler'i kullanarak alanlara doğrudan referansınızı kaybettiğiniz için daha fazla hata eğilimi vardır (yazım güvenliği, otomatik yeniden düzenleme yok). Şimdi bir alan yeniden adlandırılırsa, derleyici bir sorun bile bildirmez. Dahası, bu çözüm yansıma kullandığından, sıralama çok daha yavaştır.

Ulaşım: Google Guava'nın Karşılaştırma Zinciri ile Sıralama

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

Bu çok daha iyidir, ancak en yaygın kullanım durumu için bazı kazan plakası kodu gerektirir: null değerleri varsayılan olarak daha az değer verilmelidir. Boş alanlar için, bu durumda ne yapılacağı konusunda Guava'ya ekstra bir yönerge sağlamanız gerekir. Belirli bir şey yapmak istiyorsanız, ancak genellikle varsayılan durumu (yani 1, a, b, z, null) istiyorsanız bu esnek bir mekanizmadır.

Apache Commons ile Sıralama

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

Guava'nın CompareisonChain'i gibi, bu kütüphane sınıfı da birden çok alanda kolayca sıralanır, ancak null değerler için varsayılan davranışı da tanımlar (örn. 1, a, b, z, null). Ancak, kendi Karşılaştırıcınızı sağlamadığınız sürece başka bir şey belirtemezsiniz.

Böylece

Sonuçta lezzet ve esneklik ihtiyacı (Guava'nın Karşılaştırma Zinciri) ve özlü kod (Apache'nin CompareToBuilder) ile ilgilidir.

Bonus yöntemi

Ben güzel bir çözüm bulduğunu birleştirir birden karşılaştırıcılar öncelik sırasına göre CodeReview üzerinde bir de 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));
    }
}

Tabii ki Apache Commons Koleksiyonlarının bunun için zaten bir faydası var:

ComparatorUtils.chainedComparator (comparatorCollection)

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

22

@Patrick Birden fazla alanı art arda sıralamak için ComparatorChain'i deneyin

Bir ComparatorChain, bir veya daha fazla Karşılaştırıcıyı sırayla saran bir Karşılaştırıcıdır. ComparatorChain, 1) herhangi bir tek Comparator sıfır olmayan bir sonuç döndürürse (ve bu sonuç daha sonra döndürülür) veya 2) ComparatorChain tükenene (ve sıfır döndürülür) kadar her bir Karşılaştırıcıyı sırayla çağırır. Bu tür sıralama, SQL'deki çok sütunlu sıralamaya çok benzer ve bu sınıf, Java sınıflarının bir Listeyi sıralarken bu tür davranışları taklit etmesine izin verir.

SQL benzeri sıralamayı daha da kolaylaştırmak için, listedeki herhangi bir Karşılaştırıcının sırası tersine çevrilebilir.

Karşılaştırma (Nesne, Nesne) çağrıldıktan sonra yeni Karşılaştırıcılar ekleyen veya artan / azalan sıralamayı değiştiren bir yöntemin çağrılması UnsupportedOperationException özelliğine neden olur. Ancak, temeldeki Karşılaştırıcılar Listesini veya sıralama düzenini tanımlayan BitSet'i değiştirmemeye dikkat edin.

ComparatorChain örnekleri senkronize edilmez. Sınıf, inşaat zamanında iş parçacığı için güvenli değildir, ancak tüm kurulum işlemleri tamamlandıktan sonra birden çok karşılaştırma yapmak için iş parçacığı açısından güvenlidir.


20

Her zaman göz önünde bulundurabileceğiniz bir başka seçenek de Apache Commons. Birçok seçenek sunar.

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

Ör:

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
Bu stratejiyi çok seviyorum. Teşekkürler!
Bay Polywhirl

En etkili yol! Teşekkürler
Zakaria Bouazza

8

Java 8 akış API'sini kullanabilenler için, burada iyi belgelenmiş daha temiz bir yaklaşım var: Lambdas ve sıralama

C # LINQ eşdeğeri arıyordu:

.ThenBy(...)

Karşılaştırıcıda Java 8'de mekanizmayı buldum:

.thenComparing(...)

İşte algoritmayı gösteren pasaj.

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

Daha düzgün bir yol ve Java'nın tür çıkarımının LINQ'ya kıyasla tanımlamayı biraz daha karmaşık hale getirdiği hakkında bir açıklama için yukarıdaki bağlantıya göz atın.

İşte referans için tam birim testi:

@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

ComparatorBöyle bir kullanım durumu için el ile yazmak korkunç bir çözüm IMO'sudur. Bu tür geçici yaklaşımların birçok dezavantajı vardır:

  • Yeniden kod kullanımı yok. KURU ihlal ediyor.
  • Basmakalıp.
  • Artan hata olasılığı.

Peki çözüm nedir?

İlk önce bazı teoriler.

"Tür Akarşılaştırmayı destekler " önerisini belirtelim Ord A. (Program açısından, Ord Aiki As'yi karşılaştırmak için mantık içeren bir nesne olarak düşünebilirsiniz . Evet, tıpkı aynı Comparator.)

Şimdi, eğer Ord Ave Ord Bsonra, kompozitleri (A, B)de karşılaştırmayı desteklemelidir. yani Ord (A, B). Eğer Ord A,, Ord Bve Ord Csonra Ord (A, B, C).

Bu argümanı keyfi bir düzene genişletebiliriz ve şunu söyleyebiliriz:

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

Bu ifadeye 1 diyelim.

Kompozitlerin karşılaştırması aynen sizin sorunuzda açıklandığı gibi çalışacaktır: ilk karşılaştırma önce denenecek, sonra bir sonrakine, sonra bir sonrakine vb.

Bu bizim çözümümüzün ilk kısmı. Şimdi ikinci kısım.

Eğer biliyorsanız Ord A, ve dönüşümü bilen Biçin A(transformasyon fonksiyonu bu aramayı f), o zaman da olabilir Ord B. Nasıl? İki Börnek karşılaştırılacağı zaman, önce bunları Akullanmaya dönüştürüp fuygularsınız Ord A.

Burada, dönüşümü eşleştirdiğimize B → Aiçin Ord A → Ord B. Bu, kontravaryant eşleme (veya comapkısaca) olarak bilinir .

Ord A, (B → A)komap Ord B

Bu ifadeye 2 diyelim.


Şimdi bunu örneğinize uygulayalım.

PersonÜç tür alan içeren bir veri türünüz var String.

  • Bunu biliyoruz Ord String. İfadeye göre 1 Ord (String, String, String),.

  • Biz kolayca bir işlev yazabilirsiniz Personiçin (String, String, String). (Sadece üç alanı geri getirin.) Bildiğimizden Ord (String, String, String)ve Person → (String, String, String)ifade 2 comapile almak için kullanabiliriz Ord Person.

QED.


Tüm bu kavramları nasıl uygularım?

İyi haber şu ki gerek yok. Bu yayında açıklanan tüm fikirleri uygulayan bir kütüphane zaten var . (Bunların nasıl uygulandığını merak ediyorsanız , kaputun altına bakabilirsiniz .)

Kod onunla böyle görünecek:

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

Açıklama:

  • stringOrdtüründe bir nesnedir Ord<String>. Bu, orijinal "karşılaştırmayı destekliyor" teklifimize karşılık gelir.
  • p3Ordalan bir yöntemdir Ord<A>, Ord<B>, Ord<C>ve döner Ord<P3<A, B, C>>. Bu, 1. ifadeye karşılık gelir. ( P3Üç elementli ürün anlamına gelir. Ürün, kompozitler için cebirsel bir terimdir.)
  • comapkuyuya karşılık gelir comap.
  • F<A, B>bir dönüştürme fonksiyonunu temsil eder A → B.
  • p ürün oluşturmak için fabrika yöntemidir.
  • Tüm ifade, 2. ifadeye karşılık gelir.

Umarım yardımcı olur.


5

Karşılaştırma yöntemleri yerine, yalnızca Person sınıfı içinde çeşitli "Karşılaştırıcı" alt sınıfları tanımlamak isteyebilirsiniz. Bu şekilde bunları standart Koleksiyonlar sıralama yöntemlerine aktarabilirsiniz.


3

Bence karşılaştırma algoritmanız "akıllı" olsaydı daha kafa karıştırıcı olurdu. Önerdiğin sayısız karşılaştırma yöntemi ile giderdim.

Benim için tek istisna eşitlik olurdu. Birim testi için, iki nesne arasında birden fazla alanın eşit olup olmadığını (referansların eşit olmadığını) belirlemek için .Equals (.net) içinde geçersiz kılmak benim için yararlı oldu.


3

Bir kullanıcının kişi sipariş etmesinin birden çok yolu varsa, bir yerde sabit olarak birden çok Karşılaştırıcı kurulumuna da sahip olabilirsiniz . Sıralama işlemlerinin ve sıralanan koleksiyonların çoğu parametre olarak bir karşılaştırıcı alır.


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

Person nesnesini birden çok alana göre sıralamamız gerektiğinde, bunun kod uygulaması da buradadır.

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

Bu soruya geldim çünkü kodum cevabınızı beğenmeye başladı;)
jan groth

0

Karşılaştırılabilir arabirimi uygularsanız, sipariş vermek için basit bir özellik seçmek istersiniz. Bu doğal düzen olarak bilinir. Bunu varsayılan olarak düşünün. Belirli bir karşılaştırıcı sağlanmadığında her zaman kullanılır. Genellikle bu addır, ancak kullanım durumunuz farklı bir şey gerektirebilir. Doğal sıralamayı geçersiz kılmak için çeşitli koleksiyon API'larına sağlayabileceğiniz diğer birçok Karşılaştırıcıyı kullanmakta özgürsünüz.

Ayrıca genellikle a.compareTo (b) == 0 ise a.equals (b) == true değerine dikkat edin. Değilse sorun değil ama dikkat edilmesi gereken yan etkiler var. Karşılaştırılabilir arayüzdeki mükemmel javadokları görün ve bununla ilgili birçok harika bilgi bulacaksınız.


0

Aşağıdaki blog iyi zincirlenmiş Karşılaştırıcı örneği verildi

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

Arama Karşılaştırıcısı:

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

0

Steve'in cevabından başlayarak üçlü operatör kullanılabilir:

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

Java`da hashcode yöntemi ile iki nesneyi karşılaştırmak kolaydır

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

}

Lütfen bunu yapma. Hashcode'lar eşitlik karşılaştırmaları için değil, hashtable indeksleme için kullanılmalıdır. Karma çarpışmalar iki farklı nesne için eşitliğe neden olabilir. Karma çarpışmalar meydana gelirse karma tablolar bile gerçek eşitliğe güvenir .
Noel Widmer

0

Genellikle compareTo()çok düzeyli sıralama yapmak zorunda kaldığımda yöntemimi geçersiz kılıyorum.

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

Burada ilk olarak film adına, daha sonra sanatçıya ve son olarak songLength'a tercih edilir. Sadece bu çarpanların birbirlerinin sınırlarını aşmayacak kadar uzak olduğundan emin olmalısınız.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.