2012-02-24 15 views
6

Tengo un error muy extraño. Me encontré con esto cuando originalmente estaba usando keySet() para iterar sobre las primeras 10 claves de un TreeMap grande. Una de las claves era devolver nulo, lo que no debería ser posible en mi opinión. Así que escribí el código de prueba a continuación:Clave en TreeMap que devuelve nulo

int i = 0; 
     for (Map.Entry<String, Integer> es : sortedMap.entrySet()){ 
      if (i >= 10) { 
       break; 
      } 

      if (sortedMap.containsKey(es.getKey())){ 
       System.out.println(es.getKey() + ":" + sortedMap.get(es.getKey())); 
      } else { 
       System.out.println("Key " + es.getKey() + " does not exist, yet..."); 
       System.out.println("This does work: " + es.getKey() + ":" + es.getValue()); 
       System.out.println("This does NOT work: " + es.getKey() + ":" + sortedMap.get(es.getKey())); 
      } 
      i++; 
     } 

y obtener los siguientes resultados:

SOAP:967 
'excerpt'::679 
'type'::679 
Key 'author_url': does not exist, yet... 
This does work: 'author_url'::679 
This does NOT work: 'author_url'::null 
'date'::679 
Android:437 
TLS:295 
message:283 
server:230 
monthly:215 
<<<<<<<<<<<<<<<<<<<<DUMPING MAP! 
{SOAP=967, 'excerpt':=679, 'type':=679, 'author_url':=679, 'date':=679, Android=437, TLS=295, message=283, server=230, monthly=215... 

que cortar el mapa después de los diez primeros, ya que hay mucho más en allí, pero toda ella es una clave con un valor

Así que mi pregunta es esta: ¿Por qué estoy obteniendo un nulo cuando uso la clave para obtener directamente (clave) desde el TreeMap, pero el EntrySet devuelve la clave correcta y el valor?

Aquí es mi comparador ya que estoy pidiendo en Entero:

class ValueComparator implements Comparator<Object> { 

    Map<String, Integer> base; 
    public ValueComparator(Map<String, Integer> base) { 
     this.base = base; 
    } 

    public int compare(Object a, Object b) { 

    if ((Integer) base.get(a) < (Integer) base.get(b)) { 
     return 1; 
    } else if ((Integer) base.get(a) == (Integer) base.get(b)) { 
     return 0; 
    } else { 
     return -1; 
    } 
    } 
} 

Y el TreeMap se construye de la siguiente manera:

ValueComparator bvc = new ValueComparator(allMatches); 
TreeMap<String, Integer> sortedMap = new TreeMap<String, Integer>(bvc); 
//Sort the HashMap 
sortedMap.putAll(allMatches); 

Dónde allMatches es un HashMap<String, Integer>

+4

¿Está utilizando un comparador inusual para TreeMap? Si es así, ¿podemos ver su código? Se ve desde su volcado como si no estuviera usando el orden 'String' predeterminado ... –

+0

@LouisWasserman Agregué mi comparador. –

+2

¿Por qué tienes un constructor y un miembro en esa clase? El comparador normalmente se llama para cada elemento, por lo que no debe hacer referencia al mapa resultante. Tu código completo sería útil. – tom

Respuesta

1

problema resuelto:

class ValueComparator implements Comparator<Object> { 

Map<String, Integer> base; 

public ValueComparator(Map<String, Integer> base) { 
    this.base = base; 
} 

public int compare(Object a, Object b) { 

    if (((Integer) base.get(a)).intValue() < ((Integer) base.get(b)).intValue()) { 
     return 1; 
    } else if (((Integer) base.get(a)).intValue() == ((Integer) base.get(b)).intValue()) { 
     return ((String)a).compareTo(((String)b)); 
    } else { 
     return -1; 
    } 
} 
} 

Esto viene con el beneficio adicional de traer de vuelta k eys con el mismo valor en orden alfabético.

7

Del orden de iteración muestra su TreeMap, es definitivamente el caso de que utilizó un Comparator personalizado. [De lo contrario la iteración habría estado en orden lexicográfico]

Tenga en cuenta que de acuerdo con el javadocs:

El implementador debe garantizar que sgn (compárese con (x, y)) == -sgn (comparar (y , x)) para todos x e y. (Esto implica que comparar (x, y) debe lanzar una excepción si y sólo si se compara (y, x) se produce una excepción.)

El implementador también debe asegurarse de que la relación es transitiva: ((compare (x, y)> 0) & & (compare (y, z)> 0)) implica compare (x, z)> 0.

Finalmente, el implementador debe asegurarse de que compare (x, y) == 0 implica que sgn (compare (x, z)) == sgn (compare (y, z)) para todas las z.

Si su Comparator no aplica estas reglas, el comportamiento no está definido, ya que podría mostrar resultados extraños, como puede ver.

EDIT: [como respuesta a la pregunta Editted]
Su compartor utiliza la identidad [operator==] para comprobar dos enteros.
Tenga en cuenta que Integer es un objeto, por lo que operator== devolverá true solo si es el mismo objeto.
Debe utilizar equals() para comprobar si dos números enteros son idénticos - o incluso mejor - utilizar Integer.compareTo()

+0

El uso de compareTo hace que TreeMap colapse en cualquier valor que sea igual. '{monthly = 215, server = 230, message = 283, TLS = 295, Android = 475, excerpt = 679, SOAP = 967}' –

+0

De hecho, y eso es una pista de que su código está aún más roto. No puedes usar 'TreeMap' en absoluto, no como si estuvieras tratando de usarlo. –

+0

@DennisSullivan: Además de lo que Louis dijo: Lea los documentos java adjuntos. Debe asegurarse de que su Comparador cumpla con los términos escritos; de lo contrario, el comportamiento no está definido. – amit

3

Su mayor problema es que el uso de == en lugar de .equals en su comparador de valores se está rompiendo cosas, porque las diferentes teclas se están mapeados a diferentes objetos Integer con el mismo intValue(), que está arrojando aún más cosas impredeciblemente.

Pero si lo arreglaste, entonces tu TreeMap no te dejaría insertar varias llaves con el mismo valor, lo que casi seguramente también está causando roturas sutiles.

Una mejor solución sería algo así como this, pero básicamente, se debe rellenar un mapa sin clasificar por valores, ordenar la entrySet, y luego copiar las entradas (en orden) a un mapa como LinkedHashMap que no necesita un comparador, pero solo mantiene las entradas en el orden de inserción.

puede ser capaz de cambiar su comparador de modo que si los valores son los mismos, comparará las claves también. Esto al menos le permitiría insertar varias claves con el mismo valor ... pero sigue siendo una solución realmente hacky que es mucho más riesgosa que una solución basada en LinkedHashMap como se describió anteriormente.

+0

Voté por ayudar a mi línea de pensamiento para llegar a la respuesta correcta. –

0

Simplemente debe tener:

class ValueComparator implements Comparator<Integer> { 


    public int compare(Integer a, Integer b) { 
     return a.compareTo(b); 
    } 
} 

continuación, tiene que inicializar el mapa de árbol con el comparador y añadir todos sus artículos:

Treemap

+0

Lo está usando para comparar los valores, que no va a funcionar con un 'TreeMap', realmente no = ( –

+0

Tiene razón.Creo que estaba clasificando las llaves (como de costumbre) pero está ordenando valores. Leí mal la pregunta. Todo lo que puedo decir es que ordenar valores no se hace normalmente en un mapa. Las listas son más adecuadas para eso. – tom

Cuestiones relacionadas