[JAVA] So verwenden Sie ein Array für HashMap-Schlüssel

Wenn Sie sich Kartenschlüssel sind nicht gut, wenn sie angeordnet sind ansehen, gibt es verschiedene Möglichkeiten, damit es funktioniert, während Sie denken, dass es nicht funktioniert. Hinweise zur Verwendung des Byte-Arrays als Schlüssel in Java Map ist verknüpft und [Verwendung eines Byte-Arrays als Map-Schlüssel](http: / Es gibt einen Link unter /stackoverflow.com/questions/1058149/using-a-byte-array-as-map-key), und im letzten Stapelüberlauf sind verschiedene Dinge geschrieben.

HashMap-Schlüsselannahmen

Hier ist es HashMap. Dies sollte sich mit TreeMap ändern. → So verwenden Sie ein Array für den TreeMap-Schlüssel Die Hauptannahme ist --Implementieren gleich

Der erste und der zweite werden perfekt mit includesKey, get, put von HashMap verwendet. Der dritte ist nicht unveränderlich und kann später geändert werden. Wenn Sie jedoch den Schlüsselwert nach dem Einfügen ändern, wird er möglicherweise nicht gefunden. Wenn Sie dies nach dem Einfügen in ABC in DEF ändern, können Sie es natürlich nicht in ABC finden, aber es besteht die Möglichkeit, dass es in DEF nicht gefunden wird. Der Grund ist, dass sich der Hashcode ändert, aber was ursprünglich an die Stelle geht, die dem Hashcode von DEF entspricht, hat die Stelle eingegeben, die dem Hashcode von ABC entspricht.

Ergebnisse von Array gleich und HashCode

Equals und hashCode sind für Arrays falsch.

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

Ergebnis ist ···

false
false
false
true
true

Der Grund, warum Arrays.equals und Arrays.hashCode gestört werden, ist, dass Equals und HashCode des Arrays falsch sind.

Teil 1: Erstellen Sie einen ByteArrayWrapper

So implementieren Sie equals und hashCode durch Umbrechen von byte [].

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

Lass es uns testen.

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

Ergebnis ist ···

false
true
true
OK
OK

Es ist natürlich, weil wir Arrays.equals und Arrays.hashCode aufrufen.

修正版ByteArrayWrapper.java

(1/17 Nachschrift) Wenn Sie es von Stack Overflow bringen, ist es nutzlos. Da das Array nur so bleibt, wie es ist, tritt eine Fehlfunktion auf, wenn das an den Konstruktor übergebene Array geändert wird. Es ist eine große Sache, also lass es uns versuchen.

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

Wenn du rennst ...

OK
null
null
null
{ByteArrayWrapper@9669=OK}

Die Daten befinden sich in der Karte, aber ich kann sie nicht mit dem Schlüssel erhalten. Um dies zu verhindern, erstellen Sie eine Kopie des Arrays im Konstruktor.

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

Wenn Sie das gleiche Testprogramm ausführen ...

OK
OK
OK
null
{ByteArrayWrapper@7861=OK}

Die Anordnung im Inneren ist geschützt.

Teil 2: Verwenden Sie ByteBuffer

Es gibt einen Kommentar, den Sie mit java.nio.ByteBuffer verwenden können, ohne eine Wrapper-Klasse zu erstellen. Lass es uns testen.

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

Ergebnis ist ···

false
true
true
OK
OK

OK überhaupt. Das ist das beste Gefühl. Ich mag die Standardbibliothek, die aus JDK 1.4 hinzugefügt wurde.

Teil 3: Verwenden Sie BigInteger → Nr

Es gibt einen Kommentar, den java.math.BigInteger nicht ausführen kann, da der Konstruktor Byte [] empfängt. Wenn es jedoch {0, 100} ist, wird es mit {100} sein, richtig? Es gibt eine Kommentarrückgabe. Lass es uns testen.

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

Ergebnis ist ···

false
false
true
true
true
true
OK
OK
OK

Das ist sehr schade. {0, 100} und {100} sind zusammen. Es ist gefährlich, wenn Sie es verwenden, ohne es zu wissen.

Teil 4: In eine Zeichenfolge konvertieren → Nicht effizient

String s01 = neuer String (b01, "UTF-8"); Es scheint, dass die Zeichen verstümmelt sind. String s02 = Arrays.toString (b01); wäre legitim, aber der Ausgabe-String wäre "[1, 2, 3]". Ich brauche keine Klammern oder Leerzeichen. Erstellen Sie daher eine Dienstprogrammmethode, die numerische Werte, numerische Werte usw. ausgibt. Konvertieren Sie die Zahl in hexadezimal. Konvertieren Sie die Zahl in 36 Basis. (Character.MAX_RADIX == 36) base64 codieren.

Es ist jedoch ineffizient, da die Größe definitiv größer als Byte ist [].

Schließlich

Ich denke, ByteBuffer ist in Ordnung, aber wenn Sie int [] anstelle von Byte [] verwenden möchten, gibt es IntBuffer. Oder besser gesagt, alle primitiven Typen sind verfügbar. Verschiedene irgendwie Puffer. Das wusste ich nicht.

Genau genommen ist es nicht unveränderlich, aber Sie werden es nicht mit put hinzufügen, nachdem Sie es als Schlüssel angegeben haben. (1/17 postscript) Wenn man sich das Javadoc und die Quelle von ByteBuffer ansieht, ist get () ebenfalls nutzlos. Dies liegt daran, dass sich die aktuelle Position bewegt und gleich ist und hashCode auf die aktuelle Position und darüber hinaus abzielt. Auf Equals und HashCode wird mit get (int) zugegriffen. Die folgenden Hinweise sind in ByteBuffer / hashCode geschrieben.

Der Hash-Code des Puffers ist inhaltsabhängig. Vermeiden Sie es, den Puffer als Schlüssel für Hash-Maps oder andere Datenstrukturen zu verwenden, es sei denn, es ist klar, dass sich der Inhalt des Puffers in Zukunft nicht ändern wird.

Wenn Sie jedoch nicht wissen, wie Sie es im Freien verwenden sollen, können Sie sicher sein, dass Sie es nicht ändern können, indem Sie ByteArrayWrapper auf die übliche Weise erstellen. (1/17 Nachtrag) Es tut mir leid, der erste ByteArray Wrapper, den ich herausbrachte, war nicht unveränderlich. Fest.

ByteArrayWrapper, der sowohl mit HashMap als auch mit TreeMap verwendet werden kann

(1/18 Nachschrift) Ich habe es am Ende von TreeMap eingefügt, aber es ist ein Wrapper, der für beide verwendet werden kann. Geändert von der Quelle in HashMap: Das Konstruktorargument wurde von Byte [] in Byte ... geändert, gleich (Byte [] andere) hinzugefügt, compareTo () hinzugefügt

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

So verwenden Sie ein Array für HashMap-Schlüssel
So verwenden Sie ein Array für den TreeMap-Schlüssel
Verwendung von binding.pry für die Ansichtsdatei
[Ruby] Wie man Slice für Anfänger benutzt
Verwendung von Map
Wie benutzt man rbenv?
Verwendung mit_option
Verwendung von fields_for
Verwendung der Karte
[Java] So drehen Sie ein zweidimensionales Array mit einer erweiterten for-Anweisung
Verwendung von collection_select
So erstellen Sie Pagenationen für das "Kaminari" -Array
Wie benutzt man Twitter4J
Wie benutzt man active_hash! !!
Verwendung von MapStruct
Verwendung von TreeSet
[Verwendung des Etiketts]
Wie man Identität benutzt
Wie man Hash benutzt
[Für Rails-Anfänger] Zusammenfassung der Verwendung von RSpec (Überblick)
Verwendung von Dozer.mapper
Wie benutzt man Gradle?
[Für Super-Anfänger] Verwendung des Autofokus: true
Verwendung von org.immutables
Verwendung von java.util.stream.Collector
Verwendung von VisualVM
Verwendung von Map
So erstellen Sie eine Beurteilungsmethode, um nach einem beliebigen Zeichen im Array zu suchen
Wie schreibe ich, um Edelstein Pagy (Pagenation) auf ein Array anzuwenden
So geben Sie einen Standard aus einem Array mit for Each aus
Verwendung von Truth (Assertion Library für Java / Android)
[Für diejenigen, die Portfolios erstellen] Verwendung von Font-Awesome-Rails
Wie man GitHub für Super-Anfänger benutzt (Teamentwicklung)
Verwendung der Ketten-API
[Java] Verwendung von Map
Verwendung der Warteschlange mit Priorität
[Rails] Verwendung von Enum
Verwendung von Java Optional
Verwendung von JUnit (Anfänger)
[Rails] Verwendung von Enum
Verwendung von @Builder (Lombok)
Verwendung der Java-Klasse
Wie man Big Decimal benutzt
[Java] Verwendung von removeAll ()
Verwendung von String [] args
Verwendung von Rails Join
Verwendung von Java Map
Ruby: Wie man Cookies benutzt
Verwendung von abhängigen :: zerstören