Come garantire oggetti unici per uguaglianza?

0

Spesso ho questo problema ma non l'ho trovato come un modello di programmazione stabilito. Ho alcuni C di classe la cui uguaglianza è determinata da qualche tasto k e voglio impedire l'inizializzazione dispendiosa di tempo di ulteriori oggetti uguali.

Questa è la mia implementazione ma il metodo get sincronizzato impedisce l'istanziazione parallela.

Class C
{
 Key key;

 private static Map<Key,C> cs = new HashMap<Key,C>();

 private C(Key key)
 {
  // this takes a long time
 }

 synchronized static C get(Key key)
 {
  C c = cs.get(key);
  if(c==null)
  {
   c = new C(key);
   cs.put(key,c);
  }
  return c;
 }

 @Override public boolean equals(Object o)
 {
    if (this == o) return true;
    if (o == null) return false;
    if (getClass() != o.getClass()) return false;
    C other = (C) o;
    if (key == null)
    {
        if (other.key != null) return false;
    }
    else if (!key.equals(other.key)) return false;
    return true;
 }

 @Override public int hashCode() {..}
}

Puoi indicarmi una best practice esistente a questo, se esiste, o mostrarmi come implementarla in modo ottimale? I miei criteri sono chiarezza, brevità, prestazioni e sicurezza del thread.

    
posta Konrad Höffner 25.11.2014 - 13:20
fonte

4 risposte

1

Nel suo libro Effective Java, Josuha Bloch menziona questo modello come una buona pratica per questo tipo di scenario ( Articolo 1: considera metodi di factory statici invece di costruttori ).

Alcuni nomi alternativi comuni per il metodo che qui si chiama C.get sono i seguenti C.getInstance , C.valueOf , C.of .

    
risposta data 25.11.2014 - 13:37
fonte
1

Questa è una variante del modello singleton, chiamato Multiton .

Dovrai gestire lo stato globale introdotto con HashMap . Questo può portare a test di unità diffuse.

L'implementazione del metodo synchronized sembra la stessa, come mostrato nell'articolo di Wikipedia. Ma se riesci a ristrutturare il tuo caso d'uso, in modo da evitare la sincronizzazione degli oggetti sarebbe preferibile.

    
risposta data 25.11.2014 - 14:02
fonte
1

Se l'unico problema con l'inizializzazione di più copie di un singolo valore di oggetto è il tempo / la memoria che richiede, si può prendere in considerazione il passaggio a ConcurrentHashMap e utilizzando il metodo computeIfAbsent per inizializzare i valori. Questo ha l'effetto collaterale che possono essere inizializzate due copie di un singolo valore, ma solo una verrà mantenuta (la seconda sarà raccolta di dati inutili ad un certo punto) e tutti i thread saranno in grado di precedere simultaneamente.

Questa soluzione non è applicabile, tuttavia, se l'inizializzazione ha effetti collaterali.

    
risposta data 25.11.2014 - 20:50
fonte
0

Se l'oggetto Key è già univoco, non per equals() ma per istanza e nel caso in cui la chiave non sia mai effettivamente null (e il codice potrebbe essere modificato / la chiave di null potrebbe essere rimossa ), puoi cambiare la funzione get() in:

static C get(Key key) {
    synchronized (key) {
        C c = cs.get(key);
        if (c==null) {
            c = new C(key);
            cs.put(key,c);
        }
        return c;
    }
}
    
risposta data 25.11.2014 - 21:03
fonte

Leggi altre domande sui tag