Je veux mettre en œuvre un comparateur simple entre deux objets, dont les seules conditions sont que
.compare
Retournera 0 si et seulement si les objets sont identique .Will Comparator.comparing(System::identityHashCode)
fonctionner? Y a-t-il d'une autre manière?
Motivation : Je veux construire une collection qui me permettra de stocker des messages horodatés dans une collection de fil-sécurité, qui prendra des requêtes telles que "Obtenez-moi tous les messages dont l'horodatage réside dans [A, b) ".
Il semble que GUAVA TreeMultimap
utilise un verrouillage global (modifier: s'il est enveloppé avec le wrapper synchronizedSortedSetMultimap
_ _ ConcurrentSkipListMap
semble supporter une seule entrée par heure (c'est une carte , pas une carte multiple). J'ai donc pensé à utiliser juste un ensemble de paires:
ConcurrentSkipListSet<ImmutablePair<Float,Message>> db
,
où les paires sont commandées lexiquement, d'abord par le temps (en utilisant Float.compareTo
), puis par quelque chose comme Comparator.nullsFirst(Comparator.comparing(System::identityHashCode))
.
Le nullsFirst
est là juste alors db.subSet(ImmutablePair.of(a,null), ImmutablePair.of(b,null))
intervient l'intervalle de temps semi-ouverte [A, B).
Vous voyez pourquoi je me soucie du comparateur en préservant la similitude: si le comparateur de message renvoie zéro pour les mêmes messages, les messages peuvent être supprimés.
Vous voyez également pourquoi je n'ai pas besoin d'une grande partie du comparateur: c'est juste là pour que je puisse utiliser le mécanisme de stockage de ConcurrentSkipListSet
. Je ne veux certainement pas imposer à l'utilisateur (bien, juste moi :-) Pour mettre en œuvre un comparateur pour Message
.
Une autre solution possible consiste à utiliser un ConcurrentSkipListMap<Float, Set<Message>>
(Avec ensemble de thread-coffre-fort <> instances), mais il semble un peu gaspillé en termes de mémoire, et je devrai supprimer Ekyset's moi-même pour enregistrer la mémoire une fois que les messages sont supprimés.
EDIT : Comme plusieurs personnes notées, IdentityHashCode peut produire des collisions et, en fait, j'ai maintenant confirmé que de telles collisions existent dans ma configuration (qui est à peu près équivalente à avoir une collecte de 4k comme ci-dessus, chacune peuplée avec 4k. messages par poubelle). C'est probablement la raison pour laquelle je vois des messages tombés. Donc, je suis maintenant plus intéressé que jamais pour trouver un moyen d'avoir un opérateur de comparaison "agnostique", qui véritablement respecte la similitude. En fait, une valeur de hachage de 64 bits (au lieu de la valeur 32 bits fournie par IdentityHashCodeCode) suffirait probablement.
Comme @Stuartmarks a noté dans son commentaire, GUAVA prend en charge Ordering.arbitrary()
, qui fournit une manipulation de collision de fil-sécurité. La mise en œuvre utilise efficacement 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;
}
donc, seule s'il y a une collision de hachage, getUid
(qui utilise un compteur d'atomicinteger mémo pour allouer des UID) est invoqué.
Il est également assez facile d'écrire (peut-être moins facile à lire?) Le conteneur de messages temporel souhaité dans "une" ligne:
db = new ConcurrentSkipListSet<>(
(Ordering.<Float>natural().<ImmutablePair<Float,Message>>onResultOf(x -> x.left))
.compound(Ordering.arbitrary().nullsFirst().<ImmutablePair<Float,Message>>onResultOf(x -> x.right)))
Est-ce que comparera (système :: identityhashcode) sera-t-il? Y a-t-il d'une autre manière?
Comme mentionné, IdentityHashCode n'est pas unique.
En fait, une valeur de hachage de 64 bits (au lieu de la valeur 32 bits fournie par IdentityHashCodeCode) suffirait probablement
Je pense que cela réduirait simplement les chances de chevauchement, ne pas les enlever. Les algorithèmes de hachage sont conçus pour limite chevauchements mais n'ont généralement aucune garantie d'aucun. Par exemple, MD5 est 128 bits et a toujours des chevauchements.
Que diriez-vous de simplement affecter un numéro unique à chaque message avec AtomicLong
. Ensuite, votre fonction de comparaison ferait:
Si vous avez plusieurs systèmes effectuant l'ingération de ces messages, vous devez enregistrer un numéro de système unique et un numéro de message pour assurer l'unicité.