2009-08-26 16 views
28

¿Cuál es la forma más rápida de eliminar un elemento de un mapa por valor en Java?¿Cuál es la forma más rápida de eliminar un elemento de un mapa por valor en Java?

Actualmente estoy usando:

DomainObj valueToRemove = new DomainObj(); 
    String removalKey = null; 

    for (Map.Entry<String, DomainObj> entry : map.entrySet()) { 
     if (valueToRemove.equals(entry.getValue())) { 
      removalKey = entry.getKey(); 
      break; 
     } 
    } 

    if (removalKey != null) { 
     map.remove(removalKey); 
    } 
+0

estoy usando HashMap de Java – Supertux

Respuesta

25

Sin utilizar un mapa bi-direccional (commons-collections y google collections ellos tienen), que está pegado con la iteración el Mapa

+1

1 recomendar Google Colecciones BIMAP. Es más rápido, pero viene con una carga de memoria. – notnoop

+0

O puede mantener dos mapas, que es lo que hacen estos mapas. –

4

Si usted no tiene ninguna manera de averiguar la clave de DomainObj, entonces no veo cómo puede mejorar eso. No hay un método incorporado para obtener la clave del valor, por lo que tiene para recorrer el mapa.

Si esto es algo que está haciendo todo el tiempo, puede mantener dos mapas (string-> DomainObj y DomainObj-> Key).

+0

+1. Debería encontrar una manera de almacenar su valor como algún tipo de clave si desea optimizar. Si no puede, no puede elegir: debe iterar. –

9

Si no tiene un mapa inverso, buscaría un iterador.

DomainObj valueToRemove = new DomainObj(); 

for (
    Iterator<Map.Entry<String, DomainObj>> iter = map.entrySet().iterator(); 
    iter.hasNext(); 
) { 
    Map.Entry<String, DomainObj> entry = iter.next(); 
    if (valueToRemove.equals(entry.getValue())) { 
     iter.remove(); 
     break; // if only want to remove first match. 
    } 
} 
+4

de acuerdo. La única otra cosa que mencionaría es que si necesita eliminar por valor a menudo, asegúrese de que un Mapa sea la estructura de datos real que debe usar para su escenario. –

7

Siempre se puede utilizar la colección de valores, ya que los cambios realizados en esa colección se traducirá en el cambio que se refleja en el mapa. Entonces, si llamaras a Map.values ​​(). Remove (valueToRemove) eso debería funcionar, aunque no estoy seguro si verás el rendimiento mejor de lo que tienes con ese bucle. Una idea sería ampliar o anular la clase de mapa de modo que la colección de respaldo siempre esté ordenada por valor, lo que le permitiría hacer una búsqueda binaria en el valor que puede ser más rápido.

Editar: Esto es esencialmente lo mismo que la respuesta de Alcon, pero no creo que funcione porque el entrySet todavía se ordenará por clave, en cuyo caso no se puede llamar a .remove() con el valor .

Esto también supone que se supone que el valor es único o que también debería eliminar los duplicados del mapa.

6

, lo usaría esta

Map x = new HashMap(); 
x.put(1, "value1"); 
x.put(2, "value2"); 
x.put(3, "value3"); 
x.put(4, "value4"); 
x.put(5, "value5"); 
x.put(6, "value6"); 

x.values().remove("value4"); 

edición: porque los objetos son referenciados por "puntero" no por valor.

N

0

No creo que esto va a suceder sólo una vez en la vida de su aplicación.

Entonces, lo que yo haría, es delegar a otro objeto la responsabilidad de mantener una referencia a los objetos agregados a ese mapa.

Así que la próxima vez que necesite para eliminarlo, se utiliza ese "mapa inversa" ...

class MapHolder { 

    private Map<String, DomainObj> originalMap; 
    private Map<DomainObj,String> reverseMap; 

    public void remove(DomainObj value) { 
      if (reverseMap.contains(value)) { 
       originalMap.remove(reverseMap.get(value)); 
       reverseMap.remove(value); 
      } 
    } 
    } 

Esto es mucho más rápido que la iteración.

Obviamente, necesita mantenerlos sincronizados. Pero no debería ser tan difícil si refectorio de su código para tener un objeto que sea responsable del estado del mapa.

Recuerda que en OOP tenemos objetos que tienen un estado y comportamiento.Si sus datos están pasando variables por todos lados, está creando dependencias innecesarias entre los objetos

Sí, le tomará un tiempo corregir el código, pero el tiempo dedicado a corregirlo le ahorrará muchos dolores de cabeza en el futuro. Piénsalo.

+1

Este es un caso de "reinvención de la rueda", o en este caso, el mapa bidireccional. Google Collections ofrece una implementación completa. –

+2

requiere que el valor sea único, lo que no es necesariamente así. –

+0

@Steve: Bueno, por supuesto, se debe agregar mucho más para el código (como implementar la interfaz de Map para poder sincronizar put y get) Esto es más como un "boceto" de la sugerencia y no como un "ready to copy"/pegar "solución. Y sí, como alternativa podría haber Google Collections, pero se aplica el mismo principio, debe haber un objeto responsable de ese mapa y bidireccional, y no referencias en todo el código. Gracias por el comentario. – OscarRyz

2

Como la mayoría de los otros carteles han dicho, generalmente es una operación O (N) porque tendrá que buscar en toda la lista de valores hashtable independientemente. @tackline tiene la solución correcta para mantener el uso de la memoria en O (1) (le di un voto positivo para eso).

Su otra opción es sacrificar el espacio de la memoria por el bien de la velocidad. Si su mapa tiene un tamaño razonable, puede almacenar dos mapas en paralelo.

Si tiene un Mapa, mantenga un Mapa en paralelo. Cuando inserta/elimina en un mapa, hágalo también en el otro. De acuerdo, esto es más feo porque estás perdiendo espacio y debes asegurarte de que el método "hashCode" de DomainObj está escrito correctamente, pero tu tiempo de eliminación se reduce de O (N) a O (1) porque puedes buscar la clave/mapeo de objetos en tiempo constante en cualquier dirección.

Generalmente no es la mejor solución, pero si su preocupación número uno es la velocidad, creo que esto es probablemente lo más rápido que obtendrá.

==================== Apéndice: Esto es lo que se sugirió esencialmente @msaeed simplemente sin la biblioteca de terceros.

2

Un uso más breve del iterador es usar un iterador de valores().

DomainObj valueToRemove = new DomainObj(); 
for (Iterator<DomainObj> it = map.values().iterator(); it.hasNext();)) { 
     if (valueToRemove.equals(it.next())) { 
       it.remove(); 
       break; 
     } 
} 
19

Aquí está la solución de una sola línea:

map.values().remove(valueToRemove); 

Eso es probablemente más rápido que la definición de su propio repetidor, ya que el código de colección JDK ha sido optimizado de manera significativa.

Como han mencionado otros, un bimap tendrá una eliminación de valor más rápida, aunque requiere más memoria y tarda más en rellenarse. Además, un bimap solo funciona cuando los valores son únicos, lo que puede o no ser el caso en su código.

59

El one-liner correcto y rápido sería en realidad: while (map.values().remove(valueObject));.

Tipo de extraño que la mayoría de los ejemplos anteriores asumen que el objeto de valor es único.

+0

que funciona en casos de uso básicos, no con un predicado proporcionado. – Rolf

Cuestiones relacionadas