Pregunta sobre bytearray, hashmap, java – Usando una matriz de bytes como clave de mapa

70

¿Ves algún problema con el uso de una matriz de bytes como clave de mapa? Yo tambien podria hacernew String(byte[]) y hash porString pero es más sencillo de usarbyte[].

Tu respuesta

10   la respuesta
39

yte [] con un comparador)

HashMap<ByteBuffer, byte[]> kvs = new HashMap<ByteBuffer, byte[]>();
byte[] k1 = new byte[]{1,2 ,3};
byte[] k2 = new byte[]{1,2 ,3};
byte[] val = new byte[]{12,23,43,4};

kvs.put(ByteBuffer.wrap(k1), val);
System.out.println(kvs.containsKey(ByteBuffer.wrap(k2)));

imprimirá

true
Tenga en cuenta que: "Dado que los códigos hash del búfer dependen del contenido, no es aconsejable utilizar los búferes como claves en mapas hash o estructuras de datos similares a menos que se sepa que su contenido no cambiará" (docs.oracle.com/javase/7/docs/api/java/nio/…) LMD
+1 para la mayoría de los envoltorios de bytes ligeros (creo ...) Nicholas
Esta sería una buena solución, pero si desea serializar el mapa (como en mi caso) no puede usar este enfoque. 501 - not implemented
Esto funciona bien con ByteBuffer.wrap (), pero tenga cuidado si el contenido de ByteBuffer se ha creado utilizando un par de llamadas put () para crear una matriz de bytes de clave compuesta. En este caso, la última llamada put () debe ir seguida de una llamada rewind (); de lo contrario, igual a () devuelve true incluso cuando las matrices de bytes subyacentes contienen datos diferentes. RenniePet
1

hashCode() yequals(Object) Métodos intuitivamente. Es decir, dos matrices de bytes idénticas no compartirán necesariamente el mismo código hash y no necesariamente reclamarán ser iguales. Sin estos dos rasgos, su HashMap se comportará de manera inesperada.

Por lo tanto, recomiendoen contra utilizandobyte[] Como claves en un HashMap.

Supongo que mi redacción estaba un poco fuera de lugar. Tenía en cuenta la situación en la que se utilizaba LA MISMA matriz de bytes para la inserción en el mapa hash Y para la recuperación del mapa hash. En ese caso, las matrices de bytes "ambas" son idénticas Y comparten el mismo código hash. Adam Paynter
s / no necesariamente / no / Michael Borgwardt
72

u clave: los arreglos no implementan la "igualdad de valores" de la forma que probablemente desearía. Por ejemplo:

byte[] array1 = new byte[1];
byte[] array2 = new byte[1];

System.out.println(array1.equals(array2));
System.out.println(array1.hashCode());
System.out.println(array2.hashCode());

imprime algo como

false
1671711
11394033

(Los números reales son irrelevantes; el hecho de que sean diferentes es importante).

Asumiendo queactualmente quiero igualdad, te sugiero que crees tu propia envoltura que contenga unbyte[] e implementa apropiadamente la igualdad y la generación de código hash:

public final class ByteArrayWrapper
{
    private final byte[] data;

    public ByteArrayWrapper(byte[] data)
    {
        if (data == null)
        {
            throw new NullPointerException();
        }
        this.data = data;
    }

    @Override
    public boolean equals(Object other)
    {
        if (!(other instanceof ByteArrayWrapper))
        {
            return false;
        }
        return Arrays.equals(data, ((ByteArrayWrapper)other).data);
    }

    @Override
    public int hashCode()
    {
        return Arrays.hashCode(data);
    }
}

Tenga en cuenta que si cambia los valores dentro de la matriz de bytes después de usar elByteArrayWrapper, como clave en unHashMap (etc) tendrá problemas para volver a buscar la clave ... podría tomar una copia de los datos en elByteArrayWrapper constructor si lo desea, pero obviamente eso será un desperdicio de rendimiento si lo conoceno lo hará Estará cambiando los contenidos de la matriz de bytes.

EDITAR: Como se menciona en los comentarios, también podría utilizarByteBuffer para esto (en particular, suByteBuffer#wrap(byte[]) método). No sé si realmente es lo correcto, dadas todas las habilidades adicionales queByteBufferTengo lo que no necesitas, pero es una opción.

Solo quería señalar que la clase java.nio.ByteBuffer hace esencialmente todo lo que hace su envoltura, aunque con la misma advertencia de que solo debería usarla si el contenido de la matriz de bytes no va a cambiar. Es posible que desee modificar su respuesta para hacer mención de ella. Ed Anuff
@dfa: La prueba "instanceof" maneja el caso nulo. Jon Skeet
Algunas otras cosas que podría agregar a la implementación de la envoltura: 1. Tome una copia del byte [] en la construcción, garantizando que el objeto sea inmutable, lo que significa que no hay peligro de que el código hash de su clave cambie con el tiempo. 2. Haga un cálculo previo y almacene el código hash una vez (suponiendo que la velocidad es más importante que la sobrecarga de almacenamiento). Adamski
@Adamski: menciono la posibilidad de copiar al final de la respuesta. En algunos casos es lo correcto, pero no en otros. Probablemente me gustaría convertirlo en una opción (posiblemente métodos estáticos en lugar de constructores - copyOf y wrapperAround). Tenga en cuenta quesin Al copiar, puede cambiar la matriz subyacente hasta que primero tome el hash y compruebe la igualdad, lo que podría ser útil en algunas situaciones. Jon Skeet
Vaya, lo siento Jon; Me perdí esa parte de tu respuesta. Adamski
0

segura' usando Base32 o Base64, por ejemplo:

byte[] keyValue = new byte[] {…};
String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue);

Por supuesto, hay muchas variantes de lo anterior, como:

String key = org.apache.commons.codec.binary.Base64.encodeBase64(keyValue);
1

y métodos iguales, recuerda el contrato entre ellos.

Esto le dará una mayor flexibilidad, ya que puede omitir 0 entradas que se agregan al final de la matriz de bytes, especialmente si copia solo una parte del búfer de bytes.

De esta manera usted decidirá cómo ambos objetos DEBEN ser iguales.

12

java.math.BigInteger. Tiene unBigInteger(byte[] val) constructor. Es un tipo de referencia, por lo que podría utilizarse como una clave para la tabla hash. Y.equals() y.hashCode() se definen como para los números enteros respectivos, lo que significa que BigInteger tiene una semántica igual a la de un byte [].

La respuesta de @vinchan sería más apropiada, ya que no habría ningún problema de bytes iniciales. Artem Oboturov
gracias, solución impresionante! Vsevolod Dyomkin
Suena atractivo, pero es incorrecto, ya que dos matrices que se diferencian solo en los elementos cero iniciales (por ejemplo,{0,100} y{100}) dará el mismo BigInteger leonbloy
Buen punto @leonbloy. Podría haber una solución alternativa: al agregarle una constante fija de byte principal no nula, pero será necesario escribir una envoltura alrededor del constructor BigInteger y nos devolverá a la respuesta de Jon. Artem Oboturov
56

El problema es esebyte[] usa la identidad del objeto paraequals yhashCode, así que eso

byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}

no coincidirá en unHashMap. Veo tres opciones:

Envolver en unString, pero luego debe tener cuidado con los problemas de codificación (debe asegurarse de que el byte -> Cadena -> byte le dé los mismos bytes).UtilizarList<Byte> (Puede ser caro en la memoria).Haz tu propia clase de envoltura, escribiendohashCode yequals para utilizar los contenidos de la matriz de bytes.
Resolví el problema de ajuste de cadena mediante el uso de codificación hexadecimal. Alternativamente, podría utilizar la codificación base64. metadaddy
La opción de clase de envoltura / manejo es sencilla y debe ser muy legible. ZX9
3

más simple.

Sí, no es posible utilizar un HashMap, pero nadie le impide utilizar un SortedMap como alternativa. Lo único es escribir un comparador que necesite comparar las matrices. No es tan eficaz como un HashMap, pero si desea una alternativa simple, aquí tiene (puede reemplazar SortedMap con Map si desea ocultar la implementación):

 private SortedMap<int[], String>  testMap = new TreeMap<>(new ArrayComparator());

 private class ArrayComparator implements Comparator<int[]> {
    @Override
    public int compare(int[] o1, int[] o2) {
      int result = 0;
      int maxLength = Math.max(o1.length, o2.length);
      for (int index = 0; index < maxLength; index++) {
        int o1Value = index < o1.length ? o1[index] : 0;
        int o2Value = index < o2.length ? o2[index] : 0;
        int cmp     = Integer.compare(o1Value, o2Value);
        if (cmp != 0) {
          result = cmp;
          break;
        }
      }
      return result;
    }
  }

Esta implementación se puede ajustar para otras matrices, lo único que debe tener en cuenta es que las matrices iguales (= igual longitud con miembros iguales) deben devolver 0 y que tiene un orden determinístico

Buena solución con el enorme beneficio de no crear objetos adicionales. Error muy pequeño si las matrices no tienen la misma longitud pero la más larga solo tiene 0 después de una longitud más corta. Además, administrar la orden probablemente ayuda a acelerar el recorrido del árbol. +1! jmspaggi
0

la interfaz del comparador y el método java.util.Arrays.equals (byte [], byte []);

NOTA: El orden en el mapa no es relevante con este método

SortedMap<byte[], String> testMap = new TreeMap<>(new ArrayComparator());

static class ArrayComparator implements Comparator<byte[]> {
    @Override
    public int compare(byte[] byteArray1, byte[] byteArray2) {

        int result = 0;

        boolean areEquals = Arrays.equals(byteArray1, byteArray2);

        if (!areEquals) {
            result = -1;
        }

        return result;
    }
}
0

en lugar de implementaciones de arrays por defecto

¿Y cómo harías que HashMap usara eso? Michael Borgwardt
ver la respuesta de Jon Skeet (un contenedor de matriz de bytes) dfa

Preguntas relacionadas