Xamarin中使用字节数组作为ConcurrentHashMap键值时不符合预期
问题原因拆解
这个问题我之前在Xamarin Android开发中也碰到过,核心是C#与Java跨绑定层的数组对象映射逻辑在搞鬼:
- Java的
ConcurrentHashMap是基于对象的equals()和hashCode()来匹配键的,而Java原生的byte[]数组的equals()方法是引用相等判断——只有当两个数组是同一个内存引用时才会返回true,不会对比数组内容。 - 在Xamarin中,当你把C#的
byte[]传递给Java方法(比如Put和Get)时,绑定层会自动把C#数组转换成Java的byte[]实例。但这里有个坑:每次传递同一个C#数组变量到Java层时,绑定层可能会生成一个全新的Java数组对象。也就是说,你调用map.Put(key, data)时传入的Java数组,和调用map.Get(key)时传入的Java数组,是两个完全不同的Java对象引用。 - 这就导致
ConcurrentHashMap在查找时,找不到和传入的新数组引用匹配的键,自然返回null;而通过map.Keys()遍历拿到的k是当初Put时存储的那个Java数组引用,用它调用Get当然能匹配到对应的值。
可行解决方案
针对这个问题,有几种不同的解决思路,你可以根据自己的场景选择:
1. 直接使用Java原生数组作为键
绕过C#数组的跨绑定转换问题,直接创建Java层的byte[]来作为键和值:
// 直接创建Java byte数组 var javaKey = new byte[] { 1, 2, 3 }; // Xamarin会自动识别为Java数组 var javaData = new byte[] { 1, 2, 3, 4, 5 }; map.Put(javaKey, javaData); var val = map.Get(javaKey); // 此时能正确获取到对应值
或者用Java.Util.Arrays工具类来转换C#数组,确保前后用的是同一个Java数组:
var csharpKey = new byte[] { 1, 2, 3 }; var javaKey = Java.Util.Arrays.ToByteArray(csharpKey); var javaData = Java.Util.Arrays.ToByteArray(new byte[] { 1, 2, 3, 4, 5 }); map.Put(javaKey, javaData); var val = map.Get(javaKey); // 正确获取值
2. 自定义键类,基于内容匹配
如果需要根据数组内容来匹配键(而不是引用),可以自定义一个继承自Java.Lang.Object的包装类,重写equals()和hashCode()方法,基于数组内容来实现判断:
public class ByteArrayKey : Java.Lang.Object { private readonly byte[] _innerBytes; public ByteArrayKey(byte[] bytes) { _innerBytes = bytes; } // 重写equals,基于数组内容判断相等 public override bool Equals(Java.Lang.Object other) { if (other is ByteArrayKey otherKey) { return Java.Util.Arrays.Equals(_innerBytes, otherKey._innerBytes); } return false; } // 重写hashCode,基于数组内容生成哈希值 public override int HashCode() { return Java.Util.Arrays.HashCode(_innerBytes); } }
使用方式也很简单:
var key = new ByteArrayKey(new byte[] { 1, 2, 3 }); var data = new byte[] { 1, 2, 3, 4, 5 }; map.Put(key, data); var val = map.Get(key); // 正确获取对应值
3. 改用C#原生的并发字典
如果你的场景不需要和Java代码交互,完全可以抛弃Java的ConcurrentHashMap,改用C#自己的ConcurrentDictionary<byte[], byte[]>,并自定义一个基于数组内容的相等比较器:
// 自定义数组内容比较器 public class ByteArrayEqualityComparer : IEqualityComparer<byte[]> { public bool Equals(byte[] x, byte[] y) { if (x == null || y == null) return x == y; return x.SequenceEqual(y); } public int GetHashCode(byte[] obj) { if (obj == null) return 0; int hash = 17; foreach (byte b in obj) { hash = hash * 31 + b; } return hash; } } // 使用示例 var concurrentDict = new ConcurrentDictionary<byte[], byte[]>(new ByteArrayEqualityComparer()); var key = new byte[] { 1, 2, 3 }; var data = new byte[] { 1, 2, 3, 4, 5 }; concurrentDict.TryAdd(key, data); var val = concurrentDict[key]; // 正确获取值
内容的提问来源于stack exchange,提问作者Luis
相关产品推荐
相关产品推荐

