Obtener un elemento de un conjunto

323

¿Por qué no Setproporciona una operación para obtener un elemento que sea igual a otro 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

Puedo preguntar si Setcontiene un elemento igual a bar, entonces ¿por qué no puedo obtener ese elemento? :(

Para aclarar, el equalsmétodo se anula, pero solo verifica uno de los campos, no todos. Entonces, dos Fooobjetos que se consideran iguales pueden tener valores diferentes, por eso no puedo usarlos foo.

foobar
fuente
2
Esta publicación ya se ha discutido ampliamente, y se han sugerido buenas respuestas. Sin embargo, si solo está buscando un conjunto ordenado, simplemente use SortedSety sus implementaciones, que están basadas en mapas (por ejemplo, TreeSetpermite el acceso first()).
Eliran Malka
3
También extraño ese método, exactamente por el mismo caso que describiste anteriormente. Objective-C ( NSSet) tiene tal método. Se llama membery devuelve el objeto dentro del conjunto que compara "igual" con el parámetro del membermétodo (que, por supuesto, puede ser un objeto diferente y también tiene propiedades diferentes, que igual no puede comprobar).
Mecki

Respuestas:

118

No tendría sentido obtener el elemento si es igual. A Mapes más adecuado para este caso de uso.


Si todavía desea encontrar el elemento, no tiene otra opción que usar el iterador:

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);
    }
}
dacwe
fuente
234
Absolutamente podría haber un punto para obtener el elemento. ¿Qué sucede si desea actualizar algunos de los valores del elemento después de que ya se haya agregado al Conjunto? Por ejemplo, cuando .equals () no utiliza todos los campos, como lo especificó el OP. Una solución menos eficiente sería eliminar el elemento y volver a agregarlo con sus valores actualizados.
KyleM
14
Me gustaría volver a un argumento que Mapes más adecuado ( Map<Foo, Foo>en este caso.)
dacwe
22
@dacwe, llegué aquí porque comencé a buscar una manera de evitar exactamente eso. Un objeto que actúa, al mismo tiempo, como clave y como valor correspondiente es exactamente de lo que debe tratarse un conjunto. En mi caso, me gustaría obtener algún objeto complejo de un conjunto por clave (String). Esta cadena está encapsulada (y es única) al objeto que se está asignando. De hecho, todo el objeto 'gira' en torno a dicha clave. Además, la persona que llama conoce dicha cadena, pero no el objeto en sí; eso es exactamente por qué quiere recuperarlo por clave. Estoy usando un mapa ahora, por supuesto, pero sigue siendo un comportamiento extraño.
pauluss86
44
@KyleM Entiendo el caso de uso, pero quiero enfatizar la importancia de no tocar los atributos que son parte de hashCode / equals. Del Conjunto Javadoc: "Nota: Se debe tener mucho cuidado si se utilizan objetos mutables como elementos del conjunto. El comportamiento de un conjunto no se especifica si el valor de un objeto se cambia de una manera que afecta a las comparaciones iguales mientras el objeto es un elemento en el conjunto ". - Recomiendo que esos objetos sean inmutables, o al menos tengan atributos clave inmutables.
stivlo
55
Estoy de acuerdo en que puede usarlo Map<Foo, Foo>como reemplazo, la desventaja es que un mapa siempre debe almacenar al menos una clave y un valor (y para el rendimiento también debe almacenar el hash), mientras que un conjunto puede escapar simplemente almacenando el valor (y tal vez hash para el rendimiento). Por lo tanto, una buena implementación de conjunto puede ser igualmente rápida Map<Foo, Foo>pero usar hasta un 50% menos de memoria. En el caso de Java, no importará, ya que el HashSet se basa internamente en HashMap de todos modos.
Mecki
372

Para responder a la pregunta precisa " ¿ Por qué no Setproporcionar una operación para obtener un elemento que es igual a otro elemento?", La respuesta sería: porque los diseñadores del marco de la colección no eran muy progresistas. No anticiparon su caso de uso muy legítimo, ingenuamente intentaron "modelar la abstracción del conjunto matemático" (del javadoc) y simplemente olvidaron agregar el get()método útil .

Ahora a la pregunta implícita " ¿cómo se obtiene el elemento entonces?": Creo que la mejor solución es usar un en Map<E,E>lugar de un Set<E>, para asignar los elementos a sí mismos. De esa manera, puede recuperar eficientemente un elemento del "conjunto", porque el método get () del Mapbuscará el elemento utilizando una tabla hash eficiente o un algoritmo de árbol. Si lo desea, puede escribir su propia implementación de Setque ofrece el get()método adicional , encapsulando el Map.

Las siguientes respuestas son, en mi opinión, malas o incorrectas:

"No necesita obtener el elemento, porque ya tiene un objeto igual": la afirmación es incorrecta, como ya mostró en la pregunta. Dos objetos que son iguales todavía pueden tener un estado diferente que no es relevante para la igualdad de objetos. El objetivo es obtener acceso a este estado del elemento contenido en el Set, no el estado del objeto utilizado como una "consulta".

"No tiene otra opción que usar el iterador": es una búsqueda lineal en una colección que es totalmente ineficiente para conjuntos grandes (irónicamente, internamente Setestá organizado como un mapa o árbol hash que podría consultarse de manera eficiente). ¡No lo hagas! He visto graves problemas de rendimiento en sistemas de la vida real al usar ese enfoque. En mi opinión, lo terrible del get()método faltante no es tanto que sea un poco engorroso solucionarlo, sino que la mayoría de los programadores usarán el enfoque de búsqueda lineal sin pensar en las implicaciones.

jschreiner
fuente
27
meh El problema aquí es anular la implementación de iguales para que los objetos no iguales sean "iguales". Pedir un método que diga "consígueme el objeto idéntico a este objeto" y luego esperar que se devuelva un objeto no idéntico parece una locura y es fácil causar problemas de mantenimiento. Como otros han sugerido, el uso de un mapa resuelve todos estos problemas: y hace que lo que está haciendo se explique por sí mismo. Es fácil entender que dos objetos no iguales pueden tener la misma clave en un mapa, y tener la misma clave mostrará la relación entre ellos.
David Ogren
20
Palabras fuertes, @David Ogren. Meh ¿Loco? Pero en su comentario, está usando las palabras "idéntico" e "igual" como si significaran lo mismo. Ellos no. Específicamente, en Java, la identidad se expresa mediante el operador "==" y la igualdad se expresa mediante el método equals (). Si significaran lo mismo, no sería necesario un método igual (). En otros idiomas, esto puede ser diferente. Por ejemplo, en Groovy, la identidad es el método is () y la igualdad es "==". Gracioso, ¿no es así?
jschreiner
15
Su crítica de mi uso de la palabra idéntico cuando debería haber usado la palabra equivalente es muy válida. Pero definir iguales en un objeto para que Foo y Bar sean "iguales" pero no "lo suficientemente iguales" para que los use de manera equivalente creará todo tipo de problemas tanto con la funcionalidad como con la legibilidad / mantenibilidad. Este problema con Set es solo la punta del iceberg para posibles problemas. Por ejemplo, los objetos iguales deben tener códigos hash iguales. Así que tendrá posibles colisiones de hash. ¿Es una locura objetar llamar a .get (foo) específicamente para obtener algo más que foo?
David Ogren
12
Probablemente valga la pena señalar que, por ejemplo, HashSet se implementa como un contenedor alrededor de un HashMap (que asigna las claves a un valor ficticio). Por lo tanto, el uso de un HashMap explícitamente en lugar de un HashSet no causaría una sobrecarga en el uso de la memoria.
Alexey B.
44
@ user686249 Siento que esto se ha convertido en un debate académico. Reconozco que podría haber estado por la borda al objetar a anular iguales. Especialmente en un uso como el tuyo. Pero, todavía tengo una objeción a la idea de llamar a este método get(). En su ejemplo, estaría muy confundido con customerSet.get (thisCustomer). (Mientras que un Mapa, como lo sugieren muchas respuestas) estaría bien con canonicalCustomerMap.get (este cliente). También estaría de acuerdo con un método que tenga un nombre más claro (como el método de miembro de Objective-C en NSSet).
David Ogren
19

Si tiene un objeto igual, ¿por qué necesita el del conjunto? Si es "igual" solo por una clave, una Mapsería una mejor opción.

De todos modos, lo siguiente lo hará:

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

Con Java 8 esto puede convertirse en una línea:

return all.stream().filter(sample::equals).findAny().orElse(null);
Arne Burmeister
fuente
Me gusta más esta respuesta, simplemente evitaría usar dos declaraciones de retorno, ya que eso está en contra de OOP y hace que el valor de Complejidad Ciclomática sea más alto.
Leo
8
@Leo gracias, pero el paradigma de salida única no está en contra de OOP y es mayormente inválido para idiomas más modernos que Fortran o COBOL, vea también softwareengineering.stackexchange.com/questions/118703/…
Arne Burmeister
1
Usar un mapa en lugar de un conjunto parece una mejor opción: iterar sobre los elementos de un conjunto es más trabajo que obtener un solo valor de un mapa. (O (N) vs O (1))
Jamie Flournoy
@JamieFlournoy bien, si tienes que verificar el mismo conjunto para diferentes elementos varias veces, eso es mucho mejor. Para un solo uso, no, ya que requiere más esfuerzo construir el mapa primero.
Arne Burmeister el
18

Convierta el conjunto en lista y luego use el getmétodo de lista

Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);
A Kra
fuente
37
No entiendo esto. Esto recuperará un objeto arbitrario del conjunto. No es el objeto.
aioobe
14

Desafortunadamente, el Conjunto predeterminado en Java no está diseñado para proporcionar una operación de "obtención", como explicó jschreiner con precisión.

Las soluciones de usar un iterador para encontrar el elemento de interés (sugerido por dacwe ) o para eliminar el elemento y volver a agregarlo con sus valores actualizados (sugerido por KyleM ) podrían funcionar, pero pueden ser muy ineficientes.

Anular la implementación de iguales para que los objetos no iguales sean "iguales", como lo indicó correctamente David Ogren , puede causar fácilmente problemas de mantenimiento.

Y usar un Mapa como un reemplazo explícito (como lo sugieren muchos), en mi opinión, hace que el código sea menos elegante.

Si el objetivo es obtener acceso a la instancia original del elemento contenido en el conjunto (espero haber entendido correctamente su caso de uso), aquí hay otra posible solución.


Personalmente tuve tu misma necesidad al desarrollar un videojuego cliente-servidor con Java. En mi caso, cada cliente tenía copias de los componentes almacenados en el servidor y el problema era cuando un cliente necesitaba modificar un objeto del servidor.

Pasar un objeto a través de Internet significaba que el cliente tenía diferentes instancias de ese objeto de todos modos. Para hacer coincidir esta instancia "copiada" con la original, decidí usar UUID de Java.

Así que creé una clase abstracta UniqueItem, que automáticamente proporciona una identificación única aleatoria para cada instancia de sus subclases.

Este UUID se comparte entre el cliente y la instancia del servidor, por lo que podría ser fácil hacer coincidirlos simplemente usando un Mapa.

Sin embargo, el uso directo de un Mapa en un caso de uso similar seguía siendo poco elegante. Alguien podría argumentar que usar un Mapa podría ser más complicado de mantener y manejar.

Por estas razones, implementé una biblioteca llamada MagicSet, que hace que el uso de un Mapa sea "transparente" para el desarrollador.

https://github.com/ricpacca/magicset


Al igual que el Java HashSet original, un MagicHashSet (que es una de las implementaciones de MagicSet provistas en la biblioteca) usa un HashMap de respaldo, pero en lugar de tener elementos como claves y un valor ficticio como valores, usa el UUID del elemento como clave y el elemento mismo como valor. Esto no causa sobrecarga en el uso de la memoria en comparación con un HashSet normal.

Además, un MagicSet se puede usar exactamente como un Set, pero con algunos métodos más que proporcionan funcionalidades adicionales, como getFromId (), popFromId (), removeFromId (), etc.

El único requisito para usarlo es que cualquier elemento que desee almacenar en un MagicSet necesita extender la clase abstracta UniqueItem.


Aquí hay un ejemplo de código, imaginando recuperar la instancia original de una ciudad de un MagicSet, dada otra instancia de esa ciudad con el mismo UUID (o incluso solo su 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
}
ricpacca
fuente
11

Si su conjunto es de hecho un NavigableSet<Foo>(como un TreeSet), y Foo implements Comparable<Foo>, puede usar

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

(Gracias al comentario de @eliran-malka por la pista).

Jesse Glick
fuente
55
Si no me importara que alguien leyera mi código teniendo el pensamiento inicial de que me había vuelto completamente loco, esta sería una gran solución.
Adam
10

Con Java 8 puedes hacer:

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

Pero tenga cuidado, .get () arroja una NoSuchElementException, o puede manipular un elemento Opcional.

clima nublado
fuente
55
item->item.equals(theItemYouAreLookingFor)se puede acortar atheItemYouAreLookingFor::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);

Si solo obtiene uno, esto no será muy eficaz porque pasará sobre todos sus elementos, pero al realizar múltiples recuperaciones en un conjunto grande notará la diferencia.

Xymon
fuente
5

Por qué:

Parece que Set juega un papel útil al proporcionar un medio de comparación. Está diseñado para no almacenar elementos duplicados.

Debido a esta intención / diseño, si uno obtuviera () una referencia al objeto almacenado, luego lo mutara, es posible que las intenciones de diseño de Set se frustraran y pudieran causar un comportamiento inesperado.

De los JavaDocs

Se debe tener mucho cuidado si se utilizan objetos mutables como elementos establecidos. El comportamiento de un conjunto no se especifica si el valor de un objeto se cambia de una manera que afecta a las comparaciones iguales mientras el objeto es un elemento del conjunto.

Cómo:

Ahora que se han introducido Streams, uno puede hacer lo siguiente

mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();
Jason M
fuente
2

¿Qué pasa con el uso de la clase 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);
    }
}

salida:
elementos uno, dos

velocidad
fuente
1

Lo sé, esto se ha preguntado y respondido hace mucho tiempo, sin embargo, si alguien está interesado, aquí está mi solución: clase de conjunto personalizado respaldada por HashMap:

http://pastebin.com/Qv6S91n9

Puede implementar fácilmente todos los demás métodos Set.

Lukas Z.
fuente
77
Se prefiere incluir el ejemplo en lugar de simplemente vincularlo a uno.
Todos los trabajadores son esenciales
1

He estado allí hecho eso! Si está utilizando Guava, una forma rápida de convertirlo en un mapa es:

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

puedes usar la clase 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);   
 }
 }
}
chiha asma
fuente
1

Si desea enésimo elemento de HashSet, puede ir con la solución a continuación, aquí he agregado el objeto de ModelClass en 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;
    }
}
Hardik Patel
fuente
1

Si observa las primeras líneas de implementación java.util.HashSet, verá:

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

De todos modos, se HashSetusa de forma HashMapinterna, lo que significa que si solo usa un HashMapdirectamente y usa el mismo valor que la clave y el valor, obtendrá el efecto que desea y se ahorrará algo de memoria.

rghome
fuente
1

parece que el objeto apropiado para usar es el Interner from guava:

Proporciona un comportamiento equivalente a String.intern () para otros tipos inmutables. Las implementaciones comunes están disponibles en la clase Interners .

También tiene algunas palancas muy interesantes, como concurrencyLevel o el tipo de referencias utilizadas (vale la pena señalar que no ofrece un SoftInterner que podría ver como más útil que un WeakInterner).

molyss
fuente
0

Debido a que cualquier implementación particular de Set puede o no tener acceso aleatorio .

Siempre puede obtener un iterador y recorrer el conjunto, utilizando el next()método de los iteradores para devolver el resultado que desea una vez que encuentre el elemento igual. Esto funciona independientemente de la implementación. Si la implementación NO es un acceso aleatorio (imagínese un conjunto respaldado por una lista vinculada), un get(E element)método en la interfaz sería engañoso, ya que tendría que iterar la colección para encontrar el elemento a devolver, y get(E element)parecería que esto implicaría que sería necesario, que el Conjunto pueda saltar directamente al elemento a obtener.

contains() puede o no tener que hacer lo mismo, por supuesto, dependiendo de la implementación, pero el nombre no parece prestarse al mismo tipo de malentendidos.

Tom Tresansky
fuente
2
Todo lo que haría el método get () ya lo está haciendo el método contiene (). No puede implementar contiene () sin obtener el objeto contenido y llamar a su método .equals (). Los diseñadores de API parecían no tener reparos en agregar un get () a java.util.List a pesar de que sería lento en algunas implementaciones.
Bryan Rink
No creo que esto sea cierto. Dos objetos pueden ser iguales mediante iguales, pero no idénticos mediante ==. Si tuviera el objeto A, y un conjunto S que contiene el objeto B, y A. iguala (B) pero A! = B y desea obtener una referencia a B, puede llamar a S.get (A) para obtener una referencia a B, suponiendo que tuviera un método get con la semántica del método get de List, que es un caso de uso diferente que verificar si S. contiene (A) (lo que sería). Esto ni siquiera es un caso de uso raro para colecciones.
Tom Tresansky el
0

Sí, use HashMap... pero de una manera especializada: la trampa que preveo al tratar de usar a HashMapcomo pseudo- Setes la posible confusión entre elementos "reales" de los elementos Map/Set"candidatos", es decir, elementos utilizados para probar si un equalEl elemento ya está presente. Esto está lejos de ser infalible, pero te aleja de la trampa:

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);
    }
}

Entonces haz esto:

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

Pero ... ahora quieres candidateque se autodestruya de alguna manera a menos que el programador lo ponga inmediatamente en Map/Set... querrás contains"contaminar" candidatepara que su uso a menos que se una al Mapanatema lo haga "anatema" ". Quizás podría hacer SomeClassimplementar una nueva Taintableinterfaz.

Una solución más satisfactoria es un GettableSet , como se muestra a continuación. Sin embargo, para que esto funcione, usted debe estar a cargo del diseño SomeClasspara hacer que todos los constructores no sean visibles (o ... capaces y dispuestos a diseñar y usar una clase de envoltura para ello):

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
}

Implementación:

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 ...
}

Sus NoVisibleConstructorclases se ven así:

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 técnico con tal NoVisibleConstructorclase: se puede objetar que tal clase es inherentemente final, lo que puede ser indeseable. En realidad, siempre puedes agregar un protectedconstructor ficticio sin parámetros :

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

... que al menos permitiría que se compilara una subclase. Debería pensar si necesita incluir otro getOrCreate()método de fábrica en la subclase.

El paso final es una clase base abstracta (NB "elemento" para una lista, "miembro" para un conjunto) como esta para los miembros de su conjunto (cuando sea posible, de nuevo, alcance para usar una clase envolvente donde la clase no está bajo su control, o ya tiene una clase base, etc.), para una máxima ocultación de la implementación:

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 );
}

... uso es bastante obvio (dentro de su SomeClass's staticmétodo de fábrica):

SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );
Mike roedor
fuente
0

El contrato del código hash deja en claro que:

"Si dos objetos son iguales de acuerdo con el método Object, entonces llamar al método hashCode en cada uno de los dos objetos debe producir el mismo resultado entero".

Entonces su suposición:

"Para aclarar, el método igual se anula, pero solo verifica uno de los campos, no todos. Por lo tanto, dos objetos Foo que se consideran iguales pueden tener valores diferentes, por eso no puedo usar foo".

está mal y estás rompiendo el contrato. Si observamos el método "contiene" de la interfaz Set, tenemos que:

boolean contiene (Objeto o);
Devuelve verdadero si este conjunto contiene el elemento especificado. Más formalmente, devuelve verdadero si y solo si este conjunto contiene un elemento "e" tal que o == nulo? e == nulo: o.equals (e)

Para lograr lo que desea, puede usar un Mapa donde defina la clave y almacene su elemento con la clave que define cómo los objetos son diferentes o iguales entre sí.

jfajunior
fuente
-2

Método de ayuda rápida que podría abordar esta situación:

<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();
}
Chris Willmore
fuente
8
Es muy extraño que esta respuesta haya recibido tantos votos positivos, ya que no responde la pregunta ni intenta abordarla de ninguna manera.
David Conrad el
-2

Seguir puede ser un enfoque

   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]));
   }
Vikrant
fuente
-2

Intenta usar una matriz:

ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
canni
fuente