我有一个定期查询数据库的应用程序。它返回数百万字符串,绝大多数重复。我需要将所有这些记录存储在内存中,并且我正在努力减少占用空间。
我目前的设计是在每个字符串上调用GetHashCode(),然后存储哈希而不是字符串本身。
然后我尝试将其添加到Dictionary<hashcode,string>()
结构中。我还保留了Dictionary<hashcode,count>()
的第二个字典,当更多条目使用字符串时,该字典递增\递减。
在条目配置方法中,我递减计数器,如果使用率降至零,则从字典中删除字符串。
所以,有几个问题:
这是一个傻瓜差事吗?是否有一些我可以使用的数据类型比使用这个巨人节省了我很多时间和精力?我希望我的字符串表是线程安全的(目前它不是)。使用ConcurrentDictinary是我最好的选择吗?
提前致谢。
答案 0 :(得分:1)
这个问题的主要问题是两个不同的字符串可以具有相同的哈希码。
听起来你正在使它变得比它需要的更复杂。你需要的是在这里:
http://msdn.microsoft.com/en-us/library/system.string.intern.aspx
CLR已经维护了一个字符串实例表以节省内存。
<强>更新强>
但是......你应该记住文档中的警告:在CLR卸载之前,被拦截的字符串不会被垃圾收集,即它们会在你的app域的生命周期中闲置。
但是你可以很容易地自己实现相同的模式:
class LocalStringInterner
{
private Dictionary<string, string> _strings = new Dictionary<string, string>();
public string Intern(string str)
{
string interned;
if (_strings.TryGetValue(str, out interned))
return interned;
_strings.Add(str, str);
return str;
}
}
这样,当你不再需要那组字符串时,你可以放弃LocalStringInterner
。
为了安全地使用多个线程,您可以将Intern
的正文包裹在lock(_strings)
中。
答案 1 :(得分:0)
也许md5-Hash可以帮助你。它应该(理论上)是唯一的,并且得到大多数数据库的支持(如果不是C#将帮助你)。
MySQL:
SELECT name, md5(name)
FROM user
那就是说,我会考虑更好的数据库方法。
如果服务器端的每个字符串都有唯一的id,这应该是一项简单的任务。
假设您有一个名为string_resources
的表格,其中包含auto_increment id
列和varchar
字段。我还会在value
上添加一个唯一索引,以确保您不会存储两次字符串。
|id | value |
|1 | Hello |
|2 | World |
...
|145789 | Something else |
现在您可以将int值存储在词典中
md5: 128bit
int32: 32bit // <-- You Don't Say?
答案 2 :(得分:0)
我没有看到获取哈希码并将字符串存储在Dictionary<hash,string>
中以及将计数存储在单独的字典中的意义。您可以将字符串本身用作键,字典将自动(内部)创建和存储哈希码。因此,仅使用一个字典Dictionary<string,count>
就足够了。您还可以通过dict.Keys
从字典中检索字符串。
两个不同字符串的哈希码可以相同。这称为碰撞。 Dictionary<TKey,TValue>
会自动处理这些碰撞。
ConcurrentDictinary<TKey,TValue>
似乎是合适的;但是,我没有任何经验。