Mapキーとしてバイト配列を使用することに問題はありますか? new String(byte[])
とString
によるハッシュもできますが、byte[]
を使用する方が簡単です。
問題はそれです byte[]
はequals
およびhashCode
にオブジェクトIDを使用するため、
byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}
HashMap
では一致しません。次の3つのオプションがあります。
String
でラップしますが、エンコードの問題には注意する必要があります(バイト->文字列->バイトが同じバイトを与えることを確認する必要があります)。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
を使用した後にバイト配列内の値を変更すると、キーを再度検索する際に問題が発生することに注意してください...必要に応じてByteArrayWrapper
コンストラクタのデータのコピーを取得しますが、wo n'tバイト配列の内容を変更している。
編集:コメントで述べたように、これにByteBuffer
を使用することもできます(特に、 ByteBuffer#wrap(byte[])
メソッド)。 ByteBuffer
sに必要のない追加の能力がすべて与えられているので、それが本当に正しいかどうかはわかりませんが、これはオプションです。
このためにByteBufferを使用できます(これは基本的にコンパレーターを備えたbyte []ラッパーです)
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)
コンストラクターがあります。これは参照型なので、ハッシュテーブルのキーとして使用できます。また、.equals()
と.hashCode()
はそれぞれの整数値として定義されています。つまり、BigIntegerにはbyte []配列と同じ一貫したセマンティクスがあります。
私は、答えが最も単純な代替案を指摘していないことに非常に驚いています。
はい、HashMapを使用することはできませんが、代わりにSortedMapを使用することを妨げるものはいません。唯一のものは、配列を比較する必要があるコンパレータを書くことです。 HashMapほど高性能ではありませんが、簡単な代替手段が必要な場合は、ここに進みます(実装を非表示にする場合は、SortedMapをMapに置き換えることができます)。
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)
メソッドを直感的に実装するとは限りません。つまり、2つの同一のバイト配列は必ずしも共有しませんこれらの2つの特性がないと、HashMapは予期しない動作をします。
したがって、againstを使用してbyte[]
はHashMapのキーとして。
ByteArrKeyのようなクラスを作成し、ハッシュコードと同等のメソッドをオーバーロードする必要があります。それらの間のコントラクトを覚えておいてください。
これにより、特にバイト配列の一部のみをコピーする場合に、バイト配列の最後に追加される0エントリをスキップできるため、柔軟性が向上します。
この方法で、両方のオブジェクトを等しくする必要がある方法を決定します。
たとえば、Base32またはBase64を使用してbyte []を「安全な」文字列に変換することもできます。次に例を示します。
byte[] keyValue = new byte[] {…};
String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue);
もちろん、上記には多くのバリエーションがあります。
String key = org.Apache.commons.codec.binary.Base64.encodeBase64(keyValue);
TreeMap、Comparatorインターフェイス、およびJavaメソッドJava.util.Arrays.equals(byte []、byte []);
注:マップ内の順序は、この方法とは関係ありません
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;
}
}
デフォルトの配列実装の代わりにArrays.equalsとArray.hashCodeを使用する必要があるため、問題が発生します。
Arrays.toString(bytes)
また、このような独自のカスタムByteHashMapを作成できます。
ByteHashMap byteMap = new ByteHashMap();
byteMap.put(keybyteArray,valueByteArray);
完全な実装は次のとおりです
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;
}
}
}