1. ホーム
  2. java

[解決済み] ヌルセーフなcompareTo()の実装を簡略化する方法は?

2022-04-20 05:59:33

質問

を実装しています。 compareTo() メソッドを、このような単純なクラスで使用することができます。 Collections.sort() といった、Javaプラットフォームが提供する便利な機能があります)。

public class Metadata implements Comparable<Metadata> {
    private String name;
    private String value;

// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}

が欲しい。 自然な順序 これらのオブジェクトは、1) 名前でソートされ、2) 名前が同じ場合は値でソートされます。どちらのフィールドもNULL値は完全に許容されます。 compareTo は、これらのケースでブレークしてはならない。

思いつく解決策は、次のようなものです(他の人はシングルリターンポイントを好むかもしれませんが、私はここで"ガード節"を使用しています、それは重要なことではありません)。

// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(Metadata other) {
    if (this.name == null && other.name != null){
        return -1;
    }
    else if (this.name != null && other.name == null){
        return 1;
    }
    else if (this.name != null && other.name != null) {
        int result = this.name.compareToIgnoreCase(other.name);
        if (result != 0){
            return result;
        }
    }

    if (this.value == null) {
        return other.value == null ? 0 : -1;
    }
    if (other.value == null){
        return 1;
    }

    return this.value.compareToIgnoreCase(other.value);
}

しかし、私はこのコードに完全に満足しているわけではありません。確かに、これは 非常に 複雑ですが、かなり冗長で退屈な作業です。

という質問があります。 これをより冗長でなくするにはどうしたらいいか (機能を維持したまま)どうでしょうか?Java標準ライブラリやApache Commonsが参考になるのであれば、遠慮なく参照してください。これを(少し)単純化する唯一の選択肢は、私自身の "NullSafeStringComparator" を実装し、両方のフィールドの比較にそれを適用することでしょうか?

編集 1-3 : Eddieの言うとおり、上記のquot;両方の名前がnull"のケースを修正しました。

採用された回答について

2009年に、もちろんJava 1.6でこの質問をしたのですが、その時は Eddieによる純粋なJDKの解決策 は、私が好んで受け入れた答えでした。私は今(2017年)まで、それを変更することができませんでした。

もあります。 サードパーティライブラリソリューション -2009年のApache Commons Collectionsと2013年のGuavaは、私が投稿したものです。

私は今、きれいな Java 8ソリューション by Lukasz Wiktor という答えが返ってきました。Java 8であれば、それは間違いなく好ましいことで、最近ではJava 8はほとんどすべてのプロジェクトで利用できるはずです。

どのように解決するのですか?

使用方法 Java 8 :

private static Comparator<String> nullSafeStringComparator = Comparator
        .nullsFirst(String::compareToIgnoreCase); 

private static Comparator<Metadata> metadataComparator = Comparator
        .comparing(Metadata::getName, nullSafeStringComparator)
        .thenComparing(Metadata::getValue, nullSafeStringComparator);

public int compareTo(Metadata that) {
    return metadataComparator.compare(this, that);
}