ComparableおよびComparatorを使用する場合


108

私はフィールドでソートする必要があるオブジェクトのリストを持っています、例えばスコア。あまり考えずに、Comparatorを実装する新しいクラスを作成しました。このクラスは、タスクを実行して機能します。

これを振り返ると、Comparatorを実装する新しいクラスを作成するのではなく、代わりにmyクラスにComparableを実装させる必要があるのか​​と思います。スコアは、オブジェクトが注文される唯一のフィールドです。

  1. 練習として許容できることは何ですか?

  2. 「最初にクラスにComparableを実装させて(自然順序付けのため)、代替のフィールド比較が必要な場合は、Comparatorを実装する新しいクラスを作成する」という正しいアプローチですか?

  3. 上記の(2)がtrueの場合、クラスにComparableを実装させた後でのみコンパレータを実装する必要があるということですか?(私が元のクラスを所有していると仮定します)。

回答:


80

それがクラスをソートする明確な自然な方法である場合、オブジェクトはComparableを実装するべきだと私は言います、そして、クラスをソートする必要がある人は一般にそのようにしたいでしょう。

ただし、ソートがクラスの異常な使用である場合、またはソートが特定のユースケースでのみ意味がある場合は、コンパレータがより適切なオプションです。

別の言い方をすると、クラス名が与えられた場合、比較対象がどのようにソートされるかは明らかですか、それともjavadocを読む必要がありますか?後者の場合、将来のすべてのソートのユースケースでコンパレータが必要になる可能性があります。その場合、compariableの実装により、クラスのユーザーの速度が低下する可能性があります。


簡単な例を挙げていただけますか?
rgamber 2012年

これは良い例かもしれません:gist.github.com/yclian/2627608 ComparableVersionを使用するVersionクラスがあります。バージョン-ファクトリメソッドComparableVersionをオブジェクト(静的メソッドなし)と想定して提供-他のバージョンと比較できるバージョンを提供 責任は分離されています。
2013年


インタビュアーは、Comparableで同じことができるのになぜComparatorを使うのかと尋ねました、そして私はばかでした:(
Aadam

@aLearnerリンクが停止している
G.Brown

127

使用Comparable定義したい場合は、デフォルト問題のオブジェクトの(自然)の順序の動作を、一般的な方法は、このためにオブジェクトの技術や自然(データベース?)の識別子を使用することです。

外部の制御可能な順序付け動作Comparatorを定義する場合に使用します。これにより、デフォルトの順序付け動作をオーバーライドできます。


3
これは技術的な説明であり、現時点では正しいですが、ベストプラクティスについては何も述べていません。
extraneon

40
それはそれぞれをいつ使うべきかを伝えています-それがベストプラクティスではない場合、それは何ですか?
Bozho

1
「実装とComparableは、私が自然な秩序を定義しているということですか?」、これは私が探していた答えを私に与えました。ありがとう:)
ソムジット2013

61

使用Comparable

  • オブジェクトがコントロール内にある場合。
  • 比較動作が主な比較動作である場合。

使用Comparator

  • オブジェクトがコントロールの外にあり、それらを実装させることができない場合Comparable
  • デフォルト(で指定Comparable)の動作とは異なる動作を比較する場合。

20

同等 -java.lang.Comparable: int compareTo(Object o1)

比較可能なオブジェクトは、それ自体を別のオブジェクトと比較することができます。クラス自体は、インスタンスを比較できるようにするために、java.lang.Comparableインターフェースを実装する必要があります。

  • 現在のオブジェクトと提供されたオブジェクトを比較できます。
  • これを使用することonly one sort sequenceで、インスタンスプロパティに基づいて実装できます。例:Person.id
  • String、Wrapperクラス、Date、Calendarなどの事前定義されたクラスには、Comparableインターフェースが実装されています。

コンパレータ -java.util.Comparator: int compare(Object o1, Object o2)

コンパレータオブジェクトは、2つの異なるオブジェクトを比較できます。クラスはインスタンスを比較していませんが、他のクラスのインスタンスを比較しています。このコンパレータクラスは、java.util.Comparatorインターフェースを実装する必要があります。

  • 同じタイプの2つのオブジェクトを比較できます。
  • これを使用することによりmany sort sequence、インスタンスのプロパティに基づいて、それぞれを実装して名前を付けることができます。例:Person.id, Person.name, Person.age
  • カスタマイズされたソートのために、事前定義されたクラスにコンパレータインターフェースを実装できます。

例:

public class Employee implements Comparable<Employee> {

    private int id;
    private String name;
    private int age;
    private long salary;

    // Many sort sequences can be created with different names.
    public static Comparator<Employee> NameComparator = new Comparator<Employee>() {         
        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };
    public static Comparator<Employee> idComparator = new Comparator<Employee>() {       
        @Override
        public int compare(Employee e1, Employee e2) {
            return Integer.valueOf(e1.getId()).compareTo(Integer.valueOf(e2.getId()));
        }
    };

    public Employee() { }
    public Employee(int id, String name, int age, long salary){
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    // setters and getters.

    // Only one sort sequence can be created with in the class.
    @Override
    public int compareTo(Employee e) {
    //return Integer.valueOf(this.id).compareTo(Integer.valueOf(e.id));
    //return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        if (this.id > e.id) {
            return 1;
        }else if(this.id < e.id){
            return -1;
        }else {
            return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        }

    }   

    public static void main(String[] args) {

        Employee e1 = new Employee(5, "Yash", 22, 1000);
        Employee e2 = new Employee(8, "Tharun", 24, 25000);

        List<Employee> list = new ArrayList<Employee>();
        list.add(e1);
        list.add(e2);
        Collections.sort(list); // call @compareTo(o1)
        Collections.sort(list, Employee.nameComparator); // call @compare (o1,o2)
        Collections.sort(list, Employee.idComparator); // call @compare (o1,o2)
    }
}
  • カスタマイズされた並べ替えでは、コンパレータ@compare(o1、o2)を使用します。他のシナリオでは、比較可能な@compareTo(o1)を使用します。複数のフィールドを並べ替える場合は、コードを変更せずにコンパレータを使用します。

以下のためのJava 8のラムダ:コンパレータは私のポストを参照してください。


10

同じクラスのインスタンスを比較する場合は、Comparableを使用する必要があります。

コンパレータを使用して、異なるクラスのインスタンスを比較できます。

Comparableは、オブジェクトの自然な順序を定義する必要があるクラスによって実装されます。文字列のようにComparableを実装します。

別の並べ替え順序が必要な場合は、コンパレータを実装して、2つのインスタンスを比較する独自の方法を定義できます。


10

オブジェクトの並べ替えを自然順序に基づいて行う必要がある場合はComparableを使用し、別のオブジェクトの属性に対して並べ替えを行う必要がある場合は、Javaのコンパレータを使用します。

ComparableとComparatorの主な違い:

+------------------------------------------------------------------------------------+
¦               Comparable                ¦                Comparator                ¦
¦-----------------------------------------+------------------------------------------¦
¦ java.lang.Comparable                    ¦ java.util.Comparator                     ¦
¦-----------------------------------------+------------------------------------------¦
¦ int objOne.compareTo(objTwo)            ¦ int compare(objOne, objTwo)              ¦
¦-----------------------------------------+------------------------------------------¦
¦ Negative, if objOne < objTwo            ¦ Same as Comparable                       ¦
¦ Zero,  if objOne == objTwo              ¦                                          ¦
¦ Positive,  if objOne > objTwo           ¦                                          ¦
¦-----------------------------------------+------------------------------------------¦
¦ You must modify the class whose         ¦ You build a class separate from to sort. ¦
¦ instances you want to sort.             ¦ the class whose instances you want       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Only one sort sequence can be created   ¦ Many sort sequences can be created       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Implemented frequently in the API by:   ¦ Meant to be implemented to sort          ¦
¦ String, Wrapper classes, Date, Calendar ¦ instances of third-party classes.        ¦
+------------------------------------------------------------------------------------+

9

コンパレータは、比較可能な機能に加えて、さらに多くの機能を備えています。

| | Comparable | Comparator ._______________________________________________________________________________ Is used to allow Collections.sort to work | yes | yes Can compare multiple fields | yes | yes Lives inside the class you’re comparing and serves | | as a “default” way to compare | yes | yes Can live outside the class you’re comparing | no | yes Can have multiple instances with different method names | no | yes Input arguments can be a list of | just Object| Any type Can use enums | no | yes

次のように、コンパレータを匿名クラスとして使用するための最良のアプローチを見つけました。

private static void sortAccountsByPriority(List<AccountRecord> accounts) {
    Collections.sort(accounts, new Comparator<AccountRecord>() {

        @Override
        public int compare(AccountRecord a1, AccountRecord a2) {
            return a1.getRank().compareTo(a2.getRank());
        }
    });
}

ソートする予定のクラスの内部で、そのようなメソッドの複数のバージョンを作成できます。だからあなたは持つことができます:

  • sortAccountsByPriority
  • sortAccountsByType
  • sortAccountsByPriorityAndType

    等...

これで、これらの並べ替えメソッドをどこでも使用でき、コードを再利用できます。これは私に匹敵するものすべてに加えてさらに...だから、私は匹敵するものを使う理由はまったく見当たらない


8

私は言うでしょう:

  • 比較が直感的である場合は、必ずComparableを実装してください
  • 比較が直感的であるかどうかが不明確な場合は、コンパレータを使用してください。これはより明示的であるため、コードを保守する必要のある貧しい魂にとってより明確になります。
  • 可能な直感的な比較が複数ある場合は、比較対象のクラスのファクトリメソッドによってビルドされたコンパレータを使用します。
  • 比較が特別な目的の場合は、コンパレータを使用します

6

次の点は、Comparableを使用する状況とComparatorを決定する際に役立ちます。

1)コードの可用性

2)単一または複数のソート基準

3)Arays.sort()およびCollection.sort()

4)SortedMapおよびSortedSetのキーとして

5)クラス数の増加と柔軟性

6)クラス間の比較

7)自然秩序

より詳細な記事については、比較可能な場合とコンパレータを使用する場合を参照してください


なぜ誰もこの回答に賛成していないのでしょうか。それは本当に素晴らしいものです。+1
ディガンタ2017

4
  • クラスの作成時にソートのユースケースが1つしかない場合は、Comparableを使用します。
  • 並べ替えの戦略が複数ある場合にのみ、コンパレータを実装します。

4

自然な順序の並べ替えが必要な場合-カスタムの並べ替えが必要な場合はユーザーが比較可能-コンパレータを使用

例:

Class Employee{
private int id;
private String name;
private String department;
}

固有の並べ替えは一意であり、カスタムの並べ替えは名前と部門であるため、自然順の並べ替えはIDに基づいて行われます。

参考文献:
クラスを比較可能または比較、あるいはその 両方にする必要があるのはいつですか?http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html


3

ここで同様の質問がありました:クラスはいつComparableおよび/またはComparatorである必要がありますか?

私は次のように言います:自然な順序のようなもののためにComparableを実装します、例えば内部IDに基づいて

複数のフィールドなど、より複雑な比較アルゴリズムがある場合は、コンパレータを実装します。


1
複数のフィールドでの順序付けは、を使用して同じように行うことができますComparable
BalusC 2010

比較と比較の違いについては、java-journal.blogspot.in / 2010/12
学習者

2

同等:
必要な同種の要素とデフォルトの自然な並べ替え順序のみを保存する場合はいつでも、comparableインターフェースを実装するクラスに進むことができ ます。

コンパレータ:
同種および異種の要素を格納し、デフォルトのカスタマイズされたソート順でソートしたい場合は、いつでもcomparatorインターフェースにアクセスできます。


0

私の必要性は日付に基づいてソートされました。

そこで、Comparableを使用しましたが、簡単に機能しました。

public int compareTo(GoogleCalendarBean o) {
    // TODO Auto-generated method stub
    return eventdate.compareTo(o.getEventdate());
}

Comparableの1つの制限は、リスト以外のコレクションには使用できないことです。


0

クラスを所有している場合は、Comparableを使用してください。一般的にはコンパレータは、あなたがクラスを所有していけない場合に使用されていますが、それを使用する必要がTreeSetののTreeMapをコンパレータはTreeSetのかのTreeMapのconctructorのパラメータとして渡すことができるので。ComparatorとComparableの使用方法については、http: //preciselyconcise.com/java/collections/g_comparator.phpを参照してください。


0

インタビューの1つで、nlognの時間よりも良い範囲で特定の範囲の数値をソートするように求められました。(カウントソートを使用していません)

オブジェクトにComparableインターフェースを実装すると、暗黙の並べ替えアルゴリズムでオーバーライドされたcompareToメソッドを使用して並べ替え要素を並べ替えることができます。これは線形時間です。


0

Comparableは、数値に対して昇順で、文字列に対してアルファベット順で提供されるデフォルトの自然ソート順です。たとえば:

Treeset t=new Treeset();
t.add(2);
t.add(1);
System.out.println(t);//[1,2]

Comparatorは、カスタムのmyComparatorクラスに実装されたカスタムの並べ替え順序であり、たとえば次のようにcompareメソッドをオーバーライドします。

Treeset t=new Treeset(new myComparator());
t.add(55);
t.add(56);
class myComparator implements Comparator{
public int compare(Object o1,Object o2){
//Descending Logic
}
}
System.out.println(t);//[56,55]

-1

非常に単純なアプローチは、問題のエンティティクラスがデータベースで表され、データベーステーブルでエンティティクラスのフィールドで構成されるインデックスが必要であると想定することです。答えが「はい」の場合、同等のものを実装し、インデックスフィールドを使用して自然な並べ替え順序を実現します。他のすべてのケースでは、コンパレータを使用します。


-2

実装Comparableと私の注釈ライブラリComparator

public class Person implements Comparable<Person> {         
    private String firstName;  
    private String lastName;         
    private int age;         
    private char gentle;         

    @Override         
    @CompaProperties({ @CompaProperty(property = "lastName"),              
        @CompaProperty(property = "age",  order = Order.DSC) })           
    public int compareTo(Person person) {                 
        return Compamatic.doComparasion(this, person);         
    }  
}

リンクをクリックすると、他の例が表示されます。 http://code.google.com/p/compamatic/wiki/CompamaticByExamples

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