C#中基于整数数组与属性值维护唯一集合的优化方案
优化整数数组作为唯一标识的对象集合性能方案
问题背景
- 需求:维护一个对象集合,对象唯一性由整数数组逐值比较判定;若数组内容相同,保留
PropertyB属性值最高的对象。 - 当前实现:采用
Dictionary<string, myObject>,以整数数组拼接成的字符串(通过string.Join(",", array)生成)作为键。添加对象时,若键已存在,当新对象PropertyB值更大时替换原有对象。 - 性能瓶颈:大数据量场景下,
string.Join操作的字符串拼接耗时严重,成为性能短板。
当前实现代码(含已知错误)
private readonly Dictionary<string, myObject> myHashLookup = new Dictionary<(string,int), myObject>(); // 注:此处存在类型声明不匹配错误,实际应为Dictionary<string, myObject> public void Add(myObject t) { string id = t.GetId(); // 内部实现为string.Join(",", myIntArray) myObject cur_object; myHashLookup.TryGetValue(id, out cur_object); if (cur_object == null || (t.PropertyB > cur_object.PropertyB)) myHashLookup[id)] = t; // 注:此处多了一个右括号,应为myHashLookup[id] = t; }
优化方案
方案1:自定义整数数组相等比较器(通用场景)
直接以整数数组作为Dictionary的键,通过自定义IEqualityComparer<int[]>实现数组的逐值相等判断与哈希码计算,完全避免字符串拼接操作。
自定义比较器实现
public class IntArrayEqualityComparer : IEqualityComparer<int[]> { public bool Equals(int[] x, int[] y) { if (ReferenceEquals(x, y)) return true; if (x == null || y == null) return false; if (x.Length != y.Length) return false; // 逐元素比较 for (int i = 0; i < x.Length; i++) { if (x[i] != y[i]) return false; } return true; } public int GetHashCode(int[] obj) { if (obj == null) return 0; // 基于数组元素计算哈希码,采用经典的哈希组合方式 int hash = 17; foreach (int num in obj) { hash = hash * 31 + num.GetHashCode(); } return hash; } }
修改后的集合与Add方法
// 使用自定义比较器初始化Dictionary private readonly Dictionary<int[], myObject> myHashLookup = new Dictionary<int[], myObject>(new IntArrayEqualityComparer()); public void Add(myObject t) { int[] key = t.myIntArray; // 直接使用对象的整数数组作为键 if (myHashLookup.TryGetValue(key, out var curObject)) { // 仅当新对象PropertyB更大时替换 if (t.PropertyB > curObject.PropertyB) { myHashLookup[key] = t; } } else { myHashLookup.Add(key, t); } }
方案2:固定长度数组用结构体包装(特殊场景)
如果整数数组的长度固定,可以定义结构体持有数组元素,利用结构体的默认值比较特性,无需自定义比较器即可作为Dictionary的键,性能更优。
示例(数组长度固定为3)
// 定义结构体作为键 public struct FixedArrayKey { public int Item1 { get; } public int Item2 { get; } public int Item3 { get; } public FixedArrayKey(int[] array) { if (array.Length != 3) throw new ArgumentException("数组长度必须为3"); Item1 = array[0]; Item2 = array[1]; Item3 = array[2]; } }
修改后的集合与Add方法
private readonly Dictionary<FixedArrayKey, myObject> myHashLookup = new Dictionary<FixedArrayKey, myObject>(); public void Add(myObject t) { var key = new FixedArrayKey(t.myIntArray); if (myHashLookup.TryGetValue(key, out var curObject)) { if (t.PropertyB > curObject.PropertyB) { myHashLookup[key] = t; } } else { myHashLookup.Add(key, t); } }
方案对比
- 方案1:适配任意长度的整数数组,无需修改对象结构,性能比字符串拼接提升显著,是通用场景的最优选择。
- 方案2:仅适用于固定长度数组,结构体的哈希码与相等比较由编译器自动生成,性能略高于方案1,代码更简洁。
内容的提问来源于stack exchange,提问作者user1012525
相关产品推荐
相关产品推荐

