Obtener un elemento de un conjunto

Resuelto foobar asked hace 13 años • 22 respuestas

¿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 en realidad pueden tener valores diferentes, es por eso que no puedo usar simplemente foo.

foobar avatar Sep 02 '11 19:09 foobar
Aceptado

Para responder a la pregunta precisa "¿ Por qué no se Setproporciona una operación para obtener un elemento que sea igual a otro elemento?", la respuesta sería: porque los diseñadores del marco de la colección no fueron muy progresistas. No anticiparon su caso de uso muy legítimo, intentaron ingenuamente "modelar la abstracción del conjunto matemático" (del javadoc) y simplemente se olvidaron de 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 a Map<E,E>en lugar de a Set<E>para asignar los elementos a sí mismos. De esa manera, puede recuperar eficientemente un elemento del "conjunto", porque el método get() de Mapencontrará el elemento utilizando una tabla hash eficiente o un algoritmo de árbol. Si lo desea, puede escribir su propia implementación que ofrezca el método Setadicional , encapsulando el archivo .get()Map

En mi opinión, las siguientes respuestas son malas o incorrectas:

"No necesitas obtener el elemento, porque ya tienes un objeto igual": la afirmación es incorrecta, como ya mostraste en la pregunta. Dos objetos que son iguales aún pueden tener estados diferentes que no son relevantes para la igualdad del objeto. El objetivo es obtener acceso a este estado del elemento contenido en Set, no al estado del objeto utilizado como "consulta".

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

jschreiner avatar Aug 22 '2013 12:08 jschreiner

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


Si aún deseas encontrar el elemento no tienes 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 avatar Sep 02 '2011 12:09 dacwe

Si tienes un objeto igual, ¿por qué necesitas el del conjunto? Si es "igual" sólo por una clave, a 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 sola línea:

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

o (si equals()funciona como se esperaba):

return all.contains(sample) ? sample : null;
Arne Burmeister avatar Sep 02 '2011 12:09 Arne Burmeister