[JAVA] Comment utiliser un tableau pour les clés HashMap

En regardant Les clés de la carte ne sont pas bonnes si elles sont arrangées, il existe différentes manières de le faire fonctionner en pensant que cela ne fonctionnera pas. Notes sur l'utilisation d'un tableau d'octets comme clé dans Java Map est lié, et [Utilisation d'un tableau d'octets comme clé de mappage](http: / Il y a un lien sur /stackoverflow.com/questions/1058149/using-a-byte-array-as-map-key), et il y a diverses choses écrites dans le dernier Stack Overflow.

Hypothèses clés HashMap

Ici, c'est HashMap. Cela devrait changer avec TreeMap. → Comment utiliser un tableau pour la clé TreeMap L'hypothèse clé est

Le premier et le second s'utilisent parfaitement avec containsKey, get, put de HashMap. Le troisième n'est pas imuutable et peut être modifié ultérieurement, mais si vous modifiez la valeur de clé après l'avoir placée, il se peut qu'il ne soit pas trouvé. Si vous changez cela en DEF après l'avoir mis dans ABC, il est naturel que vous ne puissiez pas le trouver dans ABC, mais il est possible qu'il ne soit pas trouvé dans DEF. La raison en est que le hashCode change, mais ce qui se trouve à l'origine à l'endroit qui correspond au hashCode de DEF est entré à l'endroit qui correspond au hashCode de ABC.

Résultats de array equals et hashCode

Equals et hashCode sont incorrects pour les tableaux.

Main.java


    public static void main(String[] args) {
        byte[] b01 = { 1, 2, 3 };
        byte[] b02 = { 1, 2, 3 };
        int h01 = b01.hashCode();
        int h02 = b02.hashCode();
        System.out.println(b01 == b02);
        System.out.println(b01.equals(b02));
        System.out.println(h01 == h02);
        int h11 = Arrays.hashCode(b01);
        int h12 = Arrays.hashCode(b02);
        System.out.println(Arrays.equals(b01, b02));
        System.out.println(h11 == h12);
    }

Le résultat est ···

false
false
false
true
true

La raison pour laquelle Arrays.equals et Arrays.hashCode sont dérangés est que les equals et hashCode du tableau sont incorrects.

Partie 1: Créer un ByteArrayWrapper

Comment implémenter equals et hashCode en enveloppant l'octet [].

ByteArrayWrapper.java


import java.util.Arrays;

public class ByteArrayWrapper {
    private byte[] data;

    public ByteArrayWrapper(byte[] data) {
        this.data = data;
    }

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

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

Testons-le.

Main.java


    public static void main(String[] args) {
        byte[] b01 = { 1, 2, 3 };
        byte[] b02 = { 1, 2, 3 };
        ByteArrayWrapper w01 = new ByteArrayWrapper(b01);
        ByteArrayWrapper w02 = new ByteArrayWrapper(b02);
        Map<ByteArrayWrapper, String> map = new HashMap<>();
        map.put(w01, "OK");
        int h01 = w01.hashCode();
        int h02 = w02.hashCode();
        System.out.println(w01 == w02);
        System.out.println(w01.equals(w02));
        System.out.println(h01 == h02);
        System.out.println(map.get(w01));
        System.out.println(map.get(w02));
    }

Le résultat est ···

false
true
true
OK
OK

C'est naturel car nous appelons Arrays.equals et Arrays.hashCode.

修正版ByteArrayWrapper.java

(1/17 postscript) Si vous l'apportez de Stack Overflow, c'est inutile. Puisqu'il ne contient que le tableau tel quel, il ne fonctionnera pas correctement si le tableau passé au constructeur est modifié. C'est un gros problème, alors essayons-le.

Main.java


    public static void main(String[] args) {
        byte[] b01 = { 1, 2, 3 };
        byte[] b02 = { 1, 2, 3 };
        byte[] b03 = { 9, 2, 3 };
        ByteArrayWrapper w01 = new ByteArrayWrapper(b01);
        ByteArrayWrapper w02 = new ByteArrayWrapper(b02);
        ByteArrayWrapper w03 = new ByteArrayWrapper(b03);
        Map<ByteArrayWrapper, String> map = new HashMap<>();
        map.put(w01, "OK");
        System.out.println(map.get(w01));
        b01[0] = 9;
        System.out.println(map.get(w01));
        System.out.println(map.get(w02));
        System.out.println(map.get(w03));
        System.out.println(map);
    }

Quand tu cours ...

OK
null
null
null
{ByteArrayWrapper@9669=OK}

Les données sont dans la carte, mais je ne peux pas les obtenir avec la clé. Pour éviter cela, faites une copie du tableau dans le constructeur.

ByteArrayWrapper.java


import java.util.Arrays;

public class ByteArrayWrapper {
    private byte[] data;

    public ByteArrayWrapper(byte[] data) {
        this.data = data.clone();
    }

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

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

Si vous exécutez le même programme de test ...

OK
OK
OK
null
{ByteArrayWrapper@7861=OK}

La disposition à l'intérieur est protégée.

Partie 2: Utilisez ByteBuffer

Il y a un commentaire que vous pouvez utiliser avec java.nio.ByteBuffer sans créer de classe wrapper. Testons-le.

Main.java


    public static void main(String[] args) {
        byte[] b01 = { 1, 2, 3 };
        byte[] b02 = { 1, 2, 3 };
        ByteBuffer w01 = ByteBuffer.wrap(b01);
        ByteBuffer w02 = ByteBuffer.wrap(b02);
        Map<ByteBuffer, String> map = new HashMap<>();
        map.put(w01, "OK");
        int h01 = w01.hashCode();
        int h02 = w02.hashCode();
        System.out.println(w01 == w02);
        System.out.println(w01.equals(w02));
        System.out.println(h01 == h02);
        System.out.println(map.get(w01));
        System.out.println(map.get(w02));
    }

Le résultat est ···

false
true
true
OK
OK

OK du tout. C'est la meilleure sensation. J'aime la bibliothèque standard ajoutée à partir de JDK 1.4.

Partie 3: Utilisez BigInteger → Non

Il y a un commentaire que java.math.BigInteger ne peut pas aller car le constructeur reçoit l'octet []. Cependant, quand ce sera {0, 100}, ce sera avec {100}, non? Il y a un retour de commentaire. Testons-le.

Main.java


    public static void main(String[] args) {
        byte[] b01 = { 0, 100 };
        byte[] b02 = { 0, 100 };
        byte[] b03 = { 100 };
        BigInteger w01 = new BigInteger(b01);
        BigInteger w02 = new BigInteger(b02);
        BigInteger w03 = new BigInteger(b03);
        Map<BigInteger, String> map = new HashMap<>();
        map.put(w01, "OK");
        int h01 = w01.hashCode();
        int h02 = w02.hashCode();
        int h03 = w03.hashCode();
        System.out.println(w01 == w02);
        System.out.println(w01 == w03);
        System.out.println(w01.equals(w02));
        System.out.println(w01.equals(w03));
        System.out.println(h01 == h02);
        System.out.println(h01 == h03);
        System.out.println(map.get(w01));
        System.out.println(map.get(w02));
        System.out.println(map.get(w03));
    }

Le résultat est ···

false
false
true
true
true
true
OK
OK
OK

C'est dommage. {0, 100} et {100} sont ensemble. C'est dangereux si vous l'utilisez sans le savoir.

Partie 4: Conversion en chaîne de caractères → Pas efficace

String s01 = new String (b01, "UTF-8"); Il semble que les caractères soient déformés. String s02 = Arrays.toString (b01); serait légitime, mais la chaîne de sortie serait "[1, 2, 3]". Je n'ai pas besoin de parenthèses ou de blancs. Par conséquent, créez une méthode utilitaire qui génère des valeurs numériques, des valeurs numériques, ... Convertissez le nombre en hexadécimal. Convertissez le nombre en 36 base. (Caractère.MAX_RADIX == 36) encodage base64.

Cependant, il est inefficace car la taille est nettement plus grande que l'octet [].

finalement

Je pense que ByteBuffer est bien, mais si vous voulez utiliser int [] au lieu de byte [], il y a IntBuffer. Ou plutôt, tous les types primitifs sont disponibles. Divers en quelque sorte Buffer. Je ne savais pas ça.

Strictement pas imuutable, mais vous ne l'ajouteriez pas avec put après l'avoir spécifié comme clé. (1/17 postscript) En regardant le javadoc et la source de ByteBuffer, get () est également inutile. Cela est dû au fait que la position actuelle se déplace et est égale et que hashCode cible la position actuelle et au-delà. Equals et hashCode sont accessibles par get (int). Les notes suivantes sont écrites en ByteBuffer / hashCode.

Le code de hachage du tampon dépend du contenu. Évitez d'utiliser le tampon comme clé pour les cartes de hachage ou d'autres structures de données à moins qu'il ne soit clair que le contenu du tampon ne changera pas à l'avenir.

Mais si vous ne savez pas comment l'utiliser à l'extérieur, vous pouvez être assuré que vous ne pouvez pas le changer en créant ByteArrayWrapper de la manière habituelle. (1/17 postscript) Je suis désolé, le premier ByteArray Wrapper que j'ai sorti n'était pas imuutable. Fixé.

ByteArrayWrapper qui peut être utilisé avec HashMap et TreeMap

(1/18 postscript) Je l'ai mis à la fin de TreeMap, mais c'est un wrapper qui peut être utilisé pour les deux. Modifié à partir de la source dans HashMap: Modification de l'argument du constructeur d'octet [] en octet ..., ajout d'égaux (octet [] autre), ajout de compareTo ()

ByteArrayWrapper.java


import java.util.Arrays;

public class ByteArrayWrapper implements Comparable<ByteArrayWrapper> {
    private byte[] data;

    public ByteArrayWrapper(byte... data) {
        this.data = data.clone();
    }

    public boolean equals(Object other) {
        if (other instanceof ByteArrayWrapper) {
            return equals(((ByteArrayWrapper) other).data);
        }
        return false;
    }
    
    public boolean equals(byte[] other) {
        return Arrays.equals(data, other);
    }
    
    public int hashCode() {
        return Arrays.hashCode(data);
    }

    public int compareTo(ByteArrayWrapper that) {
        int n = Math.min(this.data.length, that.data.length);
        for (int i = 0; i < n; i++) {
            int cmp = Byte.compare(this.data[i], that.data[i]);
            if (cmp != 0)
                return cmp;
        }
        return this.data.length - that.data.length;
    }
}

Recommended Posts

Comment utiliser un tableau pour les clés HashMap
Comment utiliser un tableau pour la clé TreeMap
Comment utiliser binding.pry pour afficher le fichier
[Ruby] Comment utiliser slice pour les débutants
Comment utiliser Map
Comment utiliser rbenv
Comment utiliser with_option
Comment utiliser fields_for
Comment utiliser la carte
[Java] Comment transformer un tableau à deux dimensions avec une instruction for étendue
Comment utiliser collection_select
Comment créer des pages pour le tableau "kaminari"
Comment utiliser Twitter4J
Comment utiliser active_hash! !!
Comment utiliser MapStruct
Comment utiliser TreeSet
[Comment utiliser l'étiquette]
Comment utiliser l'identité
Comment utiliser le hachage
[Pour les débutants Rails] Résumé de l'utilisation de RSpec (obtenir un aperçu)
Comment utiliser Dozer.mapper
Comment utiliser Gradle
[Pour les super débutants] Comment utiliser l'autofocus: vrai
Comment utiliser org.immutables
Comment utiliser java.util.stream.Collector
Comment utiliser VisualVM
Comment utiliser Map
Comment faire une méthode de jugement pour rechercher n'importe quel caractère dans le tableau
Comment écrire pour appliquer Gem Pagy (pagination) à un tableau
Comment sortir le standard d'un tableau avec for Each
Comment utiliser Truth (bibliothèque d'assertions pour Java / Android)
[Pour ceux qui créent des portfolios] Comment utiliser font-awesome-rails
Comment utiliser GitHub pour les super débutants (développement d'équipe)
Comment utiliser l'API Chain
[Java] Comment utiliser Map
Comment utiliser Queue avec priorité
[Rails] Comment utiliser enum
Comment utiliser java Facultatif
Comment utiliser JUnit (débutant)
[Rails] Comment utiliser enum
Comment utiliser @Builder (Lombok)
Comment utiliser la classe Java
Comment utiliser Big Decimal
[Java] Comment utiliser removeAll ()
Comment utiliser String [] args
Comment utiliser la jonction de rails
Comment utiliser Java Map
Ruby: Comment utiliser les cookies
Comment utiliser Dependant :: Destroy