Ottenere un elemento da un set


323

Perché non Setfornisce un'operazione per ottenere un elemento uguale a un altro elemento?

Set<Foo> set = ...;
...
Foo foo = new Foo(1, 2, 3);
Foo bar = set.get(foo);   // get the Foo element from the Set that equals foo

Posso chiedere se Setcontiene un elemento uguale a bar, quindi perché non riesco a ottenere quell'elemento? :(

Per chiarire, il equalsmetodo viene ignorato, ma controlla solo uno dei campi, non tutti. Quindi due Foooggetti considerati uguali possono effettivamente avere valori diversi, ecco perché non posso semplicemente usarli foo.


2
Questo post è già ampiamente discusso e sono state suggerite buone risposte. Tuttavia, se stai solo cercando un set ordinato, usa semplicemente SortedSete le sue implementazioni, che sono basate su mappe (ad es. TreeSetConsente l'accesso first()).
Eliran Malka,

3
Mi manca anche questo metodo, esattamente per lo stesso caso che hai descritto sopra. Objective-C ( NSSet) ha un tale metodo. Viene chiamato membere restituisce l'oggetto all'interno dell'insieme che confronta "uguale" con il parametro del membermetodo (che può ovviamente essere un oggetto diverso e anche avere proprietà diverse, che uguali potrebbero non verificare).
Mecki,

Risposte:


118

Non avrebbe senso ottenere l'elemento se fosse uguale. A Mapè più adatto per questo caso d'uso.


Se vuoi ancora trovare l'elemento non hai altra opzione che usare l'iteratore:

public static void main(String[] args) {

    Set<Foo> set = new HashSet<Foo>();
    set.add(new Foo("Hello"));

    for (Iterator<Foo> it = set.iterator(); it.hasNext(); ) {
        Foo f = it.next();
        if (f.equals(new Foo("Hello")))
            System.out.println("foo found");
    }
}

static class Foo {
    String string;
    Foo(String string) {
        this.string = string;
    }
    @Override
    public int hashCode() { 
        return string.hashCode(); 
    }
    @Override
    public boolean equals(Object obj) {
        return string.equals(((Foo) obj).string);
    }
}

234
Potrebbe assolutamente esserci un punto per ottenere l'elemento. Cosa succede se si desidera aggiornare alcuni dei valori dell'elemento dopo che è già stato aggiunto al set? Ad esempio, quando .equals () non utilizza tutti i campi, come specificato dall'OP. Una soluzione meno efficiente sarebbe quella di rimuovere l'elemento e aggiungerlo nuovamente con i suoi valori aggiornati.
KyleM,

14
Direi ancora che a Mapè più adatto ( Map<Foo, Foo>in questo caso.)
dacwe,

22
@dacwe, sono arrivato qui perché ho iniziato a cercare un modo per evitarlo esattamente! Un oggetto che agisce, allo stesso tempo, sia come chiave che come valore corrispondente è esattamente ciò che dovrebbe essere un insieme. Nel mio caso, vorrei ottenere un oggetto complesso da un set di chiavi (String). Questa stringa è incapsulata (e unica) per l'oggetto da mappare. In effetti, l'intero oggetto "ruota" attorno a detta chiave. Inoltre, il chiamante conosce detto String, ma non l'oggetto stesso; questo è esattamente il motivo per cui vuole recuperarlo con la chiave. Ora sto usando una mappa ovviamente, ma rimane un comportamento strano.
pauluss86,

4
@KyleM Capisco il caso d'uso, ma voglio sottolineare l'importanza di non toccare gli attributi che fanno parte di hashCode / equals. Dal set Javadoc: "Nota: è necessario prestare molta attenzione se si utilizzano oggetti mutabili come elementi del set. Il comportamento di un set non viene specificato se il valore di un oggetto viene modificato in modo da influenzare i confronti uguali mentre l'oggetto è un elemento nel set. " - Raccomando che quegli oggetti siano immutabili o almeno abbiano attributi chiave immutabili.
stivlo,

5
Sono d'accordo che puoi usare Map<Foo, Foo>come rimpiazzo, il rovescio della medaglia è che una mappa deve sempre memorizzare almeno una chiave e un valore (e per prestazioni dovrebbe anche memorizzare l'hash), mentre un set può cavarsela semplicemente memorizzando il valore (e forse hash per le prestazioni). Pertanto, un'implementazione di un buon set può essere altrettanto veloce Map<Foo, Foo>ma utilizzare fino al 50% di memoria in meno. Nel caso di Java non importa, poiché HashSet è comunque basato internamente su HashMap.
Mecki,

372

Per rispondere alla domanda precisa " Perché non Setfornire un'operazione per ottenere un elemento uguale a un altro elemento?", La risposta sarebbe: perché i progettisti del framework di raccolta non erano molto lungimiranti. Non hanno anticipato il tuo caso d'uso molto legittimo, hanno ingenuamente cercato di "modellare l'astrazione dell'insieme matematico" (dal javadoc) e hanno semplicemente dimenticato di aggiungere il get()metodo utile .

Ora alla domanda implicita " come si ottiene l'elemento allora": penso che la soluzione migliore sia quella di utilizzare un Map<E,E>anziché un Set<E>, per mappare gli elementi su se stessi. In questo modo, è possibile recuperare in modo efficiente un elemento dal "set", poiché il metodo get () Maptroverà l'elemento usando una tabella hash efficiente o un algoritmo ad albero. Se lo desideri, puoi scrivere la tua implementazione di Setquella che offre il get()metodo aggiuntivo , incapsulando ilMap .

Le seguenti risposte sono a mio avviso cattive o sbagliate:

"Non hai bisogno di ottenere l'elemento, perché hai già un oggetto uguale": l'affermazione è sbagliata, come hai già mostrato nella domanda. Due oggetti uguali possono avere uno stato diverso che non è rilevante per l'uguaglianza degli oggetti. L'obiettivo è ottenere l'accesso a questo stato dell'elemento contenuto nel fileSet , non allo stato dell'oggetto utilizzato come "query".

"Non hai altra scelta che usare l'iteratore": si tratta di una ricerca lineare su una raccolta che è totalmente inefficace per grandi insiemi (ironicamente, internamente Setè organizzata come mappa hash o albero che potrebbe essere interrogato in modo efficiente). Non farlo! Ho visto gravi problemi di prestazioni nei sistemi della vita reale usando questo approccio. Secondo me ciò che è terribile del get()metodo mancante non è tanto che è un po 'complicato aggirarlo, ma che la maggior parte dei programmatori utilizzerà l'approccio di ricerca lineare senza pensare alle implicazioni.


27
meh. Sostituire l'implementazione di uguali in modo che gli oggetti non uguali siano "uguali" è il problema qui. Chiedere un metodo che dice "portami l'oggetto identico a questo oggetto", e quindi aspettarsi che un oggetto non identico venga restituito sembra folle e facile da causare problemi di manutenzione. Come altri hanno suggerito, l'uso di una mappa risolve tutti questi problemi: e rende ciò che stai facendo autoesplicativo. È facile capire che due oggetti non uguali potrebbero avere la stessa chiave in una mappa e avere la stessa chiave mostrerebbe la relazione tra di loro.
David Ogren,

20
Parole forti, @ David Ogren. Meh? Pazzo? Ma nel tuo commento, stai usando le parole "identico" e "uguale" come se significassero la stessa cosa. Loro non. In particolare, in Java, l'identità è espressa dall'operatore "==" e l'uguaglianza è espressa dal metodo equals (). Se significassero la stessa cosa, non ci sarebbe affatto bisogno di un metodo equals (). In altre lingue, questo può ovviamente essere diverso. Ad esempio, in Groovy, l'identità è il metodo is () e l'uguaglianza è "==". Divertente, vero?
Jschreiner,

15
La tua critica al mio uso della parola identica quando avrei dovuto usare la parola equivalente è molto valida. Ma definire uguali su un oggetto in modo che Foo e Bar siano "uguali" ma non "abbastanza uguali" per usarli in modo equivalente creerà tutti i tipi di problemi sia con funzionalità che con leggibilità / manutenibilità. Questo problema con Set è solo la punta dell'iceberg per potenziali problemi. Ad esempio, oggetti uguali devono avere codici hash uguali. Quindi avrà potenziali collisioni di hashish. È folle obiettare chiamando .get (foo) specificamente per ottenere qualcosa di diverso da foo?
David Ogren,

12
Vale probabilmente la pena notare che, ad esempio, HashSet è implementato come un wrapper attorno a HashMap (che mappa le chiavi su un valore fittizio). Pertanto, l'utilizzo esplicito di una HashMap anziché di un HashSet non causerebbe un sovraccarico nell'uso della memoria.
Alexey B.

4
@ user686249 Mi sembra che questo sia diventato un dibattito accademico. Ammetto che avrei potuto esagerare nell'obiettare contro l'uguaglianza. Soprattutto in un uso come il tuo. Ma ho ancora un'obiezione all'idea di chiamare questo metodo get(). Nel tuo esempio, sarei molto confuso da customerSet.get (thisCustomer). (Considerando che una mappa, come suggerito da molte risposte) andrebbe bene con canonicalCustomerMap.get (questo cliente). Starei anche bene con un metodo che è più chiaramente chiamato (come il metodo membro di Objective-C su NSSet).
David Ogren,

19

Se hai un oggetto uguale, perché hai bisogno di quello del set? Se è "uguale" solo con una chiave, unMap sarebbe una scelta migliore.

Ad ogni modo, lo farà il seguente:

Foo getEqual(Foo sample, Set<Foo> all) {
  for (Foo one : all) {
    if (one.equals(sample)) {
      return one;
    }
  } 
  return null;
}

Con Java 8 questo può diventare un solo aspetto:

return all.stream().filter(sample::equals).findAny().orElse(null);

Mi piace meglio questa risposta, eviterei semplicemente di usare due dichiarazioni di ritorno, dal momento che è contro OOP e rende più alto il valore della complessità ciclomatica.
Leone,

8
@Leo grazie, ma il paradigma della singola uscita non è contro OOP ed è per lo più non valido per le lingue più moderne di Fortran o COBOL, vedi anche softwareengineering.stackexchange.com/questions/118703/…
Arne Burmeister,

1
L'uso di una mappa invece di un set sembra un'opzione migliore: iterare gli elementi di un set è più un lavoro che ottenere un singolo valore da una mappa. (O (N) vs O (1))
Jamie Flournoy,

@JamieFlournoy, giusto, se devi controllare lo stesso set per diversi elementi più volte, è molto meglio. Per un singolo utilizzo no, in quanto richiede prima uno sforzo maggiore per costruire la mappa.
Arne Burmeister,

18

Convertire set in elenco, quindi utilizzare il getmetodo di elenco

Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);

37
Non capisco. Ciò recupererà un oggetto arbitrario dell'insieme. Non l' oggetto.
aioobe,

14

Purtroppo, Default Set in Java non è progettato per fornire un'operazione "get", come spiegato con precisione da jschreiner .

Le soluzioni dell'utilizzo di un iteratore per trovare l'elemento di interesse (suggerito da dacwe ) o per rimuovere l'elemento e aggiungerlo nuovamente con i suoi valori aggiornati (suggerito da KyleM ), potrebbero funzionare, ma possono essere molto inefficienti.

Sovrascrivere l'implementazione di uguali in modo che gli oggetti non uguali siano "uguali", come affermato correttamente da David Ogren , può facilmente causare problemi di manutenzione.

E l'uso di una mappa come sostituzione esplicita (come suggerito da molti), imho, rende il codice meno elegante.

Se l'obiettivo è ottenere l'accesso all'istanza originale dell'elemento contenuto nel set (spero di aver compreso correttamente il tuo caso d'uso), ecco un'altra possibile soluzione.


Personalmente ho avuto le tue stesse necessità durante lo sviluppo di un videogioco client-server con Java. Nel mio caso, ogni client aveva copie dei componenti archiviati nel server e il problema era ogni volta che un client aveva bisogno di modificare un oggetto del server.

Passare un oggetto attraverso Internet significava che il client aveva comunque diverse istanze di quell'oggetto. Per abbinare questa istanza "copiata" a quella originale, ho deciso di utilizzare gli UUID Java.

Così ho creato una classe astratta UniqueItem, che fornisce automaticamente un ID univoco casuale a ciascuna istanza delle sue sottoclassi.

Questo UUID è condiviso tra il client e l'istanza del server, quindi in questo modo potrebbe essere facile abbinarli semplicemente usando una mappa.

Tuttavia, l'utilizzo diretto di una mappa in un caso d'uso simile era ancora inelegante. Qualcuno potrebbe obiettare che l'uso di una mappa potrebbe essere più complicato da mantenere e gestire.

Per questi motivi ho implementato una libreria chiamata MagicSet, che rende l'utilizzo di una mappa "trasparente" per lo sviluppatore.

https://github.com/ricpacca/magicset


Come l'originale Java HashSet, un MagicHashSet (che è una delle implementazioni di MagicSet fornite nella libreria) utilizza un HashMap di supporto, ma invece di avere elementi come chiavi e un valore fittizio come valori, usa l'UUID dell'elemento come chiave e l'elemento stesso come valore. Ciò non causa un sovraccarico nell'uso della memoria rispetto a un normale HashSet.

Inoltre, un MagicSet può essere usato esattamente come un Set, ma con alcuni altri metodi che forniscono funzionalità aggiuntive, come getFromId (), popFromId (), removeFromId (), ecc.

L'unico requisito per usarlo è che qualsiasi elemento che si desidera memorizzare in un MagicSet deve estendere la classe astratta UniqueItem.


Ecco un esempio di codice, immaginando di recuperare l'istanza originale di una città da un MagicSet, data un'altra istanza di quella città con lo stesso UUID (o anche solo il suo UUID).

class City extends UniqueItem {

    // Somewhere in this class

    public void doSomething() {
        // Whatever
    }
}

public class GameMap {
    private MagicSet<City> cities;

    public GameMap(Collection<City> cities) {
        cities = new MagicHashSet<>(cities);
    }

    /*
     * cityId is the UUID of the city you want to retrieve.
     * If you have a copied instance of that city, you can simply 
     * call copiedCity.getId() and pass the return value to this method.
     */
    public void doSomethingInCity(UUID cityId) {
        City city = cities.getFromId(cityId);
        city.doSomething();
    }

    // Other methods can be called on a MagicSet too
}

11

Se il tuo set è in realtà un NavigableSet<Foo>(come un TreeSet), e Foo implements Comparable<Foo>puoi usarlo

Foo bar = set.floor(foo); // or .ceiling
if (foo.equals(bar)) {
    // use bar…
}

(Grazie al commento di @ eliran-malka per il suggerimento.)


5
Se non mi importasse nessuno che leggesse il mio codice pensando inizialmente di essere diventato completamente pazzo, sarebbe stata un'ottima soluzione.
Adam,

10

Con Java 8 puoi fare:

Foo foo = set.stream().filter(item->item.equals(theItemYouAreLookingFor)).findFirst().get();

Ma fai attenzione, .get () genera NoSuchElementException o puoi manipolare un elemento opzionale.


5
item->item.equals(theItemYouAreLookingFor)può essere abbreviato intheItemYouAreLookingFor::equals
Henno Vermeulen,

5
Object objectToGet = ...
Map<Object, Object> map = new HashMap<Object, Object>(set.size());
for (Object o : set) {
    map.put(o, o);
}
Object objectFromSet = map.get(objectToGet);

Se ne ottieni uno solo, questo non sarà molto performante perché passerai in rassegna tutti i tuoi elementi ma quando esegui recuperi multipli su un set grande noterai la differenza.


5

Perché:

Sembra che Set abbia un ruolo utile nel fornire un mezzo di confronto. È progettato per non memorizzare elementi duplicati.

A causa di questa intenzione / progettazione, se si dovesse ottenere () un riferimento all'oggetto memorizzato, quindi mutarlo, è possibile che le intenzioni di progettazione di Set possano essere contrastate e possano causare comportamenti imprevisti.

Da JavaDocs

Bisogna fare molta attenzione se gli oggetti mutabili sono usati come elementi impostati. Il comportamento di un set non viene specificato se il valore di un oggetto viene modificato in un modo che influisce sui confronti uguali mentre l'oggetto è un elemento nel set.

Come:

Ora che sono stati introdotti gli stream, si può fare quanto segue

mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();

2

Che ne dici di usare la classe Arrays?

import java.util.Arrays;
import java.util.List;
import java.util.HashSet;
import java.util.Arrays;

public class MyClass {
    public static void main(String args[]) {
        Set mySet = new HashSet();
        mySet.add("one");
        mySet.add("two");
        List list = Arrays.asList(mySet.toArray());
        Object o0 = list.get(0);
        Object o1 = list.get(1);
        System.out.println("items " + o0+","+o1);
    }
}

output:
articoli uno, due




1

Ci sono stato fatto !! Se stai usando Guava un modo rapido per convertirlo in una mappa è:

Map<Integer,Foo> map = Maps.uniqueIndex(fooSet, Foo::getKey);

1

puoi usare la classe Iterator

import java.util.Iterator;
import java.util.HashSet;

public class MyClass {
 public static void main(String[ ] args) {
 HashSet<String> animals = new HashSet<String>();
animals.add("fox");
animals.add("cat");
animals.add("dog");
animals.add("rabbit");

Iterator<String> it = animals.iterator();
while(it.hasNext()) {
  String value = it.next();
  System.out.println(value);   
 }
 }
}

1

Se vuoi l'ennesimo elemento da HashSet, puoi andare con la soluzione di seguito, qui ho aggiunto l'oggetto di ModelClass in HashSet.

ModelClass m1 = null;
int nth=scanner.nextInt();
for(int index=0;index<hashset1.size();index++){
    m1 = (ModelClass) itr.next();
    if(nth == index) {
        System.out.println(m1);
        break;
    }
}

1

Se guardi le prime righe dell'implementazione java.util.HashSetvedrai:

public class HashSet<E>
    ....
    private transient HashMap<E,Object> map;

Quindi HashSetusa HashMapinterally comunque, il che significa che se usi HashMapdirettamente un e usi lo stesso valore della chiave e del valore otterrai l'effetto desiderato e risparmierai un po 'di memoria.


1

sembra che l'oggetto corretto da usare sia l' Interner di guava:

Fornisce un comportamento equivalente a String.intern () per altri tipi immutabili. Le implementazioni comuni sono disponibili dalla classe Interners .

Ha anche alcune leve molto interessanti, come concurrencyLevel, o il tipo di riferimenti utilizzati (vale la pena notare che non offre un SoftInterner che potrei vedere più utile di un WeakInterner).


0

Perché una particolare implementazione di Set può o meno essere un accesso casuale .

Puoi sempre ottenere un iteratore e passare attraverso il Set, usando il next()metodo degli iteratori per restituire il risultato desiderato una volta trovato l'elemento uguale. Funziona indipendentemente dall'implementazione. Se l'implementazione NON è un accesso casuale (immagina un set supportato da una lista collegata), un get(E element)metodo nell'interfaccia sarebbe ingannevole, dal momento che dovrebbe iterare la raccolta per trovare l'elemento da restituire, e get(E element)sembrerebbe implicare che ciò sarebbe necessario, che il Set possa saltare direttamente all'elemento per ottenere.

contains() può o meno dover fare la stessa cosa, ovviamente, a seconda dell'implementazione, ma il nome non sembra prestarsi allo stesso tipo di equivoci.


2
Qualunque cosa il metodo get () farebbe è già stato fatto dal metodo Includes (). Non è possibile implementare contenga () senza ottenere l'oggetto contenuto e chiamare il suo metodo .equals (). I progettisti dell'API sembravano non avere scrupoli nell'aggiungere un get () a java.util.List anche se sarebbe lento in alcune implementazioni.
Bryan Rink,

Non penso sia vero. Due oggetti possono essere uguali tramite uguale, ma non identico tramite ==. Se avessi l'oggetto A e un set S contenente l'oggetto B e A. equals (B) ma A! = B e volessi ottenere un riferimento a B, potresti chiamare S.get (A) per ottenere un riferimento a B, supponendo che tu avessi un metodo get con la semantica del metodo get di List, che è un caso d'uso diverso rispetto al controllo se S.contains (A) (che sarebbe). Questo non è nemmeno un caso d'uso così raro per le raccolte.
Tom Tresansky,

0

Sì, usa HashMap... ma in modo specializzato: la trappola che prevedo nel tentativo di usare a HashMapcome pseudo- Setè la possibile confusione tra elementi "reali" degli elementi Map/Set"candidati", ovvero elementi usati per verificare se un equalelemento è già presente. Questo è tutt'altro che infallibile, ma ti spinge lontano dalla trappola:

class SelfMappingHashMap<V> extends HashMap<V, V>{
    @Override
    public String toString(){
        // otherwise you get lots of "... object1=object1, object2=object2..." stuff
        return keySet().toString();
    }

    @Override
    public V get( Object key ){
        throw new UnsupportedOperationException( "use tryToGetRealFromCandidate()");
    }

    @Override
    public V put( V key, V value ){
       // thorny issue here: if you were indavertently to `put`
       // a "candidate instance" with the element already in the `Map/Set`: 
       // these will obviously be considered equivalent 
       assert key.equals( value );
       return super.put( key, value );
    }

    public V tryToGetRealFromCandidate( V key ){
        return super.get(key);
    }
}

Quindi fai questo:

SelfMappingHashMap<SomeClass> selfMap = new SelfMappingHashMap<SomeClass>();
...
SomeClass candidate = new SomeClass();
if( selfMap.contains( candidate ) ){
    SomeClass realThing = selfMap.tryToGetRealFromCandidate( candidate );
    ...
    realThing.useInSomeWay()...
}

Ma ... ora vuoi candidateche si autodistrugga in qualche modo a meno che il programmatore non lo inserisca immediatamente nel Map/Set... vorresti contains"contaminare" il candidatemodo che qualsiasi uso di esso a meno che non si unisca al Map"anatema" ". Forse potresti SomeClassimplementare una nuova Taintableinterfaccia.

Una soluzione più soddisfacente è un GettingSet , come di seguito. Tuttavia, affinché questo funzioni o devi essere responsabile della progettazione SomeClassal fine di rendere tutti i costruttori non visibili (o ... in grado e disposto a progettare e utilizzare una classe wrapper per esso):

public interface NoVisibleConstructor {
    // again, this is a "nudge" technique, in the sense that there is no known method of 
    // making an interface enforce "no visible constructor" in its implementing classes 
    // - of course when Java finally implements full multiple inheritance some reflection 
    // technique might be used...
    NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet );
};

public interface GettableSet<V extends NoVisibleConstructor> extends Set<V> {
    V getGenuineFromImpostor( V impostor ); // see below for naming
}

Implementazione:

public class GettableHashSet<V extends NoVisibleConstructor> implements GettableSet<V> {
    private Map<V, V> map = new HashMap<V, V>();

    @Override
    public V getGenuineFromImpostor(V impostor ) {
        return map.get( impostor );
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean contains(Object o) {
        return map.containsKey( o );
    }

    @Override
    public boolean add(V e) {
        assert e != null;
        V result = map.put( e,  e );
        return result != null;
    }

    @Override
    public boolean remove(Object o) {
        V result = map.remove( o );
        return result != null;
    }

    @Override
    public boolean addAll(Collection<? extends V> c) {
        // for example:
        throw new UnsupportedOperationException();
    }

    @Override
    public void clear() {
        map.clear();
    }

    // implement the other methods from Set ...
}

Le tue NoVisibleConstructorlezioni allora assomigliano a questo:

class SomeClass implements NoVisibleConstructor {

    private SomeClass( Object param1, Object param2 ){
        // ...
    }

    static SomeClass getOrCreate( GettableSet<SomeClass> gettableSet, Object param1, Object param2 ) {
        SomeClass candidate = new SomeClass( param1, param2 );
        if (gettableSet.contains(candidate)) {
            // obviously this then means that the candidate "fails" (or is revealed
            // to be an "impostor" if you will).  Return the existing element:
            return gettableSet.getGenuineFromImpostor(candidate);
        }
        gettableSet.add( candidate );
        return candidate;
    }

    @Override
    public NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet ){
       // more elegant implementation-hiding: see below
    }
}

PS un problema tecnico con una tale NoVisibleConstructorclasse: si potrebbe obiettare che tale classe è intrinsecamente final, il che può essere indesiderabile. In realtà potresti sempre aggiungere un protectedcostruttore fittizio senza parametri :

protected SomeClass(){
    throw new UnsupportedOperationException();
}

... che almeno consentirebbe la compilazione di una sottoclasse. Dovresti quindi pensare se devi includere un altro getOrCreate()metodo factory nella sottoclasse.

Il passaggio finale è una classe base astratta (NB "elemento" per un elenco, "membro" per un set) come questo per i membri del tuo set (quando possibile - ancora una volta, ambito per l'utilizzo di una classe wrapper in cui la classe non è sotto il tuo controllo, o ha già una classe base, ecc.), per nascondere al massimo l'implementazione:

public abstract class AbstractSetMember implements NoVisibleConstructor {
    @Override
    public NoVisibleConstructor
            addOrGetExisting(GettableSet<? extends NoVisibleConstructor> gettableSet) {
        AbstractSetMember member = this;
        @SuppressWarnings("unchecked") // unavoidable!
        GettableSet<AbstractSetMembers> set = (GettableSet<AbstractSetMember>) gettableSet;
        if (gettableSet.contains( member )) {
            member = set.getGenuineFromImpostor( member );
            cleanUpAfterFindingGenuine( set );
        } else {
            addNewToSet( set );
        }
        return member;
    }

    abstract public void addNewToSet(GettableSet<? extends AbstractSetMember> gettableSet );
    abstract public void cleanUpAfterFindingGenuine(GettableSet<? extends AbstractSetMember> gettableSet );
}

... l'uso è abbastanza ovvio (all'interno del vostro SomeClass's staticmetodo factory):

SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );

0

Il contratto del codice hash chiarisce che:

"Se due oggetti sono uguali secondo il metodo Object, la chiamata del metodo hashCode su ciascuno dei due oggetti deve produrre lo stesso risultato intero."

Quindi il tuo presupposto:

"Per chiarire, il metodo uguale è ignorato, ma controlla solo uno dei campi, non tutti. Quindi due oggetti Foo considerati uguali possono effettivamente avere valori diversi, ecco perché non posso semplicemente usare foo."

è sbagliato e stai rompendo il contratto. Se guardiamo al metodo "contiene" dell'interfaccia Set, abbiamo che:

booleano contiene (oggetto o);
Restituisce vero se questo set contiene l'elemento specificato. Più formalmente, restituisce vero se e solo se questo set contiene un elemento "e" tale che o == null? e == null: o.equals (e)

Per ottenere ciò che desideri, puoi utilizzare una Mappa in cui definisci la chiave e memorizza il tuo elemento con la chiave che definisce come gli oggetti sono diversi o uguali tra loro.


-2

Metodo di supporto rapido che potrebbe risolvere questa situazione:

<T> T onlyItem(Collection<T> items) {
    if (items.size() != 1)
        throw new IllegalArgumentException("Collection must have single item; instead it has " + items.size());

    return items.iterator().next();
}

8
È molto strano che questa risposta abbia ottenuto così tanti voti poiché non risponde alla domanda e non tenta nemmeno di affrontarla in alcun modo.
David Conrad,

-2

Di seguito può essere un approccio

   SharedPreferences se_get = getSharedPreferences("points",MODE_PRIVATE);
   Set<String> main = se_get.getStringSet("mydata",null);
   for(int jk = 0 ; jk < main.size();jk++)
   {
      Log.i("data",String.valueOf(main.toArray()[jk]));
   }

-2

Prova a usare un array:

ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.