1. 序章

このチュートリアルでは、Javaでリストを並べ替えするさまざまな方法をアルファベット順に説明します。

まず、 Collections クラスから始めて、コンパレータインターフェイスを使用します。 また、ListのAPIを使用してアルファベット順に並べ替え、次にストリームを使用し、最後にTreeSet。を使用します。

さらに、例を拡張して、固有のロケールに基づくリストの並べ替え、 アクセント付きリストの並べ替え、 RuleBasedCollator の使用など、いくつかの異なるシナリオを検討します。カスタムの並べ替えルールを定義します。

2. コレクションクラスを使用した並べ替え

まず、Collectionsクラスを使用してリストを並べ替える方法を見てみましょう。

Collectionsクラスは、静的なオーバーロードされたメソッドsort を提供します。このメソッドは、リストを取得して自然な順序で並べ替えることができます。または、コンパレータを使用してカスタムの並べ替えロジックを提供できます。

2.1. 自然/辞書式順序での並べ替え

まず、入力リストを定義します。

private static List<String> INPUT_NAMES = Arrays.asList("john", "mike", "usmon", "ken", "harry");

次に、最初に Collections クラスを使用して、辞書式順序とも呼ばれる自然な順序でリストを並べ替えます。

@Test
void givenListOfStrings_whenUsingCollections_thenListIsSorted() {
    Collections.sort(INPUT_NAMES);
    assertThat(INPUT_NAMES).isEqualTo(EXPECTED_NATURAL_ORDER);
}

ここで、EXPECTED_NATURAL_ORDERは次のとおりです。

private static List<String> EXPECTED_NATURAL_ORDER = Arrays.asList("harry", "john", "ken", "mike", "usmon");

ここで注意すべきいくつかの重要なポイントは次のとおりです。

  • リストは、要素の自然順序に従って昇順で並べ替えられます
  • 要素がComparableであるCollectionからsortメソッドに渡すことができます( Compareable インターフェイスを実装します)
  • ここでは、ComparableクラスであるListof Stringクラスを渡しているため、並べ替えが機能します
  • Collectionクラスは、sortに渡されるListオブジェクトの状態を変更します。 したがって、リストを返す必要はありません

2.2. 逆順で並べ替え

同じリストをアルファベットの逆順に並べ替える方法を見てみましょう。

sort メソッドをもう一度使用してみましょうが、コンパレータを提供します。

Comparator<String> reverseComparator = (first, second) -> second.compareTo(first);

または、コンパレータインターフェイスからこの静的メソッドを使用することもできます。

Comparator<String> reverseComparator = Comparator.reverseOrder();

逆コンパレータを入手したら、それをsortに渡すだけです。

@Test
void givenListOfStrings_whenUsingCollections_thenListIsSortedInReverse() {
    Comparator<String> reverseComparator = Comparator.reverseOrder();
    Collections.sort(INPUT_NAMES, reverseComparator); 
    assertThat(INPUT_NAMES).isEqualTo(EXPECTED_REVERSE_ORDER); 
}

ここで、EXPECTED_REVERSE_ORDERは次のとおりです。

private static List<String> EXPECTED_REVERSE_ORDER = Arrays.asList("usmon", "mike", "ken", "john", "harry");

これからのいくつかの関連するポイントは次のとおりです。

  • String クラスはfinalであるため、ComparisonインターフェイスのcompareToメソッドを拡張してオーバーライドして逆ソートすることはできません。
  • コンパレータインターフェイスを使用して、降順のアルファベット順に並べ替えるカスタマイズされた並べ替え戦略を実装できます。
  • コンパレータは関数型インターフェースであるため、ラムダ式を使用できます

3. コンパレータインターフェースを使用したカスタムソート

多くの場合、カスタムの並べ替えロジックが必要な文字列のリストを並べ替える必要があります。 そのとき、コンパレータインターフェイスを実装し、必要な並べ替え基準を提供します。

3.1. コンパレータは大文字と小文字の文字列でリストをソートします

カスタムソートを呼び出すことができる典型的なシナリオは、大文字と小文字で始まる文字列の混合リストです。

このシナリオを設定してテストしてみましょう。

@Test
void givenListOfStringsWithUpperAndLowerCaseMixed_whenCustomComparator_thenListIsSortedCorrectly() {
    List<String> movieNames = Arrays.asList("amazing SpiderMan", "Godzilla", "Sing", "Minions");
    List<String> naturalSortOrder = Arrays.asList("Godzilla", "Minions", "Sing", "amazing SpiderMan");
    List<String> comparatorSortOrder = Arrays.asList("amazing SpiderMan", "Godzilla", "Minions", "Sing");

    Collections.sort(movieNames);
    assertThat(movieNames).isEqualTo(naturalSortOrder);

    Collections.sort(movieNames, Comparator.comparing(s -> s.toLowerCase()));
    assertThat(movieNames).isEqualTo(comparatorSortOrder);
}

ここに注意してください:

  • コンパレータの前の並べ替えの結果は正しくありません: [“Godzilla、” Minions “” Sing “、”amazing SpiderMan “]
  • String :: toLowerCase を抽出する主要な抽出機能です同程度のタイプからキーを並べ替えるとを返しますコンパレータ
  • カスタムコンパレータでソートした後の正しい結果は次のようになります: [“amazing SpiderMan”、 “Godzilla”、 “Minions”、 “Sing”]

3.2. コンパレータは特殊文字をソートします

特殊文字「@」で始まる名前を持つリストの別の例を考えてみましょう。

リストの最後で並べ替えて、残りは自然な順序で並べ替える必要があります。

@Test
void givenListOfStringsIncludingSomeWithSpecialCharacter_whenCustomComparator_thenListIsSortedWithSpecialCharacterLast() {
    List<String> listWithSpecialCharacters = Arrays.asList("@laska", "blah", "jo", "@sk", "foo");

    List<String> sortedNaturalOrder = Arrays.asList("@laska", "@sk", "blah", "foo", "jo");
    List<String> sortedSpecialCharacterLast = Arrays.asList("blah", "foo", "jo", "@laska", "@sk");

    Collections.sort(listWithSpecialCharacters);
    assertThat(listWithSpecialCharacters).isEqualTo(sortedNaturalOrder);

    Comparator<String> specialSignComparator = Comparator.<String, Boolean>comparing(s -> s.startsWith("@"));
    Comparator<String> specialCharacterComparator = specialSignComparator.thenComparing(Comparator.naturalOrder());

    listWithSpecialCharacters.sort(specialCharacterComparator);
    assertThat(listWithSpecialCharacters).isEqualTo(sortedSpecialCharacterLast);
}

最後に、いくつかの重要なポイントは次のとおりです。

  • コンパレータを使用しないソートの出力は次のとおりです。[“ @ alaska”、“ @ ask”、“ blah”、“ foo”、“ jo”] 私たちのシナリオ
  • 以前と同様に、タイプStringからComparableソートキーを抽出し、specialCharacterLastComparatorを返すキーエクストラクターがあります。
  • specialCharacterLastComparator をチェーンして、を使用して2次ソートを実行し、別のコンパレータを期待して比較することができます。
  • Comparator.naturalOrder は、Compareableオブジェクトを自然な順序で比較します。
  • ソートのコンパレータの後の最終出力は次のとおりです。[“blah”、 “foo”、 “jo”、 “@ alaska”、 “@ ask”]

4. ストリームを使用した並べ替え

それでは、 Java 8 Streams を使用して、Stringのリストを並べ替えましょう。

4.1. 自然な順序で並べ替え

最初に自然な順序で並べ替えましょう。

@Test
void givenListOfStrings_whenSortWithStreams_thenListIsSortedInNaturalOrder() {
    List<String> sortedList = INPUT_NAMES.stream()
      .sorted()
      .collect(Collectors.toList());

    assertThat(sortedList).isEqualTo(EXPECTED_NATURAL_ORDER);
}

重要なのは、ここで sorted メソッドは、自然な順序に従ってソートされた文字列のストリームを返すことです。

さらに、このストリームの要素は比較可能です

4.2. 逆順で並べ替え

次に、コンパレータ sorted に渡します。これは、逆ソート戦略を定義します。

@Test
void givenListOfStrings_whenSortWithStreamsUsingComparator_thenListIsSortedInReverseOrder() {
    List<String> sortedList = INPUT_NAMES.stream()
      .sorted((element1, element2) -> element2.compareTo(element1))
      .collect(Collectors.toList());
    assertThat(sortedList).isEqualTo(EXPECTED_REVERSE_ORDER);
}

ここでは、Lamda関数を使用してコンパレータを定義していることに注意してください。

または、同じコンパレータを入手することもできます。

Comparator<String> reverseOrderComparator = Comparator.reverseOrder();

次に、このreverseOrderComparatorsortedに渡すだけです。

List<String> sortedList = INPUT_NAMES.stream()
  .sorted(reverseOrder)
  .collect(Collectors.toList());

5. TreeSetを使用した並べ替え

TreeSetは、 Compareable インターフェイスを使用して、オブジェクトを並べ替えられた昇順で格納します。

ソートされていないリストをTreeSetに変換し、それを List:として収集するだけです。

@Test
void givenNames_whenUsingTreeSet_thenListIsSorted() {
    SortedSet<String> sortedSet = new TreeSet<>(INPUT_NAMES);
    List<String> sortedList = new ArrayList<>(sortedSet);
    assertThat(sortedList).isEqualTo(EXPECTED_NATURAL_ORDER);
}

このアプローチの制約は、元のリストに、ソートされたリストに保持したい重複する値が含まれていてはならないということですt。

6. リストsortを使用して並べ替える

Listsortメソッドを使用してリストを並べ替えることもできます。

@Test
void givenListOfStrings_whenSortOnList_thenListIsSorted() {
    INPUT_NAMES.sort(Comparator.reverseOrder());
    assertThat(INPUT_NAMES).isEqualTo(EXPECTED_REVERSE_ORDER);
}

sortメソッドは、指定されたComparatorによって指定された順序に従ってこのリストをソートすることに注意してください。

7. ロケールに敏感なリストの並べ替え

ロケールによっては、言語規則により、アルファベット順の並べ替えの結果が異なる場合があります。

StringListの例を見てみましょう。

 List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");

最初に通常どおりに並べ替えましょう。

 Collections.sort(accentedStrings);

出力は次のようになります。[「cosas」、「fútbol」、「árbol」、「único」]。

ただし、特定の言語ルールを使用してソートする必要があります。

特定のロケールでコレーターのインスタンスを作成しましょう

Collator esCollator = Collator.getInstance(new Locale("es"));

ここでは、esロケールを使用してCollatorのインスタンスを作成したことに注意してください。

次に、このコレーターをコンパレータとして渡すことができます。これは、リストコレクションでの並べ替え、またはストリーム:の使用に使用されます。

accentedStrings.sort((s1, s2) -> {
    return esCollator.compare(s1, s2);
});

並べ替えの結果は次のようになります: [árbol、cosas、fútbol、único]。

最後に、これをすべて一緒に示しましょう。

@Test
void givenListOfStringsWithAccent_whenSortWithTheCollator_thenListIsSorted() {
    List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");
    List<String> sortedNaturalOrder = Arrays.asList("cosas", "fútbol", "árbol", "único");
    List<String> sortedLocaleSensitive = Arrays.asList("árbol", "cosas", "fútbol", "único");

    Collections.sort(accentedStrings);
    assertThat(accentedStrings).isEqualTo(sortedNaturalOrder);

    Collator esCollator = Collator.getInstance(new Locale("es"));

    accentedStrings.sort((s1, s2) -> {
        return esCollator.compare(s1, s2);
    });

    assertThat(accentedStrings).isEqualTo(sortedLocaleSensitive);
}

8. アクセント付き文字を使用したソートリスト

アクセントやその他の装飾が施された文字は、Unicodeでいくつかの異なる方法でエンコードできるため、異なる方法で並べ替えることができます。

ソートする前にアクセント付き文字を正規化するか、文字からアクセントを削除することができます。

アクセント付きリストを並べ替えるこれらの方法の両方を調べてみましょう。

8.1. アクセント付きリストの正規化と並べ替え

このような文字列のリストを正確に並べ替えるために、 java.text.Normalizerを使用してアクセント付き文字を正規化します。

アクセントのある文字列のリストから始めましょう。

List<String> accentedStrings = Arrays.asList("único","árbol", "cosas", "fútbol");

次に、 Normalize 関数を使用してコンパレータを定義し、それをsortメソッドに渡します。

Collections.sort(accentedStrings, (o1, o2) -> {
    o1 = Normalizer.normalize(o1, Normalizer.Form.NFD);
    o2 = Normalizer.normalize(o2, Normalizer.Form.NFD);
    return o1.compareTo(o2);
});

正規化後のソート済みリストは次のようになります: [árbol、cosas、fútbol、único]

重要なのは、ここではフォームNormalizer.Form.NFDを使用してデータを正規化することです。アクセント付き文字には正規分解を使用します。 正規化に使用できる他の形式がいくつかあります。

8.2. アクセントを取り除き、並べ替える

コンパレータStringUtilsstripAccents メソッドを使用して、sortメソッドに渡します。

@Test
void givenListOfStrinsWithAccent_whenComparatorWithNormalizer_thenListIsNormalizedAndSorted() {
    List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");

    List<String> naturalOrderSorted = Arrays.asList("cosas", "fútbol", "árbol", "único");
    List<String> stripAccentSorted = Arrays.asList("árbol","cosas", "fútbol","único");

    Collections.sort(accentedStrings);
    assertThat(accentedStrings).isEqualTo(naturalOrderSorted);
    Collections.sort(accentedStrings, Comparator.comparing(input -> StringUtils.stripAccents(input)));
    assertThat(accentedStrings).isEqualTostripAccentSorted); 
}

9. ルールベースのコレーターを使用した並べ替え

javatext.RuleBasedCollator。を使用してアルファベット順に並べ替えるカスタムルールを定義することもできます。

ここで、カスタムの並べ替えルールを定義して、RuleBasedCollatorに渡します。

@Test
void givenListofStrings_whenUsingRuleBasedCollator_then ListIsSortedUsingRuleBasedCollator() throws ParseException {
    List<String> movieNames = Arrays.asList(
      "Godzilla","AmazingSpiderMan","Smurfs", "Minions");

    List<String> naturalOrderExpected = Arrays.asList(
      "AmazingSpiderMan", "Godzilla", "Minions", "Smurfs");
    Collections.sort(movieNames);

    List<String> rulesBasedExpected = Arrays.asList(
      "Smurfs", "Minions", "AmazingSpiderMan", "Godzilla");

    assertThat(movieNames).isEqualTo(naturalOrderExpected);

    String rule = "< s, S < m, M < a, A < g, G";

    RuleBasedCollator rulesCollator = new RuleBasedCollator(rule);
    movieNames.sort(rulesCollator);

    assertThat(movieNames).isEqualTo(rulesBasedExpected);
}

考慮すべき重要なポイントのいくつかは次のとおりです。

  • RuleBasedCollator は、文字をソートキーにマップします
  • 上で定義されたルールは形式ですしかし、他にもありますフォームルールも
  • ルール定義に従って、文字 sはmより小さく、Mはaより小さく、Aはgより小さくなります。
  • ルールのビルドプロセスが失敗した場合、フォーマット例外がスローされます
  • RuleBasedCollatorはComparatorインターフェースを実装しているため、sortに渡すことができます。

10. 結論

この記事では、javaでリストをアルファベット順に並べ替えるためのさまざまな手法について説明しました。

最初にコレクションを使用し、次にコンパレータインターフェイスをいくつかの一般的な例で説明しました。

コンパレータの後、リスト sort メソッド、続いてTreeSetを調べました。

また、 String のリストを別のロケールで並べ替えたり、アクセント付きリストを正規化して並べ替えたり、RuleBasedCollatorをカスタムルールで使用したりする方法についても説明しました。

いつものように、この記事の完全なソースコードは、GitHubにあります。