search
LoginSignup
30

More than 3 years have passed since last update.

posted at

updated at

JavaのStreamを使って複数条件でソートする

最近、業務で半年ぶりくらいにJavaを書いている。
以前業務で使っていたのはJava7(!)だったのだが、現在Java8を使用している。

streamを結構な頻度で使用しており、先日複数条件でソートできることを知ったため書き留め。

Java Stream API

Stream (Java Platform SE 8 )

反復処理や並行処理等、集約操作をサポートするAPI。

ラムダ式(JSでいうアロー関数)と組み合わせることで、拡張for構文等を使うことなくシンプルに記述できる。

複数条件によるソート

Comparatorインタフェースを使うことによって可能。

comparing()thenComparing()メソッドによって、ソート条件を指定し、streamsorted()に渡すことで、複数条件によるソートができる。

サンプルコード

設計の雑さについてはご容赦。

Articleクラス

Article.java
import java.time.LocalDate;

public class Article {

  /** 投稿者 */
  private String contributor;

  /** 投稿日 */
  private LocalDate date;

  /** いいね数 */
  private int goodCount;

  /** コンストラクタ */
  public Article(String contributor, LocalDate date, int goodCount) {
    this.contributor = contributor;
    this.date = date;
    this.goodCount = goodCount;
  }

  /** Getter, Setter */
  public String getContributor() {
    return this.contributor;
  }

  public void setContributor(String contributor) {
    this.contributor = contributor;
  }

  public LocalDate getDate() {
    return this.date;
  }

  public void setDate(LocalDate date) {
    this.date = date;
  }

  public int getGoodCount() {
    return this.goodCount;
  }

  public void setGoodCount(int goodCount) {
    this.goodCount = goodCount;
  }

}

Streamクラス

Stream.java
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

public class Stream {
  public static void main(String[] args) {
    // データ生成
    Article a1 = new Article("Taro", LocalDate.of(2018, 7, 11), 15);
    Article a2 = new Article("Taro", LocalDate.of(2015, 11, 1), 53);
    Article a3 = new Article("Taro", LocalDate.of(2015, 10, 31), 54);

    Article a4 = new Article("Hanako", LocalDate.of(2017, 2, 5), 53);
    Article a5 = new Article("Hanako", LocalDate.of(2016, 11, 1), 160);
    Article a6 = new Article("Hanako", LocalDate.of(2012, 8, 20), 22);

    Article a7 = new Article("Pochi", LocalDate.of(2013, 3, 16), 38);
    Article a8 = new Article("Pochi", LocalDate.of(2011, 9, 25), 200);
    Article a9 = new Article("Pochi", LocalDate.of(2018, 4, 13), 10);

    // リスト作成
    List<Article> list = Arrays.asList(a1, a2, a3, a4, a5, a6, a7, a8, a9);
    list.stream().forEach(a ->
      System.out.println(a.getContributor() + " " + a.getDate() + " " + a.getGoodCount()));

  }
}

とりあえず、a1からa9をリストにして出力すると

出力結果
Taro 2018-07-11 15
Taro 2015-11-01 53
Taro 2015-10-31 54
Hanako 2017-02-05 53
Hanako 2016-11-01 160
Hanako 2012-08-20 22
Pochi 2013-03-16 38
Pochi 2011-09-25 200
Pochi 2018-04-13 10

となる。

ソート処理

sorted (Java Platform SE 8)

Comparatorを引数に渡し、指定した順序でソートする。

単項目でのソート

辞書的に名前順でソート。

Stream.java
// Comparator作成
Comparator<Article> comparator = Comparator.comparing(Article::getContributor);
// 投稿者名でソート
list.stream().sorted(comparator)
  .forEach(a ->
    System.out.println(a.getContributor() + " " + a.getDate() + " " + a.getGoodCount()));
出力結果
Hanako 2017-02-05 53
Hanako 2016-11-01 160
Hanako 2012-08-20 22
Pochi 2013-03-16 38
Pochi 2011-09-25 200
Pochi 2018-04-13 10
Taro 2018-07-11 15
Taro 2015-11-01 53
Taro 2015-10-31 54

他の項目でもソート可能。

thenComparing

thenComparing (Java Platform SE 8)

比較条件をチェーンできるメソッド。
以下のように使用する。

comparator
Comparator.comparing(...).thenComparing(...).thenComparing(...);

前の条件が同じ(equals)だった場合に、更に比較を行う。

辞書式順序コンパレータをもう一方のコンパレータとともに返します。
このComparatorで2つの要素が等しい(つまり、compare(a, b) == 0)と見なされる場合は、otherを使ってその順序が決められます。

複数項目でのソート

名前 -> 投稿日でソート

Stream.java
// Comparator作成
Comparator<Article> comparator =
  Comparator.comparing(Article::getContributor).thenComparing(Article::getDate);
// ソート処理
list.stream().sorted(comparator)
  .forEach(a ->
    System.out.println(a.getContributor() + " " + a.getDate() + " " + a.getGoodCount()));
出力結果
Hanako 2012-08-20 22
Hanako 2016-11-01 160
Hanako 2017-02-05 53
Pochi 2011-09-25 200
Pochi 2013-03-16 38
Pochi 2018-04-13 10
Taro 2015-10-31 54
Taro 2015-11-01 53
Taro 2018-07-11 15

名前 -> いいね数でソート

Stream.java
// Comparator作成
Comparator<Article> comparator =
  Comparator.comparing(Article::getContributor).thenComparing(Article::getGoodCount);
// ソート処理
list.stream().sorted(comparator)
  .forEach(a ->
    System.out.println(a.getContributor() + " " + a.getDate() + " " + a.getGoodCount()));
出力結果
Hanako 2012-08-20 22
Hanako 2017-02-05 53
Hanako 2016-11-01 160
Pochi 2018-04-13 10
Pochi 2013-03-16 38
Pochi 2011-09-25 200
Taro 2018-07-11 15
Taro 2015-11-01 53
Taro 2015-10-31 54

昇順にしたい場合は、reversed()をつける

Stream.java
// Comparator作成
Comparator<Article> comparator =
  Comparator.comparing(Article::getContributor).thenComparing(Article::getGoodCount).reversed();
// ソート処理
list.stream().sorted(comparator)
  .forEach(a ->
    System.out.println(a.getContributor() + " " + a.getDate() + " " + a.getGoodCount()));
出力結果
Taro 2015-10-31 54
Taro 2015-11-01 53
Taro 2018-07-11 15
Pochi 2011-09-25 200
Pochi 2013-03-16 38
Pochi 2018-04-13 10
Hanako 2016-11-01 160
Hanako 2017-02-05 53
Hanako 2012-08-20 22

所感

  • 複数条件でのソートは意外と楽だった
  • Streamめちゃくちゃ便利(今更だけど)
  • Comparatorの使い方はまだまだ学ぶ必要がありそう
  • 昇順・降順等を細かく指定したい場合は、Comparator自体を分けて複数回ソートした方が良さそう

参考

Stream (Java Platform SE 8 )
Comparator (Java Platform SE 8 )

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
30