我有一个
byte[]
的哈希集,我想测试该集合中是否有新的 byte[]
。 问题在于,Java 似乎是在测试 byte[]
实例是否相同,而不是测试字节数组中的实际值是否相同。
换句话说,请考虑以下代码:
public class Test
{
public static void main(String[] args)
{
java.util.HashSet<byte[]> set=new java.util.HashSet<byte[]>();
set.add(new String("abc").getBytes());
System.out.println(set.contains(new String("abc").getBytes()));
}
}
此代码打印出
false
,我希望它打印出true
。 我该怎么做呢?
您可以使用
ByteBuffer.wrap
包装每个字节数组,这将为您提供正确的 equals 和 hashCode 行为。请小心您在 ByteBuffer
上调用的方法(不要修改数组或推进其指针)。
您可以创建一个
ByteArray
类来包装字节数组并按照您想要的方式测试相等性。 然后你就会有一个Set<ByteArray>
。
您可以定义自己的包装类,但可能最简单的方法是将数组“包装”到 ArrayList 中并使用
HashSet<ArrayList>
。
import com.google.common.collect.ImmutableSet;
import java.nio.ByteBuffer;
import java.util.Set;
import static com.google.common.base.Charsets.UTF_8;
import static java.nio.ByteBuffer.wrap;
public class Scratch
{
public static void main(String[] args)
{
final Set<ByteBuffer> bbs = ImmutableSet.of(wrap("abc".getBytes(UTF_8)).asReadOnlyBuffer());
System.out.println("bbs.contains(ByteBuffer.wrap(\"abc\".getBytes(Charsets.UTF_8))) = " + bbs.contains(wrap("abc".getBytes(UTF_8)).asReadOnlyBuffer()));
}
}
您应该永远不要将
String
转换为byte[]
而不提供Charset
,结果会根据默认值Charset
变得依赖于运行时,这通常不是一个好的结果并且可以更改。
.asReadOnlyBuffer()
很重要!
创建一个新的只读字节缓冲区,共享该缓冲区的 内容。新缓冲区的内容将是该缓冲区的内容。 对此缓冲区内容的更改将在新缓冲区中可见; 然而,新缓冲区本身将是只读的,并且不允许 需要修改的共享内容。
两个缓冲区的位置、限制和标记值将是独立的。
新缓冲区的容量、限制、位置和标记值将与该缓冲区的容量、限制、位置和标记值相同。 如果此缓冲区本身是只读的,则此方法的行为完全相同 与复制方法相同。
你可以避免包装器和愚蠢的 hashCode 问题(嘿,像 byte[] 这样的标准东西没有 hashCode 对吧?):
使用TreeSet代替HashSet,并在实例化时提供byte[]比较器:
Set<byte[]> byteATreeSet = new TreeSet<byte[]>(new Comparator<byte[]>() {
public int compare(byte[] left, byte[] right) {
for (int i = 0, j = 0; i < left.length && j < right.length; i++, j++) {
int a = (left[i] & 0xff);
int b = (right[j] & 0xff);
if (a != b) {
return a - b;
}
}
return left.length - right.length;
}});
如果你从其他地方得到一个 byte[] HashSet b,请先将你的变量 a 初始化为 TreeSet,然后使用 a.addAll(b);这样,即使 b 包含重复项,a 也不会。
实现 Adam Crume 的想法。
public class ByteArraySet
{
Set<BaWrapper> bws;
public ByteArraySet()
{
bws = new HashSet<BaWrapper>();
}
public boolean contains(final byte[] a)
{
return bws.contains(new BaWrapper(a));
}
public boolean add(final byte[] a)
{
return bws.add(new BaWrapper(a));
}
public boolean remove(final byte[] a)
{
return bws.remove(new BaWrapper(a));
}
public int size()
{
return bws.size();
}
private static class BaWrapper
{
byte[] a;
BaWrapper(final byte[] a)
{
this.a = a;
}
@Override
public boolean equals(final Object rhs)
{
return Arrays.equals(a, ((BaWrapper) rhs).a);
}
@Override
public int hashCode()
{
return Arrays.hashCode(a);
}
}
}