web-dev-qa-db-ja.com

IdentityHashCodeを使用して、同じNESSを尊重するオブジェクト間の比較を作成できますか?

私は2つのオブジェクト間に単純なコンパレータを実装したい、その要件はそれだけです。

  1. それは有効な比較器です(すなわち、すべてのオブジェクトに線形順序を定義します)。
  2. .compareは、オブジェクトが同じの場合に限り、0を返します。

Comparator.comparing(System::identityHashCode)が作業しますか?もう一つの方法はありますか?

モチベーション:タイムスタンプ付きメッセージをスレッドセーフコレクションに保存できるコレクションを構築したいと思います。これは、「TimesTampが存在するすべてのメッセージを入手してください」のようなクエリをサポートします。 NS)"。

GUAVAのTreeMultimapはグローバルロックを使用しているようです(編集:synchronizedSortedSetMultimapラッパーでラップされている場合)、ConcurrentSkipListMapは時間に1つのエントリしかサポートしているようです(マップです。 、マルチマップではありません)。だから私はただのペアのセットを使うことを考えました:

ConcurrentSkipListSet<ImmutablePair<Float,Message>> db

ペアは、最初に(Float.compareToを使用して)、Comparator.nullsFirst(Comparator.comparing(System::identityHashCode))のようなものによって字句的に並んでいる場合。

  • nullsFirstは、db.subSet(ImmutablePair.of(a,null), ImmutablePair.of(b,null))だけが半オープン時間間隔[a、b)を問い合わせます。

  • なぜコンパレータを保存するのを気にする理由がわかります。メッセージコンパレータが同じメッセージでゼロを返す場合、メッセージは削除される可能性があります。

  • また、コンパレータから他の他の多くを必要としない理由もあります。ConcurrentSkipListSetのストレージメカニズムを使用できるようになります。私は確かにユーザーに課されたくないMessageの比較器を実装するために(ちょうど私:-)に課したくありません。

  • もう1つの考えられる解決策は、ConcurrentSkipListMap<Float, Set<Message>>(スレッドセーフセット<>インスタンスを持つ)を使用することですが、メモリの点で少し無駄になるようです。

編集:いくつかの人々が指摘したように、IdentityHashcodeは衝突を生み出すかもしれません、そして実際、私はそのような衝突が私のセットアップに存在することを確認しました(これは上記のように4Kのコレクションを持つとほぼ同じであり、それぞれ4Kに占めています)時間ビンあたりのメッセージ。これは私がいくつかのメッセージが削除されたのを見る理由で最も可能性が高いです。それで、私は今まででは、「不可知論的な」比較演算子を持つために何らかの方法を見つけることにおいてもっと興味があります真に =サルテスを尊重します。実際には、64ビットのハッシュ値(IdentityHashCodeによって提供される32ビット値の代わりに)がおそらく十分です。

12
Just Me

彼のコメントに述べたように、GuavaはOrdering.arbitrary()をサポートしています。これは、スレッドセーフな衝突処理を提供します。実装はidentityHashCodeの効率的な使用を行います。

@Override
public int compare(Object left, Object right) {
  if (left == right) {
    return 0;
  } else if (left == null) {
    return -1;
  } else if (right == null) {
    return 1;
  }
  int leftCode = identityHashCode(left);
  int rightCode = identityHashCode(right);
  if (leftCode != rightCode) {
    return leftCode < rightCode ? -1 : 1;
  }

  // identityHashCode collision (rare, but not as rare as you'd think)
  int result = getUid(left).compareTo(getUid(right));
  if (result == 0) {
    throw new AssertionError(); // extremely, extremely unlikely.
  }
  return result;
}
 _

したがって、ハッシュ衝突がある場合にのみ、getUid(Memoized Atomicintegerカウンタを使用してUIDを割り当てる)が呼び出されます。

また、「1つの」行の目的のタイムスタンプメッセージコンテナの目的のタイムスタンプメッセージコンテナを書きやすく読みやすいですか?)

db = new ConcurrentSkipListSet<>(
                (Ordering.<Float>natural().<ImmutablePair<Float,Message>>onResultOf(x -> x.left))
                        .compound(Ordering.arbitrary().nullsFirst().<ImmutablePair<Float,Message>>onResultOf(x -> x.right)))
 _
1
Just Me

Comparator.comparing(System :: IdentityHashCode)が機能しますか?もう一つの方法はありますか?

前述のように、IdentityHashCodeは固有ではありません。

実際には、64ビットハッシュ値(IdentityHashCodeによって提供される32ビット値の代わりに)はおそらく十分です。

これは、それらを取り外さないで、重なりの可能性を減らすだけであると思います。ハッシュアルゴリズムは、limit重なり合うように設計されていますが、通常は何も保証はありません。たとえば、MD5は128ビットで、まだ重なっています。

AtomicLongで各メッセージに固有の番号を割り当てるだけです。それからあなたの比較関数は次のようにします。

  1. 時間を比較してください。 floatの代わりに可能な限り長く使用します。
  2. 同時に一意の値で比較します。

これらのメッセージの取り込みを実行している複数のシステムがある場合は、一意性を確保するために一意のシステムIDとメッセージ番号を記録する必要があります。

0
Gray