Ho una classe immutabile, ListaToken, che consiste in un elenco di oggetti token, che sono anche immutabili:Uso UUID per equals a basso costo() e hashCode()
@Immutable
public final class TokenList {
private final List<Token> tokens;
public TokenList(List<Token> tokens) {
this.tokens = Collections.unmodifiableList(new ArrayList(tokens));
}
public List<Token> getTokens() {
return tokens;
}
}
che faccio diverse operazioni su queste TokenLists che prende più TokenList come input e restituisce un singolo TokenList come output. Possono esserci arbitrariamente molti TokenList che entrano, e ognuno può avere arbitrariamente molti Token.
Queste operazioni sono costose e vi sono buone probabilità che la stessa operazione (vale a dire gli stessi ingressi) venga eseguita più volte, quindi mi piacerebbe memorizzare le uscite in cache. Tuttavia, le prestazioni sono critiche, e sono preoccupato per le spese di esecuzione di hashCode() ed equals() su questi oggetti che possono contenere arbitrariamente molti elementi (dato che sono immutabili allora hashCode potrebbe essere memorizzato nella cache, ma equals sarà ancora costoso).
Questo mi ha portato a chiedermi se potevo usare un'UUID per fornire equals() e hashCode() semplice ed economico facendo i seguenti aggiornamenti per ListaToken:
@Immutable
public final class TokenList {
private final List<Token> tokens;
private final UUID uuid;
public TokenList(List<Token> tokens) {
this.tokens = Collections.unmodifiableList(new ArrayList(tokens));
this.uuid = UUID.randomUUID();
}
public List<Token> getTokens() {
return tokens;
}
public UUID getUuid() {
return uuid;
}
}
E qualcosa di simile ad agire come un chiave di cache:
@Immutable
public final class TopicListCacheKey {
private final UUID[] uuids;
public TopicListCacheKey(TopicList... topicLists) {
uuids = new UUID[topicLists.length];
for (int i = 0; i < uuids.length; i++) {
uuids[i] = topicLists[i].getUuid();
}
}
@Override
public int hashCode() {
return Arrays.hashCode(uuids);
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (other instanceof TopicListCacheKey)
return Arrays.equals(uuids, ((TopicListCacheKey) other).uuids);
return false;
}
}
I capire che ci sono 2^128 UUID differenti e avrò probabilmente al massimo circa 1.000.000 ListaToken oggetti attivo nell'applicazione in qualsiasi momento. Dato questo, e il fatto che gli UUID siano usati in modo combinatorio nelle chiavi della cache, sembra che le probabilità che questo produca il risultato sbagliato sono estremamente piccole. Tuttavia, mi sento a disagio ad andare avanti con esso in quanto sembra solo 'sporco'. Ci sono dei motivi per cui non dovrei usare questo sistema? I costi delle prestazioni di SecureRandom utilizzati da UUID.randomUUID() superano i guadagni (soprattutto perché mi aspetto che più thread lo facciano allo stesso tempo)? Le collisioni saranno più probabili di quanto penso? Fondamentalmente, c'è qualcosa di sbagliato nel farlo in questo modo ??
Grazie.
Perché stai implementando gli uguali in primo luogo? Se non ne hai bisogno (e stai cercando di sostituirlo con un UUID che suggerisci di non farlo), non sovrascrivere equals/hashcode. Se lo si desidera è necessario assicurarsi che lo stesso elenco di token esegua la mappatura allo stesso uuid ogni volta. –
In questo modo si potrebbe interrompere il contratto generale su ['List.equals()'] (http://docs.oracle.com/javase/7/docs/api/java/util/List.html#equals (java.lang .Oggetto)), è quello che vuoi? Anche se equals è costoso, usa 'hashCode' prima negli uguali e poi passa agli equivalenti reali se sono gli stessi. –