6

「IDまたは名前で従業員オブジェクトのコレクションをどのようにソートしますか」. そのために、2 つのインターフェイスを使用できます。つまり、Comparator and Comparable. これは面接でよく聞かれる質問の 1 つです。

しかし、従業員オブジェクトの並べ替えに両方を使用する必要がある理由がわかりません

comparatorそれComparableができないことは何かを考えてきました。オブジェクト (比較されるインスタンス変数) に自然な順序付けがある場合、それcomparableが正しい選択であることを理解しています。ただし、カスタムの順序付けが必要な場合 (文字列の長さなど)は、クライアントが他の基準でデータを並べ替えたい場合にのみcomparator. 、ここに私のポイントを書くことができます。comparatorたとえば、を使用Employee classして並べ替えを実装します。ただし、クライアントが Employee オブジェクトを(名前) で並べ替えたい場合は、具体的なクラスとして実装するか、並べ替えで匿名で実装します。ここに欠けているものはありますか?idcomparable interfaceStringcomparator

たとえば、次のコードでは、Person オブジェクトの場合、compareTo メソッドで年齢を比較して並べ替えています。compare メソッドでは、文字列の長さ (人物の名前) を使用して並べ替えています。理論的には、以下で実装したように、compareTo メソッドで両方を達成できます。

最後に、次のいずれかの追加の利点はありますか 2 つの方法でコンパレーターを実装しました。コンパレータを実装し、collections.sort() でそのクラスのインスタンスを呼び出す新しいクラス -- これはここでは行っていません

(The commented-out parts of the code works. They are just different implementations)

mport java.util.Collections;
import java.util.Comparator;
import java.util.*;

public class PersonComparator implements Comparable{
    private String name;
    private int age;

    public PersonComparator(String name, int age) {
        this.name = name;
        this.age = age;
    }
@Override
public String toString() {
    return "name=" + name + ", age=" + age;
}

/*@Override
public int compareTo(Object obj) {
    if (!(obj instanceof PersonComparator)) {
        throw new ClassCastException("Invalid object");
    }
    PersonComparator p2 = (PersonComparator)obj;
    return this.age-p2.age;
}*/

/*Alternative CompareTo that checks for both age and name*/
 public int compareTo(Object obj) {
    if (!(obj instanceof PersonComparator)) {
        throw new ClassCastException("Invalid object");
    }
    PersonComparator p2 = (PersonComparator)obj;
    if (this.age!=p2.age){
        return this.age-p2.age;
    }
    else {
    return (this.name.length()-p2.name.length());
}
} 


/*public static Comparator nameLengthComparator 
= new Comparator() {


    @Override
    public int compare(Object obj1, Object obj2) {
        if (!(obj1 instanceof PersonComparator) || !(obj2 instanceof PersonComparator)){
            throw new ClassCastException("Invalid object");
        }
        else {
            PersonComparator p1 = (PersonComparator)obj1;
            PersonComparator p2 = (PersonComparator)obj2;
            return p1.name.length()-p2.name.length();
        }
}
};*/

 public static void main(String[] args){
     PersonComparator p1 = new PersonComparator("Alexander", 45);
     PersonComparator p2 = new PersonComparator("Pat", 27);
     PersonComparator p3 = new PersonComparator("Zacky", 45);
     PersonComparator p4 = new PersonComparator("Rake", 34);

     List<PersonComparator> list = new ArrayList<PersonComparator>();
     list.add(p1);
     list.add(p2);
     list.add(p3);
     list.add(p4);

     System.out.println("Before sorting "+ list);
     Collections.sort(list);
     //System.out.println("After sorting by age "+ list);
     //System.out.println("Before sorting "+ list);
     //Collections.sort(list, nameLengthComparator);
     System.out.println("After sorting by name length "+ list);
     /*Collections.sort(list, new Comparator<PersonComparator>() {
         @Override
            public int compare(PersonComparator p1, PersonComparator p2) {
                    return p1.name.length()-p2.name.length();
                }
        }
    );*/
     System.out.println("After sorting by name length "+ list);
 }

}

ありがとう

4

7 に答える 7

9

Comparableインターフェース

Comparableインターフェイスは、型の自然順序付けを定義します。StringまたはIntegerオブジェクトのリストがあるとします。そのリストをに渡すことができます

Collections.sort(list);

ソートされたリストが表示されます。どのように?両方ともinterface の実装とStringinterfaceの実装が自然な順序付けを提供するためです。クラス定義のように、「私のタイプのオブジェクトのコレクションが見つかった場合は、メソッドで定義した戦略に従ってそれらを並べ替えます」。IntegerComparableComparablecompareTo

独自の型を定義すると、インターフェイスを実装することで、クラスのオブジェクトの自然な順序を定義できComparableます。オブジェクトの順序付けの詳細については、Java のドキュメントを参照してください。

Comparatorインターフェース

インターフェイスは、Comparatorオブジェクトの順序付けのカスタム戦略を定義する方法を説明します。以下のような単純なPerson型があるとします。

public class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

これで、インターフェイスを実装するComparatorことで、型のインスタンスを順序付けするためのさまざまな戦略を作成できますPerson。たとえば、Person以下に示すオブジェクトを順序付けするための 2 つの戦略を考えてみましょう。

class StrategyOne implements Comparator<Person> {

    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().length() - p2.getName().length();
    }

}

class StrategyTwo implements Comparator<Person> {

    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }

}

ここでStrategyOnePerson、名前の長さに基づいてオブジェクトを並べ替え、名前の辞書式StrategyTwo順序Personに基づいてオブジェクトを並べ替えます。

実装方法Comparator

ご覧のとおり、具体的な戦略クラスはステートレスであるため、すべてのインスタンスは機能的に同等です。したがって、具体的な戦略クラスのインスタンスが 1 つだけ必要です。したがって、それはシングルトンである必要があります。匿名クラスを使用すると、呼び出しが実行されるたびに新しいインスタンスが作成されます。オブジェクトをプライベート static final フィールドに格納し、静的ファクトリ メソッドを使用してそれらにアクセスして再利用することを検討してください[ Effective Java ]。たとえば、上記の 2 つの具体的な戦略を次のように再利用できます。

class Strategies {
    private static final Comparator<Person> PERSON_NAME_LENGTH_COMPARATOR = new StrategyOne();

    private static final Comparator<Person> PERSON_NAME_LEXICAL_COMPARATOR = new StrategyTwo();

    public static Comparator<Person> personNameLengthComparator(){
         return  PERSON_NAME_LENGTH_COMPARATOR;
    }


    public static Comparator<Person> personNameLexicalComparator(){
         return  PERSON_NAME_LEXICAL_COMPARATOR;
    }
}

概要

要約すると、Comparableインターフェイスはクラスの自然な順序付けを定義するために使用され、Comparatorインターフェイスはオブジェクトの順序付けの特定の戦略を定義するために使用されます。

于 2013-08-31T06:03:33.537 に答える
8

コンパレーターは、比較可能なものよりもどのように優れていますか?

「上等」ではありません。2 つのインターフェースが (大まかに) 同じことを異なる方法で行っているだけです。Comparable順序付けロジックが順序付けられるオブジェクトにある場合。このComparator場合、ロジックは、宣言されているオブジェクトとは異なるクラスにあります。

しかし、従業員オブジェクトの並べ替えに両方を使用する必要がある理由がわかりません

両方を使用することが理にかなっている唯一のケースは、オブジェクトを異なる順序でソートできるようにする必要がある場合です。次に、関連するクラスをComparable「自然な」順序の実装として宣言し、Comparatorオブジェクトを使用して他の順序を実装できます。

ところで、コンパレータはおそらく を実装すべきではなくComparable、その逆も同様です。

コンパレーターが実装されている場合、Comparableそれは、コンパレーターオブジェクト自体のインスタンスを注文しようとしていることを意味します...

クラスPersonComparatorの名前が間違っています。本当は と呼ぶべきPersonです。


equals()Object クラスから既にメソッドを作成しているのに、インターフェイスがメソッドを再びComparator容易にする理由を明確にしていただけますか?equals()

いくつかのポイント:

  • Comparableあなたはまだとの目的を混同しているようですComparator。オブジェクトのequalsメソッドはComparator、コンパレータを他のコンパレータと比較します!!

  • このequalsメソッドは、2 つのオブジェクトが等しいかどうかを示します。どちらが先かではありません。

  • Comparatorオーバーライドする理由equalsは、オブジェクトで呼び出したときに何が行われるかを明確に文書化できるようにするためだけです。(実際の動作は ... と完全に一致していますが、プログラマーがメソッドのセマンティクスを繰り返し間違っているため、明らかにこれを行う必要があると考えていました。) equals(Object)ComparatorObject.equals(Object)

于 2013-08-31T04:08:11.650 に答える
3

Comparable を使用すると、1 つのフィールドのみに基づいてコレクション内のアイテムを並べ替えることができます。Comparator を使用すると、複数のフィールドに基づいてアイテムを柔軟に比較できます。

例えば。

class Person implements Comparable
{

int age;
String name;


Person(int age,String name)
{
   this.age=age;
   this.name=name;
}

public int compareTo(Object o1)   // Either you can compare according to age or name
{
    Person p = (Person)o1;
    if (this.age==p.age)
    return 0;
    else if (this.age>p.age)
    return 1;
    else
    return -1;
}


public int compareTo(Object o)    //Based on name comparision
{
     return (this.name.compareTo((Person)o).name));
}
public static void main (String args[])
{
     List<Person> list = new ArrayList<Person>();
     Person o = new Person(12,"Steve");
     Person o1 = new Person(13,"Jason");
     list.add(o);
     list.add(o1);
     Collections.sort(list);
    }
}

上記の Comparable の場合は、年齢または名前のいずれかを使用してアイテムを並べ替えることができますが、 Comparator の場合は、複数のフィールドに基づいてアイテムを並べ替えることができます。

class AgeComparison implements Comparator
{
   public int compare(Object o1,Object o2)
{
        Person s1 = (Person)o1;
        Person s2 =(Person)o2;
        if (s1.age==s2.age)
        return 0;
        if(s1.age>s2.age)
        return 1;
        else
        return -1;
}

class NameComparison implements Comparator
{
     public int compare(Object o1,Object o2)
    {
        Person s1 = (Person)o1;
        Person s2 =(Person)o2;
        return (s1.age.compareTo(s2.age));
    }

}

Comparator を使用するには、使用するクラスのリストとインスタンスを渡す必要があります。

Collections.sort(list,new NameComparison());
Collections.sort(list,new AgeComparison());

簡単に言えば、Comparator の利点は、オブジェクトの複数のフィールドに基づいてリストをソートできる柔軟性です。

于 2013-08-31T07:39:13.450 に答える
0

一般に、順序付けが「自明」である場合は Comparable を使用します。たとえば、文字列の場合はアルファベット順を使用し、数値の場合は数値順を使用します。Comparable オブジェクトは単一の compareTo()メソッドしか実装できないため、1 つのオプション (「自然な」「明らかな」オプション) しか得られないことに注意してください。利点は、単純であり、クライアント コードが比較のために余分な作業を行う必要がないことです。

順序があまり明確でない場合、または複数のオプションが必要な場合は、Comparator を使用します。たとえば、書籍はタイトル、著者、ISBN などで並べ替えられます。これら 3 つのケースを処理するために、3 つの異なるコンパレータを使用できます。文字列を特殊な順序でソートしたい場合があります。たとえば、外国語の特殊なケース、大文字を無視するなどです。

また、並べ替えているオブジェクトが Comparable を実装していない場合、または相互に比較したくないタイプを混在させている場合 (一般に、これは回避する必要がありますが、Books と Authors を比較できるようにしたい場合があります)。特殊なケースでは単一のリスト) Comparator を使用する必要があります。

于 2013-08-31T04:31:16.497 に答える
0

ほら、どうぞ... 写真と説明を使って、この明確化についてすでに多くのことを書いてきました。

以下のリンクを参照してください。

比較可能および比較

いつでも思い出すことができると思う人は、それは「互換的に使用することはできない」ということです。

于 2013-08-31T04:37:31.567 に答える
0

コンパレーターを使用している場合は、コンパレーター クラスを 1 つ追加し、それを List オブジェクトと共に Collections.sort() メソッドに渡すだけで、既存のコードを変更する必要はありません。

ただし、同等のインターフェースを実装する場合は、すべてのモデル/Bean クラスのコードを変更して、compareTo() メソッドをオーバーライドする必要があります。

そのため、疎結合コンパレーターの方が優れています。

于 2016-10-24T13:17:46.560 に答える