Используя массив байтов как ключ Карты

Префикс «#» при обновлении location.hash не требуется.

Измените document.location.hash ='#' + nextAnchorName; на просто document.location.hash = nextAnchorName;

См. Это jsfiddle .

72
задан Gray 23 March 2016 в 18:45
поделиться

6 ответов

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

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

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

  1. Перенос в строку , но тогда вы должны быть осторожны с проблемами кодирования (вам нужно убедиться, что байт -> String -> byte дает вам те же байты).
  2. Используйте List (может быть дорого в памяти).
  3. Создайте свой собственный класс-оболочку, записав hashCode и равный , чтобы использовать содержимое байтового массива.
60
ответ дан 24 November 2019 в 12:31
поделиться

Другие ответы не указали что не весь byte[] тайный в уникальный String. Я попал в это прерывание, делающее new String(byteArray) как ключи к карте только, чтобы найти, что много отрицательных байтов отображаются на той же строке. Вот тест, который демонстрирует что проблема:

    @Test
    public void testByteAsStringMap() throws Exception {
        HashMap<String, byte[]> kvs = new HashMap<>();
        IntStream.range(Byte.MIN_VALUE, Byte.MAX_VALUE).forEach(b->{
            byte[] key = {(byte)b};
            byte[] value = {(byte)b};
            kvs.put(new String(key), value);
        });
        Assert.assertEquals(255, kvs.size());
    }

Это бросит:

java.lang. AssertionError: Ожидаемый:255 Фактических:128

Это делает это, потому что String последовательность точек кода символа, и любое преобразование из byte[] основано на некотором кодировании байта. В вышеупомянутом случае кодировка по умолчанию платформы, оказывается, отображает много отрицательных байтов на тот же символ. Другой факт, который приблизительно String - то, что он всегда берет и дает копию своего внутреннего состояния. Если исходные байты прибыли из String, который был копией, то обертывание ее как String для использования ее в качестве ключа к карте делает вторую копию. Это может генерировать много мусора, который мог бы быть преодолимым.

существует хороший ответ здесь, который предлагает использовать java.nio.ByteBuffer с [1 113]. Проблема с этим состоит в том, что byte[] изменяемо, и это не делает копию, таким образом, необходимо стараться сделать защитную копию любого, которого массивы еще передали Вам с [1 115], ключи Вашей карты будут повреждены. При рассмотрении результата карты с [1 116] ключи в отладчике, Вы будете видеть, что буферы имеют много внутренних ссылок, разработанных для отслеживания чтения в и записи из каждого буфера. Таким образом, объекты являются намного большим количеством тяжеловеса, чем обертывание в простое String. Наконец, даже строка содержит больше состояния, чем необходимый. При рассмотрении его в моем отладчике он хранит символы как двухбайтовый массив UTF16 и также хранит четырехбайтовый хэш-код.

Мой предпочтительный подход должен иметь , Ломбок генерирует во время компиляции шаблон для создания легкой обертки массива байтов, которая не хранит дополнительное состояние:

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@ToString
@EqualsAndHashCode
@Data(staticConstructor="of")
class ByteSequence {
    final byte[] bytes;
}

Это затем проходит тест, который проверяет, что все возможные байты отображаются на уникальную строку:

    byte[] bytes(int b){
        return new byte[]{(byte)b};
    }

    @Test
    public void testByteSequenceAsMapKey() {
        HashMap<ByteSequence, byte[]> kvs = new HashMap<>();
        IntStream.range(Byte.MIN_VALUE, Byte.MAX_VALUE).forEach(b->{
            byte[] key = {(byte)b};
            byte[] value = {(byte)b};
            kvs.put(ByteSequence.of(key), value);
        });
        Assert.assertEquals(255, kvs.size());
        byte[] empty = {};
        kvs.put(ByteSequence.of(empty), bytes(1));
        Assert.assertArrayEquals(bytes(1), kvs.get(ByteSequence.of(empty)));
    }

Вы затем не должны волноваться о получении равняния и логики хэш-кода, корректной, поскольку это предоставляется Ломбоком, где это делает Arrays.deepEquals, который документируется в [1 122] https://Примечание projectlombok.org/features/EqualsAndHashCode, что Ломбок не является зависимостью во время выполнения только зависимость времени компиляции, и можно установить плагин с открытым исходным кодом на IDE так, чтобы IDE "видел" все сгенерированные шаблонные методы.

С этой реализацией, все еще необходимо волноваться о выше переменчивости байта. Если кто-то передает Вас byte[], который мог бы быть видоизменен, необходимо сделать защитную копию с помощью [1 120]:

kvs.put(ByteSequence.of(key.clone()), value);
0
ответ дан 24 November 2019 в 12:31
поделиться

Это нормально, если вам нужно только ссылочное равенство для вашего ключа - массивы не реализуют «равенство значений» так, как вы, вероятно, хотели бы. Например:

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 (и т. д.), у вас снова возникнут проблемы с поиском ключа ... вы можете скопировать данные в конструкторе ByteArrayWrapper , если хотите, но очевидно, что это будет пустой тратой производительности, если вы знаете, что не будете изменять содержимое массива байтов.

РЕДАКТИРОВАТЬ: Как упоминалось в комментариях, вы также можете использовать ByteBuffer для этого (в частности, его ByteBuffer # wrap (метод byte []) ). Я не знаю, правильно ли это, учитывая все дополнительные возможности ByteBuffer , которые вам не нужны, но это вариант.

72
ответ дан 24 November 2019 в 12:31
поделиться

I believe that arrays in Java do not necessarily implement the hashCode() and equals(Object) methods intuitively. That is, two identical byte arrays will not necessarily share the same hash code and they will not necessarily claim to be equal. Without these two traits, your HashMap will behave unexpectedly.

Therefore, I recommend against using byte[] as keys in a HashMap.

1
ответ дан 24 November 2019 в 12:31
поделиться

Я вижу проблемы, так как вы должны использовать Arrays.equals и Array.hashCode вместо реализаций массива по умолчанию

0
ответ дан 24 November 2019 в 12:31
поделиться

Arrays.toString (байты)

0
ответ дан 24 November 2019 в 12:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: