Haben Sie Probleme mit der Verwendung eines Byte-Arrays als Map-Schlüssel? Ich könnte auch new String(byte[])
und hash von String
machen, aber die Verwendung von byte[]
ist einfacher.
Das Problem ist, dass byte[]
Objektidentität für equals
und hashCode
verwendet, so dass
byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}
wird in einer HashMap
nicht übereinstimmen. Ich sehe drei Möglichkeiten:
String
, aber dann müssen Sie bei Codierungsproblemen vorsichtig sein (Sie müssen sicherstellen, dass Byte -> String -> Byte dieselben Bytes enthält).List<Byte>
(kann im Speicher teuer sein).hashCode
und equals
, um den Inhalt des Bytearrays zu verwenden.Dies ist in Ordnung, solange Sie nur Referenzgleichheit für Ihren Schlüssel wünschen - Arrays implementieren "Wertgleichheit" nicht so, wie Sie es wahrscheinlich möchten. Zum Beispiel:
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());
druckt etwas wie:
false
1671711
11394033
(Die tatsächlichen Zahlen sind irrelevant; die Tatsache, dass sie sich unterscheiden, ist wichtig.)
Wenn Sie davon ausgehen, dass Sie eigentlich Gleichheit wünschen, sollten Sie Ihren eigenen Wrapper erstellen, der einen byte[]
enthält und die Gleichheits- und Hashcode-Generierung entsprechend implementiert:
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);
}
}
Wenn Sie die Werte innerhalb des Bytearrays nach der Verwendung der ByteArrayWrapper
ändern, haben Sie als Schlüssel in einer HashMap
(etc) Probleme, den Schlüssel erneut zu suchen ... Sie könnten eine Kopie der Daten im ByteArrayWrapper
-Konstruktor erstellen Wenn Sie möchten, ist dies natürlich eine Verschwendung von Leistung, wenn Sie wissen, dass Sie nicht den Inhalt des Byte-Arrays ändern.
BEARBEITEN: Wie in den Kommentaren erwähnt, können Sie auch ByteBuffer
verwenden (insbesondere seine ByteBuffer#wrap(byte[])
-Methode). Ich weiß nicht, ob es wirklich das Richtige ist, angesichts der zusätzlichen Fähigkeiten, die ByteBuffer
s haben, die Sie nicht benötigen, aber es ist eine Option.
Wir können dazu ByteBuffer verwenden (dies ist im Grunde der Byte-Wrapper [] mit einem Komparator).
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)));
wird drucken
true
Sie könnten Java.math.BigInteger
verwenden. Es hat einen BigInteger(byte[] val)
-Konstruktor. Dies ist ein Referenztyp, der als Schlüssel für die Hashtabelle verwendet werden kann. Und .equals()
und .hashCode()
sind wie für die jeweiligen Integer-Zahlen definiert, was bedeutet, dass BigInteger als Byte [] -Array konsistente Gleichheits-Semantiken hat.
Ich bin sehr überrascht, dass die Antworten nicht die einfachste Alternative darstellen.
Ja, es ist nicht möglich, eine HashMap zu verwenden, aber niemand hindert Sie daran, eine SortedMap als Alternative zu verwenden. Die einzige Sache ist, einen Comparator zu schreiben, der die Arrays vergleichen muss. Es ist nicht so performant wie eine HashMap, aber wenn Sie eine einfache Alternative wünschen, können Sie hier gehen (Sie können SortedMap durch Map ersetzen, wenn Sie die Implementierung ausblenden möchten):
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;
}
}
Diese Implementierung kann für andere Arrays angepasst werden. Das einzige, was Sie beachten müssen, ist, dass gleiche Arrays (= gleiche Länge mit gleichen Mitgliedern) 0 zurückgeben müssen und dass Sie eine bestimmte Reihenfolge haben
Ich glaube, dass Arrays in Java die Methoden hashCode()
und equals(Object)
nicht unbedingt intuitiv implementieren. Das heißt, dass zwei identische Byte-Arrays nicht notwendigerweise denselben Hash-Code verwenden und nicht unbedingt behaupten müssen, gleich zu sein. Ohne diese beiden Eigenschaften verhält sich Ihre HashMap unerwartet.
Daher empfehle ich against , dass byte[]
als Schlüssel in einer HashMap verwendet wird.
Sie sollten eine Klasse wie ByteArrKey erstellen und Hashcode für Überladung und gleichwertige Methoden verwenden. Den Vertrag zwischen ihnen berücksichtigen.
Dies gibt Ihnen mehr Flexibilität, da Sie 0 Einträge überspringen können, die am Ende des Byte-Arrays angehängt werden, insbesondere wenn Sie nur einen Teil des anderen Byte-Puffers kopieren.
Auf diese Weise entscheiden Sie, wie beide Objekte gleich sein sollten.
Sie können das Byte [] auch mithilfe von Base32 oder Base64 in eine "sichere" Zeichenfolge konvertieren. Beispiel:
byte[] keyValue = new byte[] {…};
String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue);
natürlich gibt es viele Varianten der oben genannten, wie:
String key = org.Apache.commons.codec.binary.Base64.encodeBase64(keyValue);
Hier ist eine Lösung, die TreeMap, Comparator-Schnittstelle und Java-Methode verwendet. Java.util.Arrays.equals (Byte [], Byte []);
HINWEIS: Die Reihenfolge in der Karte ist bei dieser Methode nicht relevant
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;
}
}
Ich sehe Probleme, da Sie Arrays.equals und Array.hashCode anstelle von Standard-Array-Implementierungen verwenden sollten
Arrays.toString (Bytes)
Wir können auch eine eigene ByteHashMap wie diese erstellen.
ByteHashMap byteMap = new ByteHashMap();
byteMap.put(keybyteArray,valueByteArray);
Hier ist die vollständige Implementierung
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;
}
}
}