the questions interview framework example collection colecciones java collections

java - questions - HashSet.remove() y Iterator.remove() no funcionan



java list (9)

¿Has probado algo como

boolean removed = allResults.remove(oldData) if (!removed) // COMPLAIN BITTERLY!

En otras palabras, elimine el objeto del Conjunto y rompa el ciclo. Eso no causará que el Iterator queje. No creo que sea una solución a largo plazo, pero probablemente te brinde información sobre los hashCode , equals y equalsData

Estoy teniendo problemas con Iterator.remove () llamado en un HashSet.

Tengo un conjunto de objetos marcados con el tiempo. Antes de agregar un nuevo elemento al conjunto, recorro el conjunto, identifico una versión anterior de ese objeto de datos y lo elimino (antes de agregar el nuevo objeto). la marca de tiempo está incluida en hashCode y es igual a (), pero no es igual aData ().

for (Iterator<DataResult> i = allResults.iterator(); i.hasNext();) { DataResult oldData = i.next(); if (data.equalsData(oldData)) { i.remove(); break; } } allResults.add(data)

Lo curioso es que i.remove () falla silenciosamente (sin excepción) para algunos de los elementos del conjunto. He verificado

  • La línea i.remove () se llama realmente. Puedo llamarlo desde el depurador directamente en el punto de interrupción en Eclipse y todavía no se puede cambiar el estado de Set

  • DataResult es un objeto inmutable por lo que no puede haber cambiado después de haberse agregado originalmente al conjunto.

  • Los métodos equals y hashCode () usan @Override para asegurarse de que sean los métodos correctos. Las pruebas unitarias verifican este trabajo.

  • Esto también falla si solo uso una declaración for y Set.remove en su lugar. (por ejemplo, recorrer los elementos, encontrar el elemento en la lista, luego llamar a Set.remove (oldData) después del bucle).

  • He probado en JDK 5 y JDK 6.

Pensé que debía perderme algo básico, pero después de pasar un tiempo significativo en esto, mi colega y yo estamos perplejos. ¿Alguna sugerencia de cosas para verificar?

EDITAR:

Ha habido preguntas: DataResult es realmente inmutable. Sí. No hay setters Y cuando se recupera el objeto Date (que es un objeto mutable), se hace creando una copia.

public Date getEntryTime() { return DateUtil.copyDate(entryTime); } public static Date copyDate(Date date) { return (date == null) ? null : new Date(date.getTime()); }

MÁS EDITACIÓN (algún tiempo después): Para el registro, ¡DataResult no era inmutable! Hacía referencia a un objeto que tenía un código hash que cambiaba cuando se conservaba en la base de datos (mala práctica, lo sé). Resultó que si se creaba un DataResult con un subobjeto transitorio, y el subobjeto se conservaba, se cambiaba el hashcode DataResult.

Muy sutil: miré esto muchas veces y no noté la falta de inmutabilidad.


¿Estás absolutamente seguro de que DataResult es inmutable? ¿Cuál es el tipo de marca de tiempo? Si es un java.util.Date ¿está haciendo copias de él cuando está inicializando DataResult? Tenga en cuenta que java.util.Date es mutable.

Por ejemplo:

Date timestamp = new Date(); DataResult d = new DataResult(timestamp); System.out.println(d.getTimestamp()); timestamp.setTime(System.currentTimeMillis()); System.out.println(d.getTimestamp());

Imprimiría dos veces diferentes.

También sería útil si pudieras publicar algún código fuente.


Bajo las cubiertas, HashSet usa HashMap, que llama a HashMap.removeEntryForKey (Object) cuando se llama a HashSet.remove (Object) o Iterator.remove (). Este método usa tanto hashCode () como equals () para validar que está eliminando el objeto apropiado de la colección.

Si ambos Iterator.remove () y HashSet.remove (Object) no funcionan, entonces algo definitivamente está mal con los métodos equals () o hashCode (). Publicar el código para estos sería útil en el diagnóstico de su problema.


Gracias por toda la ayuda. Sospecho que el problema debe ser con equals () y hashCode () como lo sugiere spencerk. Los revisé en mi depurador y con pruebas unitarias, pero me falta algo.

Terminé haciendo una solución: copiar todos los elementos, excepto uno a un nuevo conjunto. Para las patadas, utilicé Apache Commons CollectionUtils.

Set<DataResult> tempResults = new HashSet<DataResult>(); CollectionUtils.select(allResults, new Predicate() { public boolean evaluate(Object oldData) { return !data.equalsData((DataResult) oldData); } } , tempResults); allResults = tempResults;

Voy a parar aquí. Demasiado trabajo para simplificar a un simple caso de prueba. Pero la ayuda es muy apreciada.


No estoy actualizado con mi Java, pero sé que no puede eliminar un elemento de una colección cuando está iterando sobre esa colección en .NET, aunque .NET emitirá una excepción si lo detecta. ¿Podría ser este el problema?


Si hay dos entradas con los mismos datos, solo se reemplaza una de ellas ... ¿ha contabilizado eso? Y por si acaso, ¿ha probado otra estructura de datos de colección que no utiliza un código hash, por ejemplo, una lista?


Todavía tenía mucha curiosidad sobre este, y escribí la siguiente prueba:

import java.util.HashSet; import java.util.Iterator; import java.util.Random; import java.util.Set; public class HashCodeTest { private int hashCode = 0; @Override public int hashCode() { return hashCode ++; } public static void main(String[] args) { Set<HashCodeTest> set = new HashSet<HashCodeTest>(); set.add(new HashCodeTest()); System.out.println(set.size()); for (Iterator<HashCodeTest> iter = set.iterator(); iter.hasNext();) { iter.next(); iter.remove(); } System.out.println(set.size()); } }

lo que resulta en:

1 1

Si el valor de hashCode () de un objeto ha cambiado desde que se agregó al HashSet, parece que el objeto no se puede quitar.

No estoy seguro de si ese es el problema con el que se está encontrando, pero es algo a tener en cuenta si decide volver a visitar esto.


Es casi seguro que los códigos hash no coincidan con los datos antiguos y nuevos que son "iguales ()". Me encontré con este tipo de cosas antes y esencialmente terminas arrojando hashcodes para cada objeto y la representación de la cadena y tratando de descubrir por qué está sucediendo la falta de coincidencia.

Si está comparando elementos antes / después de la base de datos, a veces pierde los nanosegundos (dependiendo de su tipo de columna de base de datos) que pueden hacer que cambien los códigos hash.


Todos deben tener cuidado con cualquier colección de Java que obtenga sus hijos mediante hashcode, en el caso de que el código de hash de su hijo dependa de su estado mutable. Un ejemplo:

HashSet<HashSet<?>> or HashSet<AbstaractSet<?>> or HashMap variant:

HashSet recupera un elemento por su hashCode, pero su tipo de elemento es un HashSet, y hashSet.hashCode depende del estado de su elemento.

Código para ese asunto:

HashSet<HashSet<String>> coll = new HashSet<HashSet<String>>(); HashSet<String> set1 = new HashSet<String>(); set1.add("1"); coll.add(set1); print(set1.hashCode()); //---> will output X set1.add("2"); print(set1.hashCode()); //---> will output Y coll.remove(set1) // WILL FAIL TO REMOVE (SILENTLY)

El método de eliminación de HashSet utiliza HashMap e identifica claves mediante hashCode, mientras que hashCode de AbstractSet es dinámico y depende de las propiedades mutables de sí mismo.