摘要:hashCode函数是java中一个比较重要的函数,在工程上有较多的应用。如何用python和C++实现相同的功能呢?其难点在于java内部编码统一为unicode,而python和C++的编码形式比较多样,同样的函数使用不同的编码得到的结果可能不一样(甚至python2和python3都不一样)。本文主要就此进行介绍。

一,java的hashCode函数实现:

Python hashset原理 hashcode python_java

public int hashCode() 
{ 
    int h = hash;     
    if (h == 0 && value.length > 0) 
    { 
        char val[] = value; 
        for (int i = 0; i < value.length; i++) 
        { 
            h = 31 * h + val[i]; 
        }    
        hash = h; 
    } 
    return h;
}

我们对中文字符串“玄幻”进行hashCode:

public class Java_HashCode {
    public static void main(String args[])
    {
        
        String str= "玄幻";
        long hashValue = str.hashCode();
        System.out.println(hashValue);        
    }
}

结果如下:

940919

二,Python的hashCode函数实现:

def hashCode(s):
    seed = 31
    h = 0
    for c in s:
        h = int32(seed * h) + ord(c)
    return h

从形式上,与java的hashCode函数一致,但python与java在编码形式上具有不同。Java内码统一为unicode, 而python的编码问题是python核心难点之一,而且python2和python3还有区别,这就导致在实现字符串hashCode的时候,可能出现与Java不同的hash值。例如,对“玄幻”这个中文字符串进行上面的hashCode:

#-*- coding: utf-8 -*-
tagList=["玄幻"]
print(tagList)
for tag in tagList:
    print(type(tag))
    print(tag)
    hashValue = hashCode(tag)
    print(hashValue)

python3.6的结果是:

['玄幻']
<class 'str'>
玄幻
940919

python2.7的结果是:

['\xe7\x8e\x84\xe5\xb9\xbb']
<type 'str'>
玄幻
-1841302326

我们发现,python3结果与java一致,而python2则不同,为什么会有这种差别呢?我们看看在hashCode函数中插入日志,如下:

def hashCode(s):
    seed = 31
    h = 0
    for c in s:
        print(type(c)) #打印字符类型
        print(c) #打印字符
        h = int32(seed * h) + ord(c)
    return h

再次对字符串“玄幻”进行hashCode:

python3.6的结果是:

['玄幻']
<class 'str'>
玄幻
<class 'str'>
玄
<class 'str'>
幻
940919

python2.7的结果是:

['\xe7\x8e\x84\xe5\xb9\xbb']
<type 'str'>
玄幻
<type 'str'>
�
<type 'str'>
�
<type 'str'>
�
<type 'str'>
�
<type 'str'>
�
<type 'str'>
�
-1841302326

hashCode函数需要遍历字符串每一个字符,我们可以看到,python3在处理“玄幻”这个中文字符串时,是作为2个字符处理的,而python2则是当作6个字符(每个中文字符在linux下占3个utf-8编码)。如果对此有疑问,可以参考我之前的博客

如果是python2,要想得到与java和python3一样的hash结果,需要将编码解码为unicode格式。如下:

#-*- coding: utf-8 -*-
tagList=["玄幻"]
print(tagList)
for tag in tagList:
    print(type(tag))
    print(tag)
    print(tag.decode("utf-8"))
    hashValue = hashCode(tag.decode("utf-8"))
    print(hashValue)

得到结果为:

['\xe7\x8e\x84\xe5\xb9\xbb']
<type 'str'>
玄幻
玄幻
<type 'unicode'>
玄
<type 'unicode'>
幻
940919

三,C++的hashCode函数实现:

先上结论: 需要通过类型转化将字符转为宽字符类型, 才能将utf-8编码解码为unicode,之后再做hash。

关于字符编码转换,如果是windows上,一般常用的方法是调用wcstombs、mbstowcs 函数,和 windows API 的 MultiByteToWideChar 与 WideCharToMultiByte 函数来完成向 Unicode 的转入和转出。但是,Linux下则无法使用这些函数,可以参考

在C++中实现hashCode函数,如下:

std::wstring s2ws(const std::string& str) {                                       
    if (str.empty()) {                                                              
              return L"";                                                                                                                                               
    }                                                                               
    unsigned len = str.size() + 1;                                                  
    setlocale(LC_CTYPE, "en_US.UTF-8");                                             
    wchar_t *p = new wchar_t[len];                                                  
    mbstowcs(p, str.c_str(), len);                                                  
    std::wstring w_str(p);                                                          
    delete[] p;                                                                     
    return w_str;                                                                   
}
                                                                                   
int32_t hashCode(const std::string& str) {                                         
    int32_t h = 0;                                                                  
    //SoDebug("strlength is: %u",str.length());                                     
    std::wstring wstr=s2ws(str);                                                    
    //SoDebug("wstr.length is %u",wstr.length());                                   
    if (h == 0 && wstr.length() > 0) {                                              
      for (uint32_t i = 0; i < wstr.length(); i++) {                                
      h = 31 * h + wstr.at(i);                                                      
      }                                                                             
    }                                                                                                                                                                   
   return h;                                                          
}