web-dev-qa-db-fra.com

Utiliser un tableau d'octets comme clé de carte

Voyez-vous un problème avec l'utilisation d'un tableau d'octets en tant que clé de la carte? Je pourrais aussi faire new String(byte[]) et hachage de String mais il est plus simple d’utiliser byte[].

69
shikhar

Le problème est que byte[] utilise l'identité de l'objet pour equals et hashCode, de sorte que 

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

ne correspondra pas à une HashMap. Je vois trois options:

  1. Envelopper dans une String, mais ensuite vous devez faire attention aux problèmes d'encodage (vous devez vous assurer que l'octet -> String -> octet vous donne les mêmes octets).
  2. Utilisez List<Byte> (peut être coûteux en mémoire).
  3. Créez votre propre classe de wrapping en écrivant hashCode et equals pour utiliser le contenu du tableau d'octets.
56
Kathy Van Stone

Ce n'est pas grave tant que vous ne voulez que l'égalité de référence pour votre clé - les tableaux n'implémentent pas "l'égalité de valeur" comme vous le souhaiteriez probablement. Par exemple:

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 quelque chose comme:

false
1671711
11394033

(Les chiffres réels ne sont pas pertinents; le fait qu'ils soient différents est important.)

En supposant que vous désirez réellement , je vous suggère de créer votre propre wrapper contenant un byte[] et de mettre en œuvre de manière appropriée la génération de code de hachage et d'égalité:

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

Notez que si vous modifiez les valeurs dans le tableau d'octets après avoir utilisé ByteArrayWrapper, en tant que clé d'un HashMap (etc.), vous aurez des problèmes pour rechercher la clé à nouveau ... copie des données dans le constructeur ByteArrayWrapper si vous voulez, mais évidemment ce sera un gaspillage de performance si vous vous connaissez ne le serez pas être en train de changer le contenu du tableau d'octets.

EDIT: Comme mentionné dans les commentaires, vous pouvez également utiliser ByteBuffer pour cela (en particulier, sa méthode ByteBuffer#wrap(byte[]) ). Je ne sais pas si c'est vraiment la bonne chose, compte tenu de toutes les capacités supplémentaires dont dispose ByteBuffer dont vous n'avez pas besoin, mais c'est une option.

72
Jon Skeet

Nous pouvons utiliser ByteBuffer pour cela (Il s’agit essentiellement du wrapper byte [] avec un comparateur)

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

imprimera 

true
39
byte_array

Vous pouvez utiliser Java.math.BigInteger. Il a un constructeur BigInteger(byte[] val). C'est un type de référence, donc pourrait être utilisé comme clé pour hashtable. Et .equals() et .hashCode() sont définis comme pour les nombres entiers respectifs, ce qui signifie que BigInteger a une sémantique égale à égale sous forme de tableau byte [].

12
Artem Oboturov

Je suis très surpris que les réponses n'indiquent pas l'alternative la plus simple.

Oui, il n'est pas possible d'utiliser un HashMap, mais personne ne vous empêche d'utiliser un SortedMap comme alternative. La seule chose à faire est d’écrire un comparateur qui doit comparer les tableaux. Ce n'est pas aussi performant qu'un HashMap, mais si vous voulez une alternative simple, le tour est joué (vous pouvez remplacer SortedMap par Map si vous voulez masquer l'implémentation):

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

Cette implémentation peut être ajustée pour les autres tableaux, la seule chose dont vous devez être conscient est que les tableaux égaux (= longueur égale avec membres égaux) doivent retourner 0 et que vous avez un ordre déterministe 

3
Thorsten S.

Je pense que les tableaux Java n'implémentent pas nécessairement les méthodes hashCode() et equals(Object) de manière intuitive. En d'autres termes, deux tableaux d'octets identiques ne partageront pas nécessairement le même code de hachage et ne prétendront pas nécessairement être égaux. Sans ces deux traits, votre HashMap se comportera de manière inattendue.

Par conséquent, je recommande contre en utilisant byte[] comme clés dans un HashMap.

1
Adam Paynter

Vous devez utiliser create a somthing class, comme ByteArrKey, et surcharger le hashcode et les méthodes équivalentes, rappelez-vous le contrat qui les lie.

Cela vous donnera une plus grande flexibilité car vous pouvez ignorer les 0 entrées qui sont ajoutées à la fin du tableau d'octets, en particulier si vous ne copiez qu'une partie de l'autre tampon d'octets.

De cette façon, vous déciderez comment les deux objets DEVRAIENT être égaux.

1
Milind Patil

Vous pouvez également convertir l'octet [] en une chaîne 'safe' utilisant Base32 ou Base64, par exemple:

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

bien sûr, il existe de nombreuses variantes de ce qui précède, comme: 

String key = org.Apache.commons.codec.binary.Base64.encodeBase64(keyValue);
0
Christof R

Voici une solution utilisant TreeMap, l'interface Comparator et la méthode Java Java.util.Arrays.equals (byte [], byte []);

REMARQUE: l'ordre dans la carte n'est pas pertinent avec cette méthode.

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
matdev

Je vois des problèmes puisque vous devriez utiliser Arrays.equals et Array.hashCode, à la place des implémentations de tableaux par défaut

0
dfa

Arrays.toString (octets)

0
df.

De plus, nous pouvons créer notre propre ByteHashMap personnalisé comme celui-ci,

ByteHashMap byteMap = new ByteHashMap();
byteMap.put(keybyteArray,valueByteArray);

Voici l'implémentation complète

public class ByteHashMap implements Map<byte[], byte[]>, Cloneable,
        Serializable {

    private Map<ByteArrayWrapper, byte[]> internalMap = new HashMap<ByteArrayWrapper, byte[]>();

    public void clear() {
        internalMap.clear();
    }

    public boolean containsKey(Object key) {
        if (key instanceof byte[])
            return internalMap.containsKey(new ByteArrayWrapper((byte[]) key));
        return internalMap.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return internalMap.containsValue(value);
    }

    public Set<Java.util.Map.Entry<byte[], byte[]>> entrySet() {
        Iterator<Java.util.Map.Entry<ByteArrayWrapper, byte[]>> iterator = internalMap
                .entrySet().iterator();
        HashSet<Entry<byte[], byte[]>> hashSet = new HashSet<Java.util.Map.Entry<byte[], byte[]>>();
        while (iterator.hasNext()) {
            Entry<ByteArrayWrapper, byte[]> entry = iterator.next();
            hashSet.add(new ByteEntry(entry.getKey().data, entry
                    .getValue()));
        }
        return hashSet;
    }

    public byte[] get(Object key) {
        if (key instanceof byte[])
            return internalMap.get(new ByteArrayWrapper((byte[]) key));
        return internalMap.get(key);
    }

    public boolean isEmpty() {
        return internalMap.isEmpty();
    }

    public Set<byte[]> keySet() {
        Set<byte[]> keySet = new HashSet<byte[]>();
        Iterator<ByteArrayWrapper> iterator = internalMap.keySet().iterator();
        while (iterator.hasNext()) {
            keySet.add(iterator.next().data);
        }
        return keySet;
    }

    public byte[] put(byte[] key, byte[] value) {
        return internalMap.put(new ByteArrayWrapper(key), value);
    }

    @SuppressWarnings("unchecked")
    public void putAll(Map<? extends byte[], ? extends byte[]> m) {
        Iterator<?> iterator = m.entrySet().iterator();
        while (iterator.hasNext()) {
            Entry<? extends byte[], ? extends byte[]> next = (Entry<? extends byte[], ? extends byte[]>) iterator
                    .next();
            internalMap.put(new ByteArrayWrapper(next.getKey()), next
                    .getValue());
        }
    }

    public byte[] remove(Object key) {
        if (key instanceof byte[])
            return internalMap.remove(new ByteArrayWrapper((byte[]) key));
        return internalMap.remove(key);
    }

    public int size() {
        return internalMap.size();
    }

    public Collection<byte[]> values() {
        return internalMap.values();
    }

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

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

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

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

    private final class ByteEntry implements Entry<byte[], byte[]> {
        private byte[] value;
        private byte[] key;

        public ByteEntry(byte[] key, byte[] value) {
            this.key = key;
            this.value = value;
        }

        public byte[] getKey() {
            return this.key;
        }

        public byte[] getValue() {
            return this.value;
        }

        public byte[] setValue(byte[] value) {
            this.value = value;
            return value;
        }

    }
}
0
Rakesh Chaudhari