11 ответов:
проблема в том, что
byte[]использует идентификатор объекта дляequalsиhashCode, Так чтоbyte[] b1 = {1, 2, 3} byte[] b2 = {1, 2, 3}не будет соответствовать в
HashMap. Я вижу три варианта:
- оборачивать в тег
String, но тогда вы должны быть осторожны с проблемами кодирования (вам нужно убедиться, что byte -> String -> byte дает вам те же байты).- использовать
List<Byte>(может быть дорого в памяти).- сделайте свой собственный класс упаковки, написание
hashCodeиequalsдля использования содержимого массива байтов.
это нормально, пока вы хотите только ссылочное равенство для своих ключевых массивов, не реализуйте "равенство значений" так, как вы, вероятно, захотите. Например:
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());выводит что-то вроде:
false 1671711 11394033(фактические цифры не имеют значения; важно то, что они разные.)
если вы на самом деле хотите равенства, я предлагаю вам создать свою собственную оболочку, которая содержит
byte[]и реализует равенство и генерация хэш-кода соответствующим образом: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); } }обратите внимание, что если вы измените значения в массив байтов после использования
ByteArrayWrapper, как ключ вHashMap(etc) у вас будут проблемы с поиском ключа снова... вы можете взять копию данных вByteArrayWrapperконструктор, если вы хотите, но очевидно, что это будет пустая трата производительности, если вы знаете, что вы не изменить содержимое массива байтов.EDIT: как уже упоминалось в комментариях, вы также можно использовать
ByteBufferдля этого (в частности, егоByteBuffer#wrap(byte[])метод). Я не знаю, действительно ли это правильно, учитывая все дополнительные способности, которыеByteBuffers есть, что вам не нужно, но это вариант.
мы можем использовать ByteBuffer для этого (это в основном байт [] обертка с компаратором)
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)));печати
true
вы могли бы использовать
java.math.BigInteger. Он имеетBigInteger(byte[] val)конструктор. Это ссылочный тип, поэтому может использоваться в качестве ключа для hashtable. И.equals()и.hashCode()определяются как для соответствующих целых чисел, что означает, что BigInteger имеет согласованную семантику равных значений в виде массива byte [].
Я очень удивлен, что ответы не указывают на самую простую альтернативу.
да, невозможно использовать HashMap, но никто не мешает вам использовать SortedMap в качестве альтернативы. Единственное, что нужно написать компаратор, который должен сравнивать массивы. Это не так эффективно, как HashMap, но если вы хотите простую альтернативу, здесь вы идете (вы можете заменить SortedMap с картой, если вы хотите скрыть реализацию):
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; } }этот реализация может быть скорректирована для других массивов, единственное, что вы должны знать, это то, что равные массивы (= равная длина с равными членами) должны возвращать 0 и что у вас есть детерминированный порядок
Я считаю, что массивы в Java не обязательно использовать тег
hashCode()иequals(Object)интуитивно методов. То есть два одинаковых байтовых массива не обязательно будут иметь один и тот же хэш-код, и они не обязательно будут претендовать на равенство. Без этих двух черт, ваш HashMap будет вести себя неожиданно.поэтому, я рекомендую против используя
byte[]как ключи в HashMap.
вы должны использовать создать класс что-то вроде ByteArrKey и перегрузить хэш-код и равные методы, помните контракт между ними.
Это даст вам большую гибкость, как вы можете пропустить 0 записей, которые добавляются в конец массива байтов, особенно если вы копируете только часть в буфер байта.
таким образом, вы будете решать, как оба объекта должны быть равны.
Я вижу проблемы, так как вы должны использовать массивы.равно и массив.хэш-код, вместо реализации массива по умолчанию
вы также можете преобразовать байт[] в "безопасную" строку, используя Base32 или Base64, например:
byte[] keyValue = new byte[] {…}; String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue);конечно, есть много вариантов выше, как:
String key = org.apache.commons.codec.binary.Base64.encodeBase64(keyValue);
вот решение с использованием TreeMap, интерфейс компаратора и Java метод java.утиль.Матрицы.равно (байт [], байт []);
Примечание: порядок на карте не имеет отношения к этому методу
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; } }
Comments