備忘録

備忘録

Androidで複数キーでソートする

Ⅰ. はじめに

Java8で追加されたStream APIとComparatorを使ったソートはAndroid N (Android 7.0)以上でなければ動作しません。
Android N 以下の場合はlambdaで例外が発生しアプリが強制終了します。

Ⅱ. 使うライブラリ

Lightweight-Stream-API を使います。
Android 4.4.4で動作確認済みです。

略称はLSAだそうです。(ライブラリ作者がReadmeでLSAと書いている)

LSAはStream APIではできない書き方ができるので便利です。

例えば、以下のようにメソッドの評価結果でソートが可能です。

persons = persons
       .sorted(theComparing(comparing(x -> x.getSomething(argSomething))))
       .toList();

ラムダ式を使ったこの書き方はStream APIでも1つだけのソートであれば可能ですが、
複数キーでソートする場合は不可能です。
なんとも中途半端な作りですね・・・

// OK
persons.sort(comparing(x -> x.name));
// OK
persons.sort(comparing(x -> x.getName()));
// NG
persons.sort(comparing(x -> x.name).thenComparing(x -> x.age));
// NG
persons.sort(comparing(x -> x.getName()).thenComparing(x -> x.getAge()));

また、ixJavaなど他のライブラリもありましたが、複数キーでソートするにはLSAが最適でした。
(ixJavaはそもそも複数キーソートに対応していない)

Ⅲ. プログラム

import java.util.ArrayList;
import java.util.List;

public class Main {

  static class Person {

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

    private String name;
    public String getName() {
      return name;
    }

    private Integer age;
    public Integer getAge() {
      return age;
    }
  }

  public static void main(String[] args) {

    List<Person> persons = new ArrayList<>();

    persons.add(new Person("a", 1));
    persons.add(new Person("b", 4));
    persons.add(new Person("b", 2));
    persons.add(new Person("c", 3));

    // Stream API
    // Android 7.0未満では動きません
    // import static java.util.Comparator.*;
    // persons.sort(comparing(Person::getName, reverseOrder()).thenComparing(Person::getAge));

    // Lightweight-Stream-API
    // Android 4.4.4で動作確認済み
    // import static com.annimon.stream.ComparatorCompat.*;
    persons = Stream.of(persons)
           .sorted(thenComparing(comparing(x -> x.name, reverseOrder()), comparing(x -> x.age)))
           .toList();

    // または
    //persons = Stream.of(persons)
    //       .sorted(thenComparing(comparing(Person::getName, reverseOrder()), comparing(Person::getAge)))
    //       .toList();

    for(Person p : persons) {
      System.out.println(p.name + p.age);
    }

  }
}

Ⅳ. 出力結果

c3
b2
b4
a1