Map キーとしてバイト配列を使用することに問題はありますか? new String(byte[])
とハッシュすることもできますがString
、 を使用する方が簡単byte[]
です。
13 に答える
キーの参照の等価性のみが必要な場合は問題ありません。配列は、おそらく必要な方法で「値の等価性」を実装しません。例えば:
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
(etc)のキーとして ,を使用した後にバイト配列内の値を変更するとHashMap
、キーを再度検索する際に問題が発生することに注意してください...必要にByteArrayWrapper
応じて、コンストラクターでデータのコピーを取得できます、しかし、バイト配列の内容を変更しないことがわかっている場合、明らかにパフォーマンスが無駄になります。
ByteBuffer
EDIT:コメントで述べたように、これ(特にそのByteBuffer#wrap(byte[])
メソッド)にも使用できます。必要のない余分な能力がすべてあることを考えると、それが本当に正しいことなのかどうかはわかりByteBuffer
ませんが、それはオプションです.
問題は、がとにbyte[]
オブジェクト ID を使用するため、equals
hashCode
byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}
では一致しませんHashMap
。次の 3 つのオプションが表示されます。
- でラップし
String
ますが、エンコーディングの問題に注意する必要があります (バイト -> 文字列 -> バイトが同じバイトを与えることを確認する必要があります)。 - 使用します
List<Byte>
(メモリの負荷が高くなる可能性があります)。 - 独自のラッピング クラスを作成し、バイト配列の内容を記述
hashCode
して使用します。equals
これには 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)
コンストラクタがあります。これは参照型なので、ハッシュテーブルのキーとして使用できます。And.equals()
と.hashCode()
は、それぞれの整数として定義されています。つまり、BigInteger は byte[] 配列として一貫した equals セマンティクスを持っています。
答えが最も単純な代替案を指摘していないことに非常に驚いています。
はい、HashMap を使用することはできませんが、代わりに SortedMap を使用することを誰も妨げません。唯一のことは、配列を比較する必要がある Comparator を書くことです。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 を返さなければならず、決定的な順序があることです。
ByteArrKey のようなクラスを作成し、hashcode と equal メソッドをオーバーロードする必要があります。それらの間の契約を覚えておいてください。
これにより、バイト配列の最後に追加された 0 エントリをスキップできるため、柔軟性が高まります。特に、他のバイト バッファから一部のみをコピーする場合に当てはまります。
このようにして、両方のオブジェクトがどのように等しいかを決定します。
Java の配列は必ずしもhashCode()
andequals(Object)
メソッドを直感的に実装するとは限りません。つまり、2 つの同一のバイト配列が必ずしも同じハッシュ コードを共有するとは限らず、必ずしも等しいと主張するとは限りません。これら 2 つの特性がないと、HashMap は予期しない動作をします。
したがって、HashMap のキーとして使用しないことをお勧めします。byte[]
Arrays.toString(バイト)
デフォルトの配列実装の代わりに Arrays.equals と Array.hashCode を使用する必要があるため、問題が発生します
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);