recorrer manejo interseccion funciona ejemplo conjuntos como arreglos java collections set equals

manejo - set java ejemplo



Obteniendo un elemento de un conjunto (20)

Con Java 8 puedes hacer:

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

Pero tenga cuidado, .get () lanza una excepción NoSuchElementException, o puede manipular un elemento opcional.

¿Por qué Set no proporciona una operación para obtener un elemento que es 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 el Set contiene un elemento igual a la bar , ¿por qué no puedo obtener ese elemento? :(

Para aclarar, el método equals está anulado, pero solo comprueba uno de los campos, no todos. Entonces, dos objetos Foo que se consideran iguales pueden tener valores diferentes, por eso no puedo usar foo .


Convierte set a list, y luego usa get method of list

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


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

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.

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

Y al usar un Mapa como un reemplazo explícito (como lo sugieren muchos), imho, 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 que haya entendido correctamente su caso de uso), esta es otra solución posible.

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 cada vez que 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 los UUID de Java.

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

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

Sin embargo, el uso directo de un Mapa en un caso de uso similar seguía sin ser 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 que se proporciona 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 en sí 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 conjunto, 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 desde 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 }



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


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.


Lo siguiente 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])); }


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


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

Si aún 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); } }


Para responder a la pregunta precisa " ¿ Por qué Set no proporciona 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 avanzados. No anticiparon su caso de uso muy legítimo, intentaron ingenuamente "modelar la abstracción de conjuntos matemáticos" (desde el javadoc) y simplemente se olvidaron de agregar el método útil get() .

Ahora, para la pregunta implícita " cómo se obtiene el elemento entonces": creo que la mejor solución es usar un Map<E,E> lugar de un Set<E> , para asignar los elementos a ellos mismos. De esa manera, puede recuperar un elemento del "conjunto" de manera eficiente , ya que el método get () del Map buscará el elemento utilizando una tabla hash o un algoritmo de árbol eficiente. Si lo desea, puede escribir su propia implementación de Set que ofrece el método adicional get() , que encapsula 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 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 al estado del objeto utilizado como una "consulta".

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


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 y luego lo mutara, es posible que las intenciones de diseño de Set se frustren y causen un comportamiento inesperado.

De los JavaDocs

Se debe tener mucho cuidado si los objetos mutables se utilizan 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 que el objeto es un elemento en el conjunto.

Cómo:

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

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


Porque cualquier implementación particular de Set puede o no ser un acceso aleatorio .

Siempre puede obtener un iterator y recorrer el conjunto, utilizando el método next() 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 de acceso aleatorio (imagínese un conjunto respaldado por listas vinculadas), un método de get(E element) en la interfaz sería engañoso, ya que tendría que iterar la colección para encontrar el elemento a devolver, y una get(E element) parece implicar que esto sería necesario, que el Conjunto podría 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 a la misma clase de malentendidos.


Sí, use HashMap ... pero de una manera especializada: la trampa que preveo al tratar de usar un HashMap como un pseudo- Set es la posible confusión entre los elementos "reales" del Map/Set y los elementos "candidatos", es decir Elementos utilizados para probar si un elemento equal 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 quiere que el candidate se autodestruya de alguna manera, a menos que el programador lo coloque de inmediato en el Map/Set ... que desearía que contains "manchar" al candidate para que cualquier uso del mismo, a menos que Unirse al Map hace "anatema". Quizás podrías hacer que SomeClass implemente una nueva interfaz de Taintable .

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

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

Tus clases de NoVisibleConstructor 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 una clase NoVisibleConstructor : se puede objetar que dicha clase es inherentemente final , lo que puede ser indeseable. En realidad, siempre se podría agregar un constructor protected parámetros ficticios:

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

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

El paso final es una clase base abstracta (NB "elemento" para una lista, "miembro" para un conjunto) como este para los miembros de su conjunto (cuando sea posible, de nuevo, el alcance para usar una clase envoltura donde la clase no está bajo su control, o ya tiene una clase base, etc.), para la 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 ); }

... el uso es bastante obvio (dentro del método de fábrica static SomeClass ):

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


Si desea el nth Element de HashSet, puede ir con la siguiente solución, aquí he agregado un 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; } }


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

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

Por HashSet tanto, HashSet usa HashMap interna, lo que significa que si solo usas un HashMap directamente y usas el mismo valor que la clave y el valor, obtendrás el efecto que deseas y te ahorrarás algo de memoria.


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


Si tiene un objeto igual, ¿por qué necesita uno del conjunto? Si es "igual" solo por una clave, un Map serí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 un liner:

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


Trate de usar una matriz:

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


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


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 haces una, esto no será muy rentable porque recorrerás todos tus elementos, pero cuando realices recuperaciones múltiples en un conjunto grande, notarás la diferencia.