Использование массива байтов в качестве ключа карты



вы видите какие-либо проблемы с использованием байтовый массив в качестве ключа карты? Я тоже мог бы сделать new String(byte[]) и гашиш путем String но это более простой в использовании byte[].

764   11  

11 ответов:

проблема в том, что byte[] использует идентификатор объекта для equals и hashCode, Так что

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

не будет соответствовать в HashMap. Я вижу три варианта:

  1. оборачивать в тег String, но тогда вы должны быть осторожны с проблемами кодирования (вам нужно убедиться, что byte -> String -> byte дает вам те же байты).
  2. использовать List<Byte> (может быть дорого в памяти).
  3. сделайте свой собственный класс упаковки, написание 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 записей, которые добавляются в конец массива байтов, особенно если вы копируете только часть в буфер байта.

таким образом, вы будете решать, как оба объекта должны быть равны.

Я вижу проблемы, так как вы должны использовать массивы.равно и массив.хэш-код, вместо реализации массива по умолчанию

массивы.метод toString(байт)

вы также можете преобразовать байт[] в "безопасную" строку, используя 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

    Ничего не найдено.