座標間の距離に基づいて住所のリストを並べ替えるコードがあります。これは、カスタムコンパレータでcollections.sortを介して行われます。
ただし、座標のないアドレスがリストに含まれていると、NullPointerExceptionが発生することがあります。これを修正する私の最初のアイデアは、少なくとも1つの座標がnullであるアドレスに対して、コンパレータが距離として0を返すようにすることでした。これにより、リスト内の「有効な」要素の順序が破損する可能性があります。
コンパレータでnullデータの「0」値を返しますか、またはこれを解決するよりクリーンな方法がありますか?.
null
が無限に遠くを意味するように処理します。したがって:
comp(1234, null) == -1
comp(null, null) == 0
comp(null, 1234) == 1
これにより、一貫した順序を取得できます。
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コレクションの力が実を結び始めるところです。
これについての私の考えは、null
座標を「良くする」ためにあなたがやろうとすることは、ただひび割れの上に紙を張っているということです。本当に必要なのは、偽のnull
座標を注入しているバグを見つけて修正することです。
私の経験では、NPEバグの侵入は、次の悪いコーディング習慣によって頻繁に引き起こされます。
null
を使用して、空の配列またはコレクションを作成しないようにします。null
を返す、またはnull
を使用します。(「値なし」問題のより良い解決策は通常、コードを書き直して、これを表現する必要がない必要がないか、またはnull以外を使用することです。代わりに値;たとえば、空の文字列、特別なインスタンス、予約された値。常により良い解決策を見つけることはできませんが、多くの場合見つけることができます。)
これがアプリケーションを説明している場合、NPEを隠す方法を考えるのではなく、コードの問題を根絶するために時間を費やす必要があります。
私の解決策(ここで見ている人に役立つかもしれません)は、通常の比較を行い、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に設定できます。
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剥離]
アドレスが等距離であり、本当に知らないことを意味するので、おそらく0を返したくないでしょう。これは、悪い入力データに対処しようとする非常に古典的な問題です。 dont know distanceの場合、アドレスが実際の用語でどれだけ離れているかを判断するのは、コンパレータの責任だとは思わない。ソートする前にリストからそれらのアドレスを削除します。
ハックは、リストの一番下にそれらを移動することです(しかし、それはいです!)
いいえ、クリーンな方法はありません。おそらく:
しかし、もっと重要なのは、不足している座標を削除/埋めようとすることです。または、より良い方法は、不足している座標を持つアドレスをリストに入れないことです。
実際には、それらをリストに入れないことが最も論理的な動作です。それらをリストに入れた場合、結果は実際には距離順に並べられません。
座標が欠落しているアドレスを含む別のリストを作成し、その情報を必要とするユーザー(エンドユーザー、APIユーザー)に、最初のリストには必要なデータのアドレスのみが含まれ、2番目のリストには必要な情報が不足しています。
私は個人的にコンパレータのどこでも特別なヌルケースを扱うのが嫌いなので、よりクリーンなソリューションを探していて、最終的にGoogleコレクションを見つけました。彼らの注文は素晴らしいです。それらは複合コンパレータをサポートし、ヌルを先頭と末尾にソートし、比較する前に特定の関数を実行できるようにします。コンパレータの作成はこれまでになく簡単になりました。試してみてください。
これをコンパレータの技術的な問題のように見るのではなく、要件をもう一度見てみるのが良いでしょう:ここで本当にやろうとしていること、このソートされたリストで何をしますか?
既に理解しているように、そのうちの1つがnullのときに常に0を返すことは、ここでは良い考えではありません。実際に結果が破損する可能性があります。しかし、代わりに何をすべきかは、他の人が通常行う/必要とするものではなく、必要なものに依存します。場所のないアドレス(つまり、ユーザーに表示されるアドレス)でプログラムがどのように動作するかは、コンパレータの「ベストプラクティス」のような技術的な詳細に依存するべきではありません。 (私にとって、ここで「ベストプラクティス」とは何かを尋ねるのは、「ベストプラクティス」とは何かを尋ねるようなものです)。