模板类HashMap【Key-Value】的结构对于Key的判断问题记录

问题现象

情况1:

由于两段代码的判断逻辑基本是一样的,所以两个HashMap的数据量一致。
当然不仅数量,数据也应该是完全一致的。

第一段代码(满足条件且HashMapA中 没有 的数据加载到HashMapA):

SomeKey Key;
for (Table.IterBegin() ; Table.IterNext() ; )
{
	if (!条件1 || !Key.set(Table.Field[xxx].Value) ||HashMapA.Find(Key)) continue;
	if (!条件2 || !条件3 || !条件4) continue;
	HashMapA.Add(Key);
}
//实际加载了10000条

第二段代码(满足条件且HashMapA中 的数据加载到HashMapB):

for (Table.IterBegin() ; Table.IterNext() ; )
{
	if (!条件1 || !Key.set(Table.Field[xxx].Value) || !HashMapA.Find(Key)) continue;
	if (!条件2 || !条件3 || !条件4) continue;
	HashMapB.Add(Key);
}
//实际加载了10000条

情况2:

第一段代码修改了两个if判断的顺序,居然数据量不等了!!!!
第一段代码(满足条件且HashMapA中 没有 的数据加载到HashMapA):

SomeKey Key;
for (Table.IterBegin() ; Table.IterNext() ; )
{
	if (!条件2 || !条件3 || !条件4) continue;
	if (!条件1 || !Key.set(Table.Field[xxx].Value) ||HashMapA.Find(Key)) continue;
	HashMapA.Add(Key);
}
//实际加载了10000条

第二段代码(满足条件且HashMapA中 的数据加载到HashMapB):

for (Table.IterBegin() ; Table.IterNext() ; )
{
	if (!条件1 || !Key.set(Table.Field[xxx].Value) || !HashMapA.Find(Key)) continue;
	if (!条件2 || !条件3 || !条件4) continue;
	HashMapB.Add(Key);
}
//实际加载了9994条

分析过程

推断

条件的顺序肯定是不影响逻辑的,那么问题一定出在HashMap.Add,或Find上。
由于HashMapA的数据量一直是10000没有变过,暂时推断Add没有问题。
先看看Find是否有问题。

验证

将两个HashMap加载的全部类容输出到文本文件,排序后比较。
果然HashMapA,没有发生任何变化。
而HashMapB在情况2中有6条数据不见了,假设一条其中一条叫MissingKey1。

调试

MissingKey1,也就是Key的结构如下:

struct SomeKey
{
        unsigned char Key[37];
		SomeKey() { memset(Key,0,sizeof(Key)); }
		SomeKey(const SomeKey& obj);
		SomeKey& operator=(const SomeKey& obj) { memcpy(Key, obj.Key, sizeof(Key)); return *this; }
		bool operator==(const SomeKey& obj) const { return memcmp(Key, obj.Key, sizeof(Key)) == 0; }
		myuint_t GetHash() const { return mymhashuA(Key, sizeof(Key)); }

                bool Set(const unsigned char* rawValue, myuint32_t rawSize)
                {
                     if(rawSize > 36 || rawSize <= 0) return false;
                     memcpy(Key,rawValue,rawSize);
                     Key[rawSize] = 0;
                     return true;
                }
                
                void Get(char* outvalue) const
                {
                     strcpy(outvalue, Key);
                }
};

发现非常简单,暂时看不出什么问题。
而通过跟踪程序,发现的确是HashMapA.Find(Key)的时候没找到。
那么为什么第一段代码修改顺序前就能找到呢?

继续分析

修改顺序前后,第一段代码唯一变化的就是

    Key.set(Table.Field[xxx].Value

这段语句,第一种情况下会比较多次的被执行,【敲黑板】。
但是由于下一个过滤条件,某些Key会被过滤掉不会加入HashMapA。
那我们来看看set:

                bool Set(const unsigned char* rawValue, myuint32_t rawSize)
                {
                     if(rawSize > 36 || rawSize <= 0) return false;
                     memcpy(Key,rawValue,rawSize);
                     Key[rawSize] = 0;
                     return true;
                }

内存拷贝最长36的字符串,并在最后添加0。
作为字符串的确没有什么问题。

再看HashMap是怎么Find的。
当然就是先GetHash出位置,再判断两个Key是否相等。
嗯,GetHash貌似没有问题(不想贴那么多代码了,mymhashuA也是基于字符串unsigned char*)。

判断两个Key是否相等,Key1==Key2.。。。纳尼?

bool operator==(const SomeKey& obj) const { return memcmp(Key, obj.Key, sizeof(Key)) == 0; }

等一下。。。

    memcmp(Key, obj.Key, sizeof(Key)) 

总算发现问题在哪里了,结构内部的Key是字符串,但判断是否相同是内存比较。

问题结论

先看看前面敲黑板的地方,修改代码顺序后,Key.set(),两段代码被执行的次数不一样。
也就是说,上一个被set的数据不一样。
那么MissingKey1符合条件准备被加入到HashMap时,如果比之前的Key长度短了的话。

这个MissingKey1在修改后的第一段代码的内容就会是:

    MissingKey1.key='13908085678\0未清空的上一条数据A一直铺满37个字节';

而在第二段代码里同一个MissingKey1的内容却是(上一条不一样啊):

    MissingKey1.key='13908085678\0未清空的上一条数据B一直铺满37个字节';

既然==是用的内存比较,那么当然就不一样啦!!!!

总结与修改

如果Key是定长的,不会出现这种情况。
如果Key是整型等,不会出现这种情况。
如果set前清空内存,也可避免这种情况:

                bool Set(const unsigned char* rawValue, myuint32_t rawSize)
                {
                     if(rawSize > 36 || rawSize <= 0) return false;
                     memset(Key,0,sizeof(Key));
                     memcpy(Key,rawValue,rawSize);
                     Key[rawSize] = 0;
                     return true;
                }

如果重载==用字符串比较,也可以避免:
Sorry, sorry… 如果要用字符串比较,则Hash也得改啊,写的时候没注意。。。

		bool operator==(const SomeKey& obj) const { return strcmp(Key, obj.Key) == 0; }
		myuint_t GetHash() const { return mymhashuA(Key, strlen(Key)); }

自此,怪异问题解决。
丢掉的数据找到了。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值