カスタムオブジェクトのArrayListをプロパティで並べ替え


1145

私はコンパレータを使用してArrayListsをソートすることについて読みましたが、すべての例で人々が使用compareToしたものは、いくつかの調査によると文字列のメソッドです。

カスタムオブジェクトのArrayListを、プロパティの1つであるDateオブジェクト(getStartDay())で並べ替えたいと考えました。通常私はそれらを比較するitem1.getStartDate().before(item2.getStartDate())ので、私は次のようなものを書くことができるかどうか疑問に思っていました:

public class CustomComparator {
    public boolean compare(Object object1, Object object2) {
        return object1.getStartDate().before(object2.getStartDate());
    }
}

public class RandomName {
    ...
    Collections.sort(Database.arrayList, new CustomComparator);
    ...
}


2
この投稿の@Yishaiによる回答は、コンパレータチェーンを利用したカスタムソートおよびグループ化ソート(複数の引数)のためのenumのエレガントな使用法を示しています。
gunalmel 2012

回答:


1538

はをDate実装Comparableしているため、doesとcompareTo同じようなメソッドを持ってStringいます。

したがって、カスタムComparatorは次のようになります。

public class CustomComparator implements Comparator<MyObject> {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
}

compare()この方法は、返す必要がありint、あなたが直接返すことができなかったので、booleanとにかくに計画していたように。

あなたのソートコードはあなたが書いたものとほぼ同じです:

Collections.sort(Database.arrayList, new CustomComparator());

コンパレータを再利用する必要がない場合、これを少し短く書く方法は、インラインの匿名クラスとして書くことです。

Collections.sort(Database.arrayList, new Comparator<MyObject>() {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
});

以来

最後にラムダ式を使用して、最後の例を短い形式で記述できますComparator

Collections.sort(Database.arrayList, 
                        (o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

そして、List持っているsort(Comparator)あなたも、これをさらに短縮することができますので、この方法を:

Database.arrayList.sort((o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

これは非常に一般的なイディオムであり、キーを持つクラスのを生成する組み込みメソッドComparatorがありComparableます。

Database.arrayList.sort(Comparator.comparing(MyObject::getStartDate));

これらはすべて同等の形式です。


33
+1が返されるべきでintありDate#compareTo()、これを使用する方がよいことを言及します。なぜこれが他の答えより上に賛成されないのかは私を超えています。このリンクも役に立ちます:Sun.comのObject Ordering Tutorial
BalusC 2010年

5
Java 8でそれを行うための適切な方法も回答に含める必要があります。Collections.sort(list、Comparator.comparing(MyObject :: getStartDate)); 読みやすく、エラーが発生しにくくなります。return o1.getStartDate()。compareTo(o1.getStartDate());を書くのはとても簡単です。
クバ2014年

2
コンパレータクラスは静的でなければなりません:)
Jarmez De La Rocha '27

4
@Kubaの方が良いList.sort()です。
shmosel

3
このソリューションは、Android API <24では機能しません。皆さんはこれに対する解決策を知っていますか?
ジムクレルモンズ

194

自然な並べ替え順序を持つクラス(例としてクラス番号)はComparableインターフェースを実装する必要がありますが、自然な並べ替え順序がないクラス(例としてクラス椅子)にはコンパレータ(または匿名のコンパレータ)を提供する必要がありますクラス)。

2つの例:

public class Number implements Comparable<Number> {
    private int value;

    public Number(int value) { this.value = value; }
    public int compareTo(Number anotherInstance) {
        return this.value - anotherInstance.value;
    }
}

public class Chair {
    private int weight;
    private int height;

    public Chair(int weight, int height) {
        this.weight = weight;
        this.height = height;
    }
    /* Omitting getters and setters */
}
class ChairWeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getWeight() - chair2.getWeight();
    }
}
class ChairHeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getHeight() - chair2.getHeight();
    }
}

使用法:

List<Number> numbers = new ArrayList<Number>();
...
Collections.sort(numbers);

List<Chair> chairs = new ArrayList<Chair>();
// Sort by weight:
Collections.sort(chairs, new ChairWeightComparator());
// Sort by height:
Collections.sort(chairs, new ChairHeightComparator());

// You can also create anonymous comparators;
// Sort by color:
Collections.sort(chairs, new Comparator<Chair>() {
    public int compare(Chair chair1, Chair chair2) {
        ...
    }
});

私はそれを試しました-しかし、他のクラスの外部のコンパレータークラスChairWeightComparatorにアクセスしたい場合、このクラスにはアクセスできません(もちろん、パブリックではないためです)。別のファイルで新しいパブリックChairWeightComparatorクラスを作成する必要がありますか?-私は本当に3年後にこれを試した最初のものですか、それとも私はsthを逃しましたか?
user387184 2014年

@ user387184-単に公開して、それを独自のファイル(できれば独自のパッケージも含む)に配置すると、プロジェクト内のどこでも使用できるようになります。追加のクラスを作成する必要はありません!
ビョルン14年

つまり、クラスではなく新しいファイルを作成して、コードに次のように記述します。「クラスChairWeightComparatorはComparator <Chair> {....を実装します」?
user387184 2014年

@ user387184、まさに-のpublic前にキーワードがありclassます。
ビョルン

157

並べ替えにはArrayList、次のコードスニペットを使用できます。

Collections.sort(studList, new Comparator<Student>(){
    public int compare(Student s1, Student s2) {
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
    }
});

1
ラムダ使って誰でもstackoverflow.com/questions/2784514/...
ソーター

それはソートされますが、各要素に2つの値を与えます
Sam

44

はい、できます。アイテムの比較には、ComparableインターフェースとComparatorインターフェースの2つのオプションがあります。

これらのインターフェースはどちらも異なる動作を可能にします。Comparableを使用すると、オブジェクトを、文字列を記述したように動作させることができます(実際、StringはComparableを実装しています)。2つ目はコンパレータです。これにより、求めていることを実行できます。あなたはこのようにします:

Collections.sort(myArrayList, new MyComparator());

これにより、Collections.sortメソッドがコンパレータを使用してソートメカニズムを実行します。ArrayListのオブジェクトが同等のものを実装している場合は、代わりに次のようなことを行うことができます。

Collections.sort(myArrayList);

コレクションクラスは、これらの便利な、一般的なツールの数が含まれています。


42

JAVA 8ラムダ式

Collections.sort(studList, (Student s1, Student s2) ->{
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
});

または

Comparator<Student> c = (s1, s2) -> s1.firstName.compareTo(s2.firstName);
studList.sort(c)

3
またはCollections.sort(studList, Comparator.comparing(Student::getFirstName));
Holger

5
..またはstudList.sort(Comparator.comparing(Student::getFirstName));
Alexis C.

あなたの場合、ソート順は常に昇順です。私の例では、ソート順も処理します。紳士に感謝します。
ソーター、2015

33

Java 8では、コンパレーターのメソッド参照を使用できます。

import static java.util.Comparator.comparing;

Collections.sort(list, comparing(MyObject::getStartDate));

@ user387184残念ながらandroidはJava 8をサポートしていませんが、回避策はあるかもしれません(私はテストしていません)。
アッシリアス14年

14

テクノロジーは日々登場しているので、答えは時間とともに変わります。私はLambdaJを見て、とても興味深いようです。

LambdaJでこれらのタスクを解決してみることができます。ここで見つけることができます:http : //code.google.com/p/lambdaj/

ここに例があります:

反復ソート

List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>() {
        public int compare(Person p1, Person p2) {
           return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
        }
});

ラムダで並べ替え

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

もちろん、このような美しさはパフォーマンスに影響します(平均2倍)が、もっと読みやすいコードを見つけることができますか?


それはソートされますが、それを回避する方法を各要素に二重の値を与えます
Sam

@サム、それはいけない...それは期待通りに機能しています。バグのある新しいバージョンを使用しているのでない限り、それをフォーラムに投稿することをお勧めします。とにかく、この答えはjava 8より前の投稿でした。使用すると、lambdajを使用するよりもはるかに良くなります
Federico Piazza

私はforeachループ内のアイテムも削除する必要がありました。そうしないと、各コンテンツの2倍になります。
Sam

13
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;

public class test {

public static class Person {
    public String name;
    public int id;
    public Date hireDate;

    public Person(String iname, int iid, Date ihireDate) {
        name = iname;
        id = iid;
        hireDate = ihireDate;
    }

    public String toString() {
        return name + " " + id + " " + hireDate.toString();
    }

    // Comparator
    public static class CompId implements Comparator<Person> {
        @Override
        public int compare(Person arg0, Person arg1) {
            return arg0.id - arg1.id;
        }
    }

    public static class CompDate implements Comparator<Person> {
        private int mod = 1;
        public CompDate(boolean desc) {
            if (desc) mod =-1;
        }
        @Override
        public int compare(Person arg0, Person arg1) {
            return mod*arg0.hireDate.compareTo(arg1.hireDate);
        }
    }
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    SimpleDateFormat df = new SimpleDateFormat("mm-dd-yyyy");
    ArrayList<Person> people;
    people = new ArrayList<Person>();
    try {
        people.add(new Person("Joe", 92422, df.parse("12-12-2010")));
        people.add(new Person("Joef", 24122, df.parse("1-12-2010")));
        people.add(new Person("Joee", 24922, df.parse("12-2-2010")));
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    Collections.sort(people, new Person.CompId());
    System.out.println("BY ID");
    for (Person p : people) {
        System.out.println(p.toString());
    }

    Collections.sort(people, new Person.CompDate(false));
    System.out.println("BY Date asc");
    for (Person p : people) {
        System.out.println(p.toString());
    }
    Collections.sort(people, new Person.CompDate(true));
    System.out.println("BY Date desc");
    for (Person p : people) {
        System.out.println(p.toString());
    }

}

}

7
Stackoverflowへようこそ。この質問は少し前に回答されました。古いスレッドを復活させる前に、あなたの応答がスレッドに重要な何かを追加していることを確認してください。
リー2012

1
回答に説明を追加してください。
Arashsoft 2016

コンパイルして実行するだけです。コードはコメントと説明です。
CharlesW 2016

9

JAVA 8の最も簡単な方法は、英語のアルファベット順です。

クラスの実装

public class NewspaperClass implements Comparable<NewspaperClass>{
   public String name;

   @Override
   public int compareTo(NewspaperClass another) {
      return name.compareTo(another.name);
   }
}

ソート

  Collections.sort(Your List);

英語以外の文字を含むアルファベットをソートする場合は、ロケールを使用できます...コードの下でトルコ語の文字ソートを使用してください...

クラスの実装

public class NewspaperClass implements Comparator<NewspaperClass> {
   public String name;
   public Boolean isUserNewspaper=false;
   private Collator trCollator = Collator.getInstance(new Locale("tr_TR"));



   @Override
   public int compare(NewspaperClass lhs, NewspaperClass rhs) {
      trCollator.setStrength(Collator.PRIMARY);
      return trCollator.compare(lhs.name,rhs.name);
   }
}

ソート

Collections.sort(your array list,new NewspaperClass());

9

関数とメソッドのリファレンス

このCollections.sortメソッドはListComparatorパスを使用してソートできます。これComparatorは、必要に応じComparator.comparingメソッド参照を渡すことができるメソッドを使用して実装できますFunction。幸いなことに、実際のコードはこの説明よりもはるかに単純で短くなっています。

Java 8の場合:

Collections.sort(list, comparing(ClassName::getName));

または

Collections.sort(list, comparing(ClassName::getName).reversed());

別の方法は

Collections.sort(list, comparing(ClassName::getName, Comparator.nullsLast(Comparator.naturalOrder())));

1
呼び出しにはAPIレベル24が必要です
akshay bhange


6

Java 8 Lambdaはソートを短縮します。

Collections.sort(stdList, (o1, o2) -> o1.getName().compareTo(o2.getName()));

2
Collections.sort(stdList, Comparator.comparing(SomeClass::getName));
bcsb1001 2015年


5

はい、たとえばこの回答では可能です。vクラスのプロパティで並べ替えますIndexValue

    // Sorting by property v using a custom comparator.
    Arrays.sort( array, new Comparator<IndexValue>(){
        public int compare( IndexValue a, IndexValue b ){
            return a.v - b.v;
        }
    });

ここで気付いたら、匿名の内部クラス(クロージャのJava)を作成し、それをsortクラスのメソッドに直接渡します。Arrays

オブジェクトは実装することもできますComparable(これがStringとJavaのほとんどのコアライブラリが実行します)が、それ自体がクラスの「自然なソート順」を定義し、新しいものをプラグインすることができません。


1
...しかし、これはComparator:)でオーバーライドすることができます
BalusC

5

これらの回答のすべてではないにしても、ほとんどの場合、基になるクラス(オブジェクト)に依存して、同等のものを実装するか、ヘルパーに匹敵するインターフェイスを持っています。

私の解決策ではありません!次のコードでは、文字列名を知ることでオブジェクトのフィールドを比較できます。名前を使用しないように簡単に変更できますが、それを公開するか、比較するオブジェクトの1つを作成する必要があります。

Collections.sort(anArrayListOfSomeObjectPerhapsUsersOrSomething, new ReflectiveComparator(). new ListComparator("name"));

public class ReflectiveComparator {
    public class FieldComparator implements Comparator<Object> {
        private String fieldName;

        public FieldComparator(String fieldName){
            this.fieldName = fieldName;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public int compare(Object object1, Object object2) {
            try {
                Field field = object1.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);

                Comparable object1FieldValue = (Comparable) field.get(object1);
                Comparable object2FieldValue = (Comparable) field.get(object2);

                return object1FieldValue.compareTo(object2FieldValue);
            }catch (Exception e){}

            return 0;
        }
    }

    public class ListComparator implements Comparator<Object> {
        private String fieldName;

        public ListComparator(String fieldName) {
            this.fieldName = fieldName;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public int compare(Object object1, Object object2) {
            try {
                Field field = object1.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);
                Comparable o1FieldValue = (Comparable) field.get(object1);
                Comparable o2FieldValue = (Comparable) field.get(object2);

                if (o1FieldValue == null){ return -1;}
                if (o2FieldValue == null){ return 1;}
                return o1FieldValue.compareTo(o2FieldValue);
            } catch (NoSuchFieldException e) {
                throw new IllegalStateException("Field doesn't exist", e);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Field inaccessible", e);
            }
        }
    }
}

5

グアバ注文を試すことができます:

Function<Item, Date> getStartDate = new Function<Item, Date>() {
    public Date apply(Item item) {
        return item.getStartDate();
    }
};

List<Item> orderedItems = Ordering.natural().onResultOf(getStartDate).
                          sortedCopy(items);

5

あなたはJava 8を使用して並べ替えることができます

yourList.sort(Comparator.comparing(Classname::getName));

or

yourList.stream().forEach(a -> a.getBObjects().sort(Comparator.comparing(Classname::getValue)));

3

このコードスニペットは役に立つかもしれません。私の場合、オブジェクトを並べ替える場合は、VolumeNameで並べ替えます。

public List<Volume> getSortedVolumes() throws SystemException {
    List<Volume> volumes = VolumeLocalServiceUtil.getAllVolumes();
    Collections.sort(volumes, new Comparator<Volume>() {
        public int compare(Volume o1, Volume o2) {
            Volume p1 = (Volume) o1;
            Volume p2 = (Volume) o2;
            return p1.getVolumeName().compareToIgnoreCase(
                    p2.getVolumeName());
        }
    });
    return volumes;
}

これは機能します。私は自分のjspでそれを使用しています。


3

このライブラリを使用すると、カスタムオブジェクトのリストを複数の列でソートできます。ライブラリはバージョン8.0の機能を使用します。サンプルもご用意しています。これが行うサンプルです

SortKeys sortKeys = new SortKeys();
sortKeys.addField("firstName")
            .addField("age", true); // This (true) will sort the age descending

// Other ways to specify a property to the sorter are
//      .addField("lastName", String.class);
//      .addField("dob", Date.class, true);

// Instantiate a ListSorter
ListSorter listSorter = new ListSorter();

// Pass the data to sort (listToSort) and the "by keys" to sort (sortKeys)
List sortedList = (List<Person>) listSorter.sortList(listToSort, sortKeys);

3

このプレゼンテーションは、2016年にドイツのシュトゥットガルトで開催されるJavaフォーラムで見ることができます。

少数のスライドのみがドイツ語を使用しており、コンテンツの99%は「英語ベース」のJavaソースコードです。お気に入り

someCollection.sort(
  OurCustomComparator
    .comparing(Person::getName)
    .thenComparing(Person::getId)
);

OurCustomComparator使用されるデフォルトの方法(および他の興味深いアイデア)。示されているように、ソート用のゲッターメソッドを選択する非常に簡潔なコードにつながります。ソート基準の超シンプルなチェーン(または逆)。

java8に慣れている場合は、始めるための資料がたくさんあります。


3

1.8以降の新機能は、Collection.sort()を使用する代わりにList.sort()メソッドであるため、mylistcontainer.sort()を直接呼び出す

以下は、List.sort()機能を示すコードスニペットです。

List<Fruit> fruits = new ArrayList<Fruit>();
fruits.add(new Fruit("Kiwi","green",40));
fruits.add(new Fruit("Banana","yellow",100));
fruits.add(new Fruit("Apple","mixed green,red",120));
fruits.add(new Fruit("Cherry","red",10));

// a) using an existing compareto() method
fruits.sort((Fruit f1,Fruit f2) -> f1.getFruitName().compareTo(f2.getFruitName()));
System.out.println("Using String.compareTo(): " + fruits);
//Using String.compareTo(): [Apple is: mixed green,red, Banana is: yellow, Cherry is: red, Kiwi is: green]

// b) Using a comparable class
fruits.sort((Fruit f1,Fruit f2) -> f1.compareTo(f2));  
System.out.println("Using a Comparable Fruit class (sort by color): " + fruits);
// Using a Comparable Fruit class (sort by color): [Kiwi is green, Apple is: mixed green,red, Cherry is: red, Banana is: yellow]

Fruitクラスは次のとおりです。

public class Fruit implements Comparable<Fruit>
{
    private String name;
    private String color;
    private int quantity;

    public Fruit(String name,String color,int quantity)
    { this.name = name; this.color = color; this.quantity = quantity; }

    public String getFruitName() { return name; }        
    public String getColor() { return color; }  
    public int getQuantity() { return quantity; }

    @Override public final int compareTo(Fruit f) // sorting the color
    {
        return this.color.compareTo(f.color);
    }     
    @Override public String toString()
    {   
        return (name + " is: " + color);
    }
} // end of Fruit class   

2

customComparatorクラスを使用するには、java.util.Comparatorを実装する必要があります。また、compare()AND equals()もオーバーライドする必要があります。

compare()は質問に答える必要があります。オブジェクト1はオブジェクト2より小さいか、等しいか、大きいですか?

完全なドキュメント:http : //java.sun.com/j2se/1.5.0/docs/api/java/util/Comparator.html


1

私はこのプロセスを好みます:

public class SortUtil
{    
    public static <T> List<T> sort(List<T> list, String sortByProperty)
    {
            Collections.sort(list, new BeanComparator(sortByProperty));
            return list;
    }
}

List<T> sortedList = SortUtil<T>.sort(unsortedList, "startDate");

オブジェクトのリストにというプロパティがある場合startDate、use thisを繰り返し呼び出します。それらを連鎖させることもできますstartDate.time

これがあるためにあなたのオブジェクトを必要としComparable、あなたが必要とするどの手段compareToequalsおよびhashCode実装を。

はい、それはより速くなる可能性があります...しかし、今では、ソートの種類ごとに新しいコンパレータを作成する必要はありません。開発時間を節約して実行時間をあきらめることができる場合は、これで十分です。


3
1、この回答は2時間前に提供され、動作するコードも提供されました。特にBeanComparatorは標準クラスではないため、同じソリューションを再投稿してフォーラムを散らかす必要はありません。したがって、投稿者があなたが話していることを知らない場合、それは実際にはソリューションではありません。元の提案が気に入ったら、賛成投票して、必要に応じてコメントを追加できます。
camickr

0

Javaの8の使用を使用すると、定義することができますComparator使用して1行にComparator.comparing()

次のいずれかの方法を使用します。

オプション1:

listToBeSorted.sort(Comparator.comparing(CustomObject::getStartDate));

オプション2:

Collections.sort(listToBeSorted, Comparator.comparing(CustomObject::getStartDate));

1
3年前のこの回答の複製のようです。
バジルブルク2018

1
厳密には、ここではオプション2のみが、前述の回答のオプション1と同様です。2つの異なる回答が同じ質問に対する回答である場合、少なくともいくつかの類似点がある可能性があると思います。
Sahil Chhabra

0

カスタムクラスは、CompareToメソッドの実装を必要とする「Comparable」インターフェイスを実装できます。CompareToメソッドでは、オブジェクトが他のオブジェクトよりも小さい、または大きいという意味を定義できます。したがって、例では、次のようになります。

public class MyCustomClass implements Comparable<MyCustomClass>{

..........

 @Override
public int compareTo(MyCustomClass a) {
    if(this.getStartDate().before(a.getStartDate())){
        return -1;
    }else if(a.getStartDate().before(this.getStartDate())){
        return 1;
    }else {
        return 0;
    }
}

負の数は、これが比較対象のオブジェクトよりも小さいことを示します。正の数値は、これがオブジェクトと比較して大きいことを示し、ゼロはオブジェクトが等しいことを意味します。

次に、collections.sort(myList)を使用して、コンパレーターをフィードせずにリストをソートできます。このメソッドには、TreeSetやTreeMapなどのソートされたコレクションデータ構造を使用する場合に、自動的にソートされるという利点もあります。

Comparableインターフェースの詳細を読みたい場合は、この記事をチェックできます(開示:私は作成者です;)) https://nullbeans.com/the-java-comparable-interface-automatic-sort-of-collections/


0

並べ替える(ネストされた)プロパティへのStringプロパティパスのみがある場合は、Springs PropertyComparatorを使用することもできます。

List<SomeObject> list = ...;
PropertyComparator<HitWithInfo> propertyComparator = new PropertyComparator<>(
    "property.nested.myProperty", false, true);
list.sort(propertyComparator);

欠点は、このコンパレーターが存在しないプロパティまたはアクセスできないプロパティを暗黙的に無視し、比較のためにこれをnull値として処理することです。つまり、このようなコンパレータを慎重にテストするか、プロパティパスの存在を何らかの方法で検証する必要があります。


0

java-8ストリームAPIを使用すると、次の基準でソートできますArrayList

 Comparator<Person> birthdayComparator = Comparator.comparing(Person::getBirthday);
 List<Person> sortedList = list.stream().sorted(birthdayComparator).collect(toList());

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