Utilizando uma matriz de bytes como chave Mapa
Pergunta
Você vê algum problema com o uso de uma matriz de bytes como chave do Mapa? Eu também poderia fazer new String(byte[])
e haxixe por String
mas é mais simples de usar byte[]
.
Solução
O problema é que a identidade usos byte[]
objeto para equals
e hashCode
, de modo que
byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}
não irá corresponder em uma HashMap
. Vejo três opções:
- Embrulho em um
String
, mas então você tem que ter cuidado sobre a codificação problemas (você precisa ter certeza de que o byte -> String -> byte dá-lhe os mesmos bytes). - Use
List<Byte>
(pode ser caro na memória). - Faça a sua própria classe de embrulho, escrevendo
hashCode
eequals
de usar o conteúdo da matriz de bytes.
Outras dicas
Está tudo bem, desde que você só deseja igualdade de referência para a sua chave - matrizes não implementar "valor igualdade" na maneira que você provavelmente gostaria. Por exemplo:
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());
imprime algo como:
false
1671711
11394033
(Os números reais são irrelevantes;. O fato de que eles são diferentes é importante)
Assumindo que você realmente quer a igualdade, eu sugiro que você criar o seu próprio invólucro que contém um byte[]
e implementos igualdade e código hash geração apropriadamente:
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);
}
}
Note que se você alterar os valores dentro da matriz de bytes depois de usar o ByteArrayWrapper
, como uma chave em um HashMap
(etc) você terá problemas olhando para cima a tecla novamente ... você poderia tirar uma cópia dos dados em o construtor ByteArrayWrapper
se quiser, mas obviamente que vai ser um desperdício de desempenho se você sabe que você não estar mudando o conteúdo da matriz de bytes.
EDIT: Como mencionado nos comentários, você também pode usar ByteBuffer
para este (em particular, a sua ByteBuffer#wrap(byte[])
método). Eu não sei se é realmente a coisa certa, dadas todas as habilidades extras que ByteBuffer
s têm que você não precisa, mas é uma opção.
Podemos utilizar para este ByteBuffer (Isto é, basicamente, o invólucro byte [] com um comparador)
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)));
irá imprimir
true
Você pode usar java.math.BigInteger
. Tem um construtor BigInteger(byte[] val)
. É um tipo de referência, de modo que poderia ser usado como uma chave para hashtable. E .equals()
e .hashCode()
são definidos como para os respectivos números inteiros, o que significa que tem BigInteger equals semântica consistentes como byte [] matriz.
Estou muito surpreso que as respostas não estão apontando a alternativa mais simples.
Sim, não é possível usar um HashMap, mas ninguém o impede de usar um SortedMap como alternativa. A única coisa é escrever um comparador que precisa para comparar as matrizes. Não é tão alto desempenho como um HashMap, mas se você quiser uma alternativa simples, aqui vai (você pode substituir SortedMap com o mapa se você quiser esconder a implementação):
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;
}
}
Esta implementação pode ser ajustado para outras matrizes, a única coisa que você deve estar ciente de que as matrizes de igual (= comprimento igual com os membros iguais) deve retornar 0 e que você tem uma ordem DETERMISTIC
Eu acredito que os arrays em Java não necessariamente implementar os métodos hashCode()
e equals(Object)
intuitivamente. Ou seja, duas matrizes de bytes idênticos não necessariamente compartilham o mesmo código hash e eles não serão necessariamente afirmam ser iguais. Sem essas duas características, o seu HashMap irá se comportar de forma inesperada.
Portanto, eu recomendo contra usando byte[]
como chaves em um HashMap.
Você deve usar criar um somthing classe como ByteArrKey e hashcode sobrecarga e métodos iguais, lembre-se o contrato entre eles.
Isso irá dar-lhe uma maior flexibilidade no que pode saltar 0 entradas que estão apensas no final da matriz de bytes, especialmente se copiar apenas alguma parte formar o outro tampão de byte.
Desta forma, você vai decidir como ambos os objetos devem ser iguais.
Eu vejo problemas, pois você deve usar Arrays.equals e Array.hashCode, no lugar de implementações de matriz padrão
Arrays.toString (bytes)
Você também pode converter o byte [] para uma string 'seguro' usando Base32 ou Base64, por exemplo:
byte[] keyValue = new byte[] {…};
String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue);
é claro que existem muitas variantes do acima, como:
String key = org.apache.commons.codec.binary.Base64.encodeBase64(keyValue);
Aqui é uma solução utilizando a interface e método Java java.util.Arrays.equals TreeMap, comparadores (byte [], byte []);
NOTA: A ordenação no mapa não é relevante com este método
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;
}
}