サーチ…


構文

  • パブリッククラスMyClassは、Comparable <MyClass >
  • パブリッククラスMyComparatorは、Comparator <SomeOtherClass >
  • public int compareTo(MyClassその他)
  • public int compare(SomeOtherClass o1、SomeOtherClass o2)

備考

doubleに依存するcompareTo(..)メソッドを実装する場合は、次の操作は行わないでください。

public int comareTo(MyClass other) {
    return (int)(doubleField - other.doubleField); //THIS IS BAD
}

(int)キャストによる切り捨てにより、メソッドが正または負の数ではなく0を誤って返すことがあり、その結果、比較およびソートのバグにつながる可能性があります。

代わりに、 Double.compareを次のように使用するのが最も簡単な正しい実装です。

public int comareTo(MyClass other) {
    return Double.compare(doubleField,other.doubleField); //THIS IS GOOD
} 

Comparable<T>ジェネリックでないバージョン、単にComparable はJava 1.2以来存在しています 。レガシーコードとのインタフェース以外にも、ジェネリック版のComparable<T> 、比較時にキャストする必要がないため、実装する方が常に優れています。


クラスがそれ自身に匹敵することは、次のように非常に標準的です。

public class A implements Comparable<A>

このパラダイムから壊れる可能性はありますが、そうするときは慎重にしてください。


Comparator<T>は、そのクラスがComparable<T>実装している場合でも、そのクラスのインスタンスで使用できます。この場合、 Comparatorのロジックが使用されます。 Comparable実装で指定された自然順序付けは無視されます。

Comparableを使ってリストをソートするまたはコンパレータ

私たちはPersonを名字で表すクラスを作っているとしましょう。これを行うための基本クラスを作成し、適切なequals hashCodehashCodeメソッドを実装しました。

public class Person {

    private final String lastName; //invariant - nonnull
    private final String firstName; //invariant - nonnull

    public Person(String firstName, String lastName){
        this.firstName = firstName != null ? firstName : "";
        this.lastName = lastName != null ? lastName : "";
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String toString() {
        return lastName + ", " + firstName;
    }

    @Override
    public boolean equals(Object o) {
        if (! (o instanceof Person)) return false;
        Person p = (Person)o;
        return firstName.equals(p.firstName) && lastName.equals(p.lastName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, lastName);
    }
}

次のシナリオのように、 Personオブジェクトのリストを名前でソートするとします。

public static void main(String[] args) {
    List<Person> people = Arrays.asList(new Person("John", "Doe"),
                                     new Person("Bob", "Dole"),
                                     new Person("Ronald", "McDonald"),
                                     new Person("Alice", "McDonald"),
                                     new Person("Jill", "Doe"));
    Collections.sort(people); //This currently won't work.
}

残念ながら、マークされているように、上記は現在コンパイルされません。 Collections.sort(..)は、リスト内の要素が匹敵している場合、または比較のカスタムメソッドが指定されている場合にのみ、リストのソート方法を知っています。

あなたが次のリストを並べ替えるように求められたなら: 1,3,5,4,2 、答えは1,2,3,4,5と言っても問題ありません。これは、整数(Javaと数学的には両方とも)が自然順序付けを持つためです。これは、標準のデフォルト比較ベース順序です。 Personクラスに自然順序付けを与えるために、 compareTo(Person p):メソッドの実装が必要Comparable<Person>を実装しcompareTo(Person p):

public class Person implements Comparable<Person> {

    private final String lastName; //invariant - nonnull
    private final String firstName; //invariant - nonnull

    public Person(String firstName, String lastName) {
        this.firstName = firstName != null ? firstName : "";
        this.lastName = lastName != null ? lastName : "";
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String toString() {
        return lastName + ", " + firstName;
    }

    @Override
    public boolean equals(Object o) {
        if (! (o instanceof Person)) return false;
        Person p = (Person)o;
        return firstName.equals(p.firstName) && lastName.equals(p.lastName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, lastName);
    }

    @Override
    public int compareTo(Person other) {
        // If this' lastName and other's lastName are not comparably equivalent,
        // Compare this to other by comparing their last names.
        // Otherwise, compare this to other by comparing their first names
        int lastNameCompare = lastName.compareTo(other.lastName);
        if (lastNameCompare != 0) {
            return lastNameCompare;
        } else {
            return firstName.compareTo(other.firstName);
        }
    }
}

さて、与えられた主なメソッドは正しく機能します

public static void main(String[] args) {
    List<Person> people = Arrays.asList(new Person("John", "Doe"),
                                     new Person("Bob", "Dole"),
                                     new Person("Ronald", "McDonald"),
                                     new Person("Alice", "McDonald"),
                                     new Person("Jill", "Doe"));
    Collections.sort(people); //Now functions correctly

    //people is now sorted by last name, then first name:
    // --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}

ただし、クラスPersonを変更したくない場合や変更できない場合は、任意の2つのPersonオブジェクトの比較を処理するカスタムComparator<T>を用意することができます。 circle, square, rectangle, triangle, hexagonリストソートするよう依頼された場合、コーナーの数に基づいてそのリストソートするように求められた場合は、そうすることができます。ちょうど、比較器を提供することは、Javaに、2つの通常比較できないオブジェクトを比較する方法を指示します。

public class PersonComparator implements Comparator<Person> {

    public int compare(Person p1, Person p2) {
        // If p1's lastName and p2's lastName are not comparably equivalent,
        // Compare p1 to p2 by comparing their last names.
        // Otherwise, compare p1 to p2 by comparing their first names
        if (p1.getLastName().compareTo(p2.getLastName()) != 0) {
            return p1.getLastName().compareTo(p2.getLastName());
        } else {
            return p1.getFirstName().compareTo(p2.getFirstName());
        }
    }
}

//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
    List<Person> people = Arrays.asList(new Person("John", "Doe"),
                                     new Person("Bob", "Dole"),
                                     new Person("Ronald", "McDonald"),
                                     new Person("Alice", "McDonald"),
                                     new Person("Jill", "Doe"));
    Collections.sort(people); //Illegal, Person doesn't implement Comparable.
    Collections.sort(people, new PersonComparator()); //Legal

    //people is now sorted by last name, then first name:
    // --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}

Comparatorは、匿名の内部クラスとして作成/使用することもできます

//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
    List<Person> people = Arrays.asList(new Person("John", "Doe"),
                                     new Person("Bob", "Dole"),
                                     new Person("Ronald", "McDonald"),
                                     new Person("Alice", "McDonald"),
                                     new Person("Jill", "Doe"));
    Collections.sort(people); //Illegal, Person doesn't implement Comparable.

    Collections.sort(people, new PersonComparator()); //Legal

    //people is now sorted by last name, then first name:
    // --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald

    //Anonymous Class
    Collections.sort(people, new Comparator<Person>() { //Legal
        public int compare(Person p1, Person p2) {
            //Method code...
        }
    });
}
Java SE 8

ラムダ式コンパレータ

Java 8の時点では、コンパレータはラムダ式

    //Lambda
    Collections.sort(people, (p1, p2) -> { //Legal
        //Method code....
    });

コンパレータのデフォルトメソッド

さらに、Comparatorを構築するためのComparatorインターフェイスには、興味深いデフォルトメソッドがあります。次は、 lastNamefirstName比較器を作成しfirstName

Collections.sort(people, Comparator.comparing(Person::getLastName)
                                .thenComparing(Person::getFirstName));

コンパレータの順序を反転する

任意の比較も容易に使用して逆にすることができるreversedMethod降順に昇順に変更されます。

compareToメソッドとcompareメソッド

Comparable<T>インターフェイスには、1つのメソッドが必要です。

public interface Comparable<T> {

    public int compareTo(T other);

}

また、 Comparator<T>インターフェイスには、1つのメソッドが必要です。

public interface Comparator<T> {

    public int compare(T t1, T t2);

}

:これらの2つの方法は、1つのマイナー違いが、基本的に同じことを行うcompareTo比較しthisするotherのに対し、 compare比較t1するt2ではなく、思いやりのすべてについてthis

その違いとは別に、2つの方法にも同様の要件があります。特に(compareToの場合)、 このオブジェクトを指定されたオブジェクトと比較して注文します。このオブジェクトが指定されたオブジェクトよりも小さい、等しい、または大きい場合は、負の整数、ゼロまたは正の整数を返します。したがって、 ab比較のa

  • a < ba.compareTo(b)およびcompare(a,b)が負の整数を返し、 b.compareTo(a)およびcompare(b,a)が正の整数を返す必要がある場合
  • a > ba.compareTo(b)およびcompare(a,b)が正の整数を返し、 b.compareTo(a)およびcompare(b,a)が負の整数を返す必要がある場合
  • 場合等しいa b比較のために、すべての比較は返す必要があります0

自然(比較可能)と明示(比較)ソート

2つのCollections.sort()メソッドがあります:

  • かかる一List<T>パラメータとしてT匹敵実装し、上書きしなければならないcompareTo()ソート順序を決定する方法。
  • 引数としてListとComparatorをとり、Comparatorがソート順を決定するものです。

まず、Comparableを実装するPersonクラスを次に示します。

public class Person implements Comparable<Person> {         
    private String name;  
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }         

    @Override
    public int compareTo(Person o) {
        return this.getAge() - o.getAge();
    }
    @Override
    public String toString() {
        return this.getAge()+"-"+this.getName();
    }

}

上記のクラスを使用して、 compareTo()メソッドのオーバーライドによって定義された要素の自然順序付けでリストをソートする方法を次に示します。

//-- usage
List<Person> pList = new ArrayList<Person>();
            Person p = new Person();
            p.setName("A");
            p.setAge(10);
            pList.add(p);
            p = new Person();
            p.setName("Z");
            p.setAge(20);
            pList.add(p);
            p = new Person();
            p.setName("D");
            p.setAge(30);
            pList.add(p);
            
            //-- natural sorting i.e comes with object implementation, by age
            Collections.sort(pList);

            System.out.println(pList);

匿名のインラインコンパレータを使用して、Comparableを実装していないListをソートするか、この場合、自然順序付け以外の順序でListをソートする方法は次のとおりです。

            //-- explicit sorting, define sort on another property here goes with name
            Collections.sort(pList, new Comparator<Person>() {

                @Override
                public int compare(Person o1, Person o2) {
                    return o1.getName().compareTo(o2.getName());
                }
            });            
            System.out.println(pList);

マップエントリのソート

Java 8以降では、マップ反復のソートを可能にするためにMap.EntryインターフェースにデフォルトのメソッドがありMap.Entry

Java SE 8
Map<String, Integer> numberOfEmployees = new HashMap<>();
numberOfEmployees.put("executives", 10);
numberOfEmployees.put("human ressources", 32);
numberOfEmployees.put("accounting", 12);
numberOfEmployees.put("IT", 100);

// Output the smallest departement in terms of number of employees
numberOfEmployees.entrySet().stream()
    .sorted(Map.Entry.comparingByValue())
    .limit(1)
    .forEach(System.out::println);   // outputs : executives=10

もちろん、これらはストリームAPIの外側でも使用できます。

Java SE 8
List<Map.Entry<String, Integer>> entries = new ArrayList<>(numberOfEmployees.entrySet());
Collections.sort(entries, Map.Entry.comparingByValue());

比較メソッドを使用したコンパレータの作成

Comparator.comparing(Person::getName)

これは、このPerson名を比較元として使用するPersonクラスのコンパレーターを作成します。メソッドバージョンを使用してlong、int、doubleを比較することもできます。例えば:

Comparator.comparingInt(Person::getAge)

逆順

逆順を使用するコンパレータを作成するには、reverse reversed()メソッドを使用します。

Comparator.comparing(Person::getName).reversed()

コンパレータのチェーン

Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName)

これは、firsが姓と比較してファーストネームと比較するコンパレータを作成します。多くのコンパレータを必要に応じてチェーンすることができます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow