web-dev-qa-db-ja.com

null値のコンパレータ

座標間の距離に基づいて住所のリストを並べ替えるコードがあります。これは、カスタムコンパレータでcollections.sortを介して行われます。

ただし、座標のないアドレスがリストに含まれていると、NullPointerExceptionが発生することがあります。これを修正する私の最初のアイデアは、少なくとも1つの座標がnullであるアドレスに対して、コンパレータが距離として0を返すようにすることでした。これにより、リスト内の「有効な」要素の順序が破損する可能性があります。

コンパレータでnullデータの「0」値を返しますか、またはこれを解決するよりクリーンな方法がありますか?.

54
pvgoddijn

nullが無限に遠くを意味するように処理します。したがって:

  • comp(1234, null) == -1
  • comp(null, null) == 0
  • comp(null, 1234) == 1

これにより、一貫した順序を取得できます。

75
Sjoerd

WilliSchönbornの答えをさらに拡張するために、私はここに来て、 google-collections がまさにあなたが求めているものだと言いました。

一般的なケースでは、nullを無視する独自のComparatorを記述し(nullでないと仮定して重要なロジックに集中できる)、 Ordering を使用してヌル:

Collections.sort(addresses, Ordering.from(new AddressComparator()).nullsLast());

ただし、あなたの場合、ソートに使用されているのは住所(座標)内のデータですよね?この場合、google-collectionsは偶数moreに役立ちます。したがって、次のようなものがあります。

// Seems verbose at first glance, but you'll probably find yourself reusing 
// this a lot and it will pay off quickly.
private static final Function<Address, Coordinates> ADDRESS_TO_COORDINATES = 
  new Function<Address, Coordinates>() {
      public Coordinates apply(Address in) {
          return in.getCoordinates();
      }
  };

private static final Comparator<Coordinates> COORDINATE_SORTER = .... // existing

次に、ソートしたいとき:

Collections.sort(addresses,
    Ordering.from(COORDINATE_SORTER)
            .nullsLast()
            .onResultOf(ADDRESS_TO_COORDINATES));

それが、Googleコレクションの力が実を結び始めるところです。

23
Cowan

これについての私の考えは、null座標を「良くする」ためにあなたがやろうとすることは、ただひび割れの上に紙を張っているということです。本当に必要なのは、偽のnull座標を注入しているバグを見つけて修正することです。

私の経験では、NPEバグの侵入は、次の悪いコーディング習慣によって頻繁に引き起こされます。

  • 入力パラメータの不十分な検証、
  • nullを使用して、空の配列またはコレクションを作成しないようにします。
  • 例外がスローされるはずだったときにnullを返す、または
  • より良い解決策がある場合、「値なし」を表すためにnullを使用します。

(「値なし」問題のより良い解決策は通常、コードを書き直して、これを表現する必要がない必要がないか、またはnull以外を使用することです。代わりに値;たとえば、空の文字列、特別なインスタンス、予約された値。常により良い解決策を見つけることはできませんが、多くの場合見つけることができます。)

これがアプリケーションを説明している場合、NPEを隠す方法を考えるのではなく、コードの問題を根絶するために時間を費やす必要があります。

8
Stephen C

私の解決策(ここで見ている人に役立つかもしれません)は、通常の比較を行い、null値を0ではなく可能な最大値(たとえば、Integer.MAX_VALUE)で置き換えます。値自体が0である場合、0を返すことは一貫していません。ここに正しい例を示します。

        public int compare(YourObject lhs, YourObject rhs) {
            Integer l = Integer.MAX_VALUE;
            Integer r = Integer.MAX_VALUE;
            if (lhs != null) {
                l = lhs.giveMeSomeMeasure();
            }
            if (rhs != null) {
                r = rhs.giveMeSomeMeasure();
            }
            return l.compareTo(r);
        }

整数の最大値は不要であると付け加えました。それは、giveMeSomeMeasure()メソッドが何を返すことができるかに依存します。たとえば、天気の摂氏温度を比較する場合、nオブジェクトを設定する場所に応じて、リストの先頭または末尾にlとrを-300または+300に設定できます。

8
stoilkov

Java 8を使用している場合、Comparatorクラスに2つの新しい静的メソッドがあります。これらは便利です。

public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator)

比較はnullセーフであり、ソートされたシーケンスのどこにnull値を配置するかを選択できます。

次の例:

List<String> monkeyBusiness = Arrays.asList("Chimp", "eat", "sleep", "", null, "banana",
            "throw banana peel", null, "smile", "run");
Comparator<? super String> comparator = (a, b) -> a.compareTo(b);
monkeyBusiness.stream().sorted(Comparator.nullsFirst(comparator))
            .forEach(x -> System.out.print("[" + x + "] "));

[null] [null] [] [Chimp] [banana] [eat] [run] [sleep] [smile] [throw banana剥離]

4
Adrian Cosma

アドレスが等距離であり、本当に知らないことを意味するので、おそらく0を返したくないでしょう。これは、悪い入力データに対処しようとする非常に古典的な問題です。 dont know distanceの場合、アドレスが実際の用語でどれだけ離れているかを判断するのは、コンパレータの責任だとは思わない。ソートする前にリストからそれらのアドレスを削除します。

ハックは、リストの一番下にそれらを移動することです(しかし、それはいです!)

3
LenW

いいえ、クリーンな方法はありません。おそらく:

  • 比較された両方のオブジェクトの座標がnullの場合、0を返します
  • オブジェクトの1つの座標がnullの場合、-1/1を返します(最初の引数か2番目の引数かによって異なります)

しかし、もっと重要なのは、不足している座標を削除/埋めようとすることです。または、より良い方法は、不足している座標を持つアドレスをリストに入れないことです。

実際には、それらをリストに入れないことが最も論理的な動作です。それらをリストに入れた場合、結果は実際には距離順に並べられません。

座標が欠落しているアドレスを含む別のリストを作成し、その情報を必要とするユーザー(エンドユーザー、APIユーザー)に、最初のリストには必要なデータのアドレスのみが含まれ、2番目のリストには必要な情報が不足しています。

1
Bozho

私は個人的にコンパレータのどこでも特別なヌルケースを扱うのが嫌いなので、よりクリーンなソリューションを探していて、最終的にGoogleコレクションを見つけました。彼らの注文は素晴らしいです。それらは複合コンパレータをサポートし、ヌルを先頭と末尾にソートし、比較する前に特定の関数を実行できるようにします。コンパレータの作成はこれまでになく簡単になりました。試してみてください。

1
whiskeysierra

これをコンパレータの技術的な問題のように見るのではなく、要件をもう一度見てみるのが良いでしょう:ここで本当にやろうとしていること、このソートされたリストで何をしますか?

  • 最も関連性の高いソリューションを最初にユーザーに示すためにそれらを並べ替えようとしている場合は、不明な場所を最後に配置することをお勧めします。そのため、無限大のように扱いますヌル)。
  • この結果を使用してグラフを描画するか、距離によって実際にソートされることに依存する他の計算を実行する場合、nullはおそらくそこにあるべきではありません(したがって、最初に削除するか、例外として、その時点で実際に場所がnullのアドレスが想定されていなかった場合)。

既に理解しているように、そのうちの1つがnullのときに常に0を返すことは、ここでは良い考えではありません。実際に結果が破損する可能性があります。しかし、代わりに何をすべきかは、他の人が通常行う/必要とするものではなく、必要なものに依存します。場所のないアドレス(つまり、ユーザーに表示されるアドレス)でプログラムがどのように動作するかは、コンパレータの「ベストプラクティス」のような技術的な詳細に依存するべきではありません。 (私にとって、ここで「ベストプラクティス」とは何かを尋ねるのは、「ベストプラクティス」とは何かを尋ねるようなものです)。

1