如何利用哈希与字典优化ClientModule列表的查找删除逻辑?
问题
我的程序中,服务器会向客户端发送模块,客户端加载完成后返回模块信息。服务器将待加载的客户端信息存在LoadingClientModules列表里,收到响应后移除对应项标记加载完成。
目前用List.RemoveAll()遍历整个列表,通过匹配ModuleName和TimeDateStamp来移除目标项,不仅效率低,代码可读性也差。想知道能不能通过这两个成员的哈希值改用Dictionary来优化实现。
当前代码:
public List<ClientModule> LoadingClientModules = new(); public List<ClientModule> LoadedClientModules = new(); private void ClientModuleAdded_Callback(Packet packet) { ClientModule f = Utils.ByteArrayToStructure<ClientModule>(packet.Payload, 0); Utils.Log.Information($"{_ClientInformation.ComputerUser} Loaded {f.ModuleName} TimeDateStamp: {f.TimeDateStamp} Address: {f.Address}"); LoadedClientModules.Add(f); LoadingClientModules.RemoveAll(i => i.ModuleName == f.ModuleName && i.TimeDateStamp == f.TimeDateStamp); OnClientModuleLoaded?.Invoke(this, f); }
解决方案
完全可以用Dictionary替代List来优化查找和移除效率,核心是把ModuleName和TimeDateStamp组合成唯一键,直接通过键访问目标项,时间复杂度从O(n)降到O(1)。
有两种常用实现方式:
方式一:用ValueTuple作为Dictionary的键
C# 7.0及以上支持ValueTuple,直接把(string ModuleName, long TimeDateStamp)作为键,无需额外定义类:
// 替换原List为Dictionary,键是ModuleName+TimeDateStamp的组合 public Dictionary<(string ModuleName, long TimeDateStamp), ClientModule> LoadingClientModules = new(); public List<ClientModule> LoadedClientModules = new(); private void ClientModuleAdded_Callback(Packet packet) { ClientModule f = Utils.ByteArrayToStructure<ClientModule>(packet.Payload, 0); Utils.Log.Information($"{_ClientInformation.ComputerUser} Loaded {f.ModuleName} TimeDateStamp: {f.TimeDateStamp} Address: {f.Address}"); LoadedClientModules.Add(f); // 直接通过组合键查找并移除,效率更高 var key = (f.ModuleName, f.TimeDateStamp); LoadingClientModules.Remove(key); OnClientModuleLoaded?.Invoke(this, f); }
注意:添加待加载模块到LoadingClientModules时,也要用同样的组合键作为Key。
方式二:为ClientModule重写GetHashCode和Equals
如果不想用ValueTuple,可以在ClientModule类中重写GetHashCode()和Equals()方法,让类本身可以作为Dictionary的键:
public class ClientModule { public string ModuleName { get; set; } public long TimeDateStamp { get; set; } public IntPtr Address { get; set; } // 其他成员... public override bool Equals(object obj) { if (obj is not ClientModule other) return false; return ModuleName == other.ModuleName && TimeDateStamp == other.TimeDateStamp; } public override int GetHashCode() { // 组合两个字段的哈希值,确保唯一性 return HashCode.Combine(ModuleName, TimeDateStamp); } }
然后Dictionary定义为:
public Dictionary<ClientModule, ClientModule> LoadingClientModules = new(); // 或者如果不需要存重复对象,也可以用HashSet<ClientModule>,更轻量 // public HashSet<ClientModule> LoadingClientModules = new();
回调方法中移除逻辑改为:
LoadingClientModules.Remove(f); // 如果用HashSet就是 LoadingClientModules.Remove(f);
注意事项
- 确保
ModuleName和TimeDateStamp的组合是唯一的,避免键冲突导致数据丢失。 - 如果用ValueTuple作为键,要保证两个字段的类型和顺序完全匹配。
- 若
ClientModule是可变类,要避免作为键的字段(ModuleName、TimeDateStamp)在存入Dictionary后被修改,否则会导致无法正确查找。
内容的提问来源于stack exchange,提问作者Meme Machine
相关产品推荐
相关产品推荐

