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