複数のフィールドでオブジェクトを比較する方法


237

次の方法で比較できるいくつかのフィールドを持ついくつかのオブジェクトがあるとします。

public class Person {

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

    /* Constructors */

    /* Methods */

}

したがって、この例では、次のことを尋ねると、

a.compareTo(b) > 0

あなたはaの姓がbの前に来るのか、またはaがbより古いのかなどを尋ねているかもしれません...

不要な混乱やオーバーヘッドを追加せずに、これらの種類のオブジェクト間の複数の比較を可能にする最もクリーンな方法は何ですか?

  • java.lang.Comparable インターフェースにより、1つのフィールドのみによる比較が可能
  • 多数の比較メソッド(つまりcompareByFirstName()compareByAge()など)を追加することは、私の意見では混乱しています。

それで、これについて最善の方法は何ですか?


3
なぜこれがcwなのですか?これは完全に有効なプログラミングの質問です。
エリー

2
Comparableは、好きなだけ多くのフィールドで比較できることを知っていますか?
DJClayworth 2008

回答:


81

Comparator2つのPersonオブジェクトを比較するを実装でき、必要な数のフィールドを調べることができます。比較するフィールドを指示する変数をコンパレーターに入れることができますが、複数のコンパレーターを記述する方がおそらく簡単です。


5
私は実際には単一のコンパレータを使用するという考え方を好みます。私はこの答えが間違っているとは思いませんが、それを読んでいる人は必ず以下のSteve Kuoの答えを確認する必要があります。
FelipeLeão2014年

複数のコンパレータは、データ自体の関数ではないさまざまな比較方法が必要な場合にのみ使用されます。つまり、名前で比較したり、他の時間で比較したりしたい場合があります。複数のフィールドで同時に比較するには、1つのコンパレータのみ必要になるでしょう。
エリー2014年

399

Java 8の場合:

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

アクセサメソッドがある場合:

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

クラスがComparableを実装している場合、そのようなコンパレーターはcompareToメソッドで使用できます。

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

5
特に、キャスト(Person p)はチェーンコンパレータにとって重要です。
メンバーズサウンド

5
並べ替えなど、多数のオブジェクトを比較する場合、どの程度効率的ですか?Comparatorすべての呼び出しで新しいインスタンスを作成する必要がありますか?
jjurm

4
文字列のように、比較しているフィールドの1つがnullの場合、NullPointerExceptionが発生します。とにかくこの比較形式を維持するが、ヌルセーフにすることはできますか?
rveach

3
@jjurm- .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))最初のフィールドセレクター、次にコンパレーター
ジバンコア2018年

2
@jjurmをcompareTo上記のように使用するとComparator、メソッドが呼び出されるたびに作成されます。これを防ぐには、コンパレーターをプライベートな静的最終フィールドに格納します。
ガンダルフ

165

実装する必要がありますComparable <Person>。すべてのフィールドが(単純化するために)nullではない場合、その年齢はintであり、比較ランキングは最初、最後、年齢であり、compareToメソッドは非常に単純です。

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
Comparable <Person>を実装する場合、メソッドはcompareTo(Person p)。です。この答えは、Comparatorのcompare <T o1、T o2>メソッドと混同されているようです
Mike

5
これは推奨されません。複数のフィールドがある場合は、コンパレータを使用します。
インディカ2014

1
これが現時点で最良のソリューションです(多くのコンパレータより優れています)
Vasile Surdu 2014

4
@indika、私は好奇心が強い:なぜこれが推奨されないのですか?複数のプロパティを使用して比較することは、私には完全にうまくいきます。
ars-longa-vita-brevis 2014

4
@ ars-longa-vita-brevis、Comparableを使用している場合、並べ替えロジックはオブジェクトが並べ替えられている同じクラスにある必要があるため、これはオブジェクトの自然順序付けと呼ばれます。コンパレータを使用すると、Personクラスの外部でカスタムの並べ替えロジックを記述できます。Personオブジェクトを姓または名だけで比較する場合は、このロジックを使用できません。あなたはもう一度それを書かなければなりません
インディカ'26

78

複数のフィールドに基づいてJavaでオブジェクトのリストをソートする方法から

この要点の作業コード

Java 8ラムダの使用(2019年4月10日追加)

Java 8はこれをラムダによって適切に解決します(ただし、GuavaとApache Commonsはより高い柔軟性を提供します)。

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

以下の @gaoagongの回答に感謝します

乱雑で複雑:手で並べ替え

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

これには多くのタイピングとメンテナンスが必要で、エラーが発生しやすくなります。

反射的な方法:BeanComparatorによるソート

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

Collections.sort(pizzas, chain);  

明らかにこれはより簡潔ですが、代わりに文字列を使用してフィールドへの直接参照を失うため、より多くのエラーが発生しやすくなります(型保証なし、自動リファクタリング)。フィールドの名前が変更されても、コンパイラは問題を報告しません。さらに、このソリューションではリフレクションを使用しているため、並べ替えは非常に遅くなります。

アクセス:Google GuavaのComparisonChainによる並べ替え

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

これははるかに優れていますが、最も一般的な使用例では、いくつかのボイラープレートコードが必要です。null値は、デフォルトでは小さい値にする必要があります。nullフィールドの場合、その場合に何をするかをGuavaに追加のディレクティブを提供する必要があります。これは、特定のことをしたい場合の柔軟なメカニズムですが、多くの場合、デフォルトのケース(つまり、1、a、b、z、null)が必要です。

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

GuavaのComparisonChainと同様に、このライブラリクラスは複数のフィールドで簡単に並べ替えますが、null値(つまり、1、a、b、z、null)のデフォルトの動作も定義します。ただし、独自のコンパレータを提供しない限り、他に何も指定することはできません。

したがって

最終的には、フレーバーと柔軟性の必要性(GuavaのComparisonChain)と簡潔なコード(ApacheのCompareToBuilder)が関係します。

ボーナス方式

はCodeReviewの優先順位に従って複数のコンパレータを組み合わせた素晴らしいソリューションを見つけました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));
    }
}

もちろん、Apache Commons Collectionsには、このためのユーティリティがすでにあります。

ComparatorUtils.chainedComparator(comparatorCollection)

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

22

@Patrick複数のフィールドを連続して並べ替えるには、ComparatorChainを試してください。

ComparatorChainは、1つ以上のコンパレータを順番にラップするコンパレータです。ComparatorChainは、1)単一のコンパレータがゼロ以外の結果を返す(そしてその結果が返される)、または2)ComparatorChainが使い果たされる(そしてゼロが返される)まで、各コンパレータを順番に呼び出します。この種類の並べ替えは、SQLの複数列の並べ替えとよく似ており、このクラスを使用すると、Javaクラスでリストの並べ替え時にそのような動作をエミュレートできます。

SQLのような並べ替えをさらに容易にするために、リスト内の単一のコンパレータの順序を逆にすることができます。

compare(Object、Object)が呼び出された後に、新しいコンパレーターを追加するメソッド、または昇順/降順のソートを変更するメソッドを呼び出すと、UnsupportedOperationExceptionが発生します。ただし、基になるコンパレータのリストまたはソート順を定義するビットセットを変更しないように注意してください。

ComparatorChainのインスタンスは同期されません。クラスは構築時にスレッドセーフではありませんが、すべてのセットアップ操作が完了した後に複数の比較を実行することはスレッドセーフです。


20

常に考慮できるもう1つのオプションは、Apache Commonsです。それは多くのオプションを提供します。

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

例:

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
私はこの戦略がとても好きです。ありがとう!
Mr. Polywhirl

最も効果的な方法!ありがとう
Zakaria Bouazza

8

Java 8ストリーミングAPIを使用できる人のために、ここで十分に文書化されている簡潔なアプローチがあります: ラムダとソート

C#LINQに相当するものを探していました。

.ThenBy(...)

コンパレータでJava 8のメカニズムを見つけました。

.thenComparing(...)

これがアルゴリズムを示すスニペットです。

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

上記のリンクで、Javaの型推論によってLINQに比べてJavaの型推論によって定義が少し不格好になる方法についての説明を確認してください。

参考のための完全な単体テストを次に示します。

@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

Comparatorそのようなユースケースのために手動で書くことはひどい解決策IMOです。このようなアドホックアプローチには多くの欠点があります。

  • コードの再利用はありません。DRYに違反しています。
  • ボイラープレート。
  • エラーの可能性の増加。

それで解決策は何ですか?

最初にいくつかの理論。

「型Aは比較をサポートする」という命題をで示しましょうOrd A。(プログラムの観点からは、Ord A2つAのを比較するためのロジックを含むオブジェクトと考えることができます。はい、と同じComparatorです。)

そして、もしOrd Aとならば、Ord Bそれらのコンポジット(A, B)も比較をサポートするはずです。すなわちOrd (A, B)。もしOrd AOrd B、およびOrd C、その後、Ord (A, B, C)

この議論を任意のアリティに拡張して、次のように言うことができます。

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

このステートメントを1としましょう。

コンポジットの比較は、質問で説明したとおりに機能します。最初の比較が最初に試行され、次に次の比較が試行されます。

それが私たちのソリューションの最初の部分です。それでは次のパートです。

それを知っていてOrd A、どのように変換BするかA(その変換関数を呼び出すf)を知っている場合は、も持つことができますOrd B。どうやって?さて、2つのBインスタンスを比較するときは、まずそれらをA使用に変換してからf適用しますOrd A

ここでは、変換B → AをにマッピングしていOrd A → Ord Bます。これは反変マッピング(またはcomap略して)と呼ばれます。

Ord A, (B → A)comap Ord B

このステートメントを2と呼びましょう。


これをあなたの例に適用しましょう。

タイプのPerson3つのフィールドで構成されるという名前のデータタイプがありますString

  • 私たちはそれを知っていOrd Stringます。文1によりますOrd (String, String, String)

  • からPersonまでの関数を簡単に記述できます(String, String, String)。私たちが知っているので(ジャスト。三つのフィールドを返す)Ord (String, String, String)Person → (String, String, String)の文2によって、私たちが使用することができ、comap取得しますOrd Person

QED。


これらの概念をすべて実装するにはどうすればよいですか?

良いニュースは、あなたがする必要がないことです。この投稿で説明されているすべてのアイデアを実装するライブラリはすでに存在しいます。(これらの実装方法に興味がある場合は、内部で確認できます。)

これは、コードがどのように見えるかです。

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

説明:

  • stringOrdタイプのオブジェクトですOrd<String>。これは、元の「サポートの比較」の命題に対応しています。
  • p3Ordかかる方法でありOrd<A>Ord<B>Ord<C>、および戻りOrd<P3<A, B, C>>。これはステートメント1に対応します(P33つの要素を持つ製品を表します。製品は複合材料の代数的な用語です。)
  • comapよく対応しますcomap
  • F<A, B>は、変換関数を表しますA → B
  • p 製品を作成するためのファクトリメソッドです。
  • 式全体がステートメント2に対応します。

お役に立てば幸いです。


5

比較メソッドの代わりに、Personクラス内にいくつかのタイプの「コンパレータ」サブクラスを定義したい場合があります。そうすれば、それらを標準のコレクションの並べ替えメソッドに渡すことができます。


3

あなたの比較アルゴリズムが「賢い」だったらもっと混乱するだろうと思います。私はあなたが提案した数多くの比較方法を採用します。

私の唯一の例外は平等でしょう。ユニットテストでは、2つのオブジェクト間で複数のフィールドが等しいかどうか(参照が等しいかどうか)を判断するために.Equals(.net内)をオーバーライドすると便利です。


3

ユーザーが人を注文する方法が複数ある場合は、複数のComparatorを定数としてどこかにセットアップすることもできます。ほとんどのソート操作とソートされたコレクションは、パラメーターとしてコンパレーターを取ります。


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オブジェクトをソートする必要がある場合、同じのコード実装がここにあります。

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

私のコードがあなたの答えを好きになり始めたので、私はこの質問に来ました;)
jan groth

0

Comparableインターフェースを実装する場合は、1つの単純なプロパティを選択して並べ替えます。これは自然順序付けとして知られています。デフォルトと考えてください。特定のコンパレータが指定されていない場合は常に使用されます。通常、これは名前ですが、ユースケースでは別の名前が必要になる場合があります。さまざまなコレクションAPIに提供できる他のコンパレータをいくつでも自由に使用して、自然な順序をオーバーライドできます。

また、通常、a.compareTo(b)== 0の場合、a.equals(b)== trueになることに注意してください。大丈夫ですが、注意すべき副作用があります。Comparableインターフェースで優れたjavadocを参照してください。これについては、多くのすばらしい情報が見つかります。


0

良いチェーンコンパレータの例を与えられた次のブログ

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

コンパレータを呼び出す:

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

0

スティーブの答えから始めて、三項演算子を使うことができます:

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

2つのオブジェクトを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()));

}

しないでください。ハッシュコードは、等価比較ではなく、ハッシュテーブルのインデックス作成に使用する必要があります。ハッシュの衝突により、2つの異なるオブジェクトが等しくなる場合があります。ハッシュの衝突が発生した場合でも、ハッシュテーブルは実際の等価性に依存しています。
Noel Widmer 2017年

0

通常、compareTo()マルチレベルのソートを行う必要があるときはいつでも、このようにメソッドをオーバーライドします。

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

ここでは、最初に映画名、次にアーティスト、最後にsongLengthが優先されます。あなたはそれらの乗数がお互いの境界を越えないように十分離れていることを確認する必要があります。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.