我需要将字符串转换为某种形式的哈希。 这在JavaScript中可行吗?
我没有使用服务器端语言,所以我不能那样做。
#1楼
编辑
根据我的jsperf测试,可接受的答案实际上更快: http ://jsperf.com/hashcodelordvlad
原版的
如果有人感兴趣,这是一个改进的(更快的)版本,它将在缺少reduce
数组功能的旧版浏览器上失败。
hashCode = function(s){
return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
}
单线箭头功能版本:
hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0)
#2楼
我需要一个类似的函数(但有所不同)来根据用户名和当前时间生成一个唯一的ID。 所以:
window.newId = ->
# create a number based on the username
unless window.userNumber?
window.userNumber = 0
for c,i in window.MyNamespace.userName
char = window.MyNamespace.userName.charCodeAt(i)
window.MyNamespace.userNumber+=char
((window.MyNamespace.userNumber + Math.floor(Math.random() * 1e15) + new Date().getMilliseconds()).toString(36)).toUpperCase()
产生:
2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc
编辑2015年6月:对于新代码,我使用shortid: https ://www.npmjs.com/package/shortid
#3楼
如果对任何人都有用,我会将前两个答案组合成一个较旧的浏览器容忍版本,如果可以使用reduce
,则使用快速版本;如果没有,则使用esmiralha的解决方案。
/**
* @see http://stackoverflow.com/q/7616461/940217
* @return {number}
*/
String.prototype.hashCode = function(){
if (Array.prototype.reduce){
return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
}
var hash = 0;
if (this.length === 0) return hash;
for (var i = 0; i < this.length; i++) {
var character = this.charCodeAt(i);
hash = ((hash<<5)-hash)+character;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
用法就像:
var hash = new String("some string to be hashed").hashCode();
#4楼
注意:即使使用最佳的32位哈希,冲突迟早也会发生。
哈希冲突概率可以计算为
,近似为 ( 请参见此处 )。 这可能比直觉暗示的要高:
假设一个32位哈希且k = 10,000个项目,则发生碰撞的概率为1.2%。 对于77,163个样本,概率变为50%! ( 计算器 )。
我建议在底部的一种解决方法。
在回答这个问题时, 哪种哈希算法最适合唯一性和速度? ,伊恩·博伊德(Ian Boyd)进行了深入的分析 。 简而言之(据我的解释),他得出的结论是Murmur最好,其次是FNV-1a。
esmiralha提出的Java String.hashCode()算法似乎是DJB2的一种变体。
- FNV-1a比DJB2具有更好的分布,但速度较慢
- DJB2比FNV-1a快,但往往会产生更多碰撞
- MurmurHash3比DJB2和FNV-1a更好和更快(但是优化的实现比FNV和DJB2需要更多的代码行)
一些带有较大输入字符串的基准测试: http : //jsperf.com/32-bit-hash
散列短输入字符串时,相对于DJ2B和FNV-1a,杂音的性能下降: http ://jsperf.com/32-bit-hash/3
因此,一般而言,我会推荐murmur3。
参见此处以获取JavaScript实现: https : //github.com/garycourt/murmurhash-js
如果输入字符串短并且性能比分发质量更重要,请使用DJB2(由esmiralha接受的答案提出)。
如果质量和小的代码大小比速度更重要,那么我将使用FNV-1a的此实现(基于此代码 )。
/**
* Calculate a 32 bit FNV-1a hash
* Found here: https://gist.github.com/vaiorabbit/5657561
* Ref.: http://isthe.com/chongo/tech/comp/fnv/
*
* @param {string} str the input value
* @param {boolean} [asString=false] set to true to return the hash value as
* 8-digit hex string instead of an integer
* @param {integer} [seed] optionally pass the hash of the previous chunk
* @returns {integer | string}
*/
function hashFnv32a(str, asString, seed) {
/*jshint bitwise:false */
var i, l,
hval = (seed === undefined) ? 0x811c9dc5 : seed;
for (i = 0, l = str.length; i < l; i++) {
hval ^= str.charCodeAt(i);
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
}
if( asString ){
// Convert to 8 digit hex string
return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
}
return hval >>> 0;
}
提高碰撞概率
如此处所述 ,我们可以使用以下技巧扩展哈希位大小:
function hash64(str) {
var h1 = hash32(str); // returns 32 bit (as 8 byte hex string)
return h1 + hash32(h1 + str); // 64 bit (as 16 byte hex string)
}
请谨慎使用,但不要期望过高。
#5楼
我结合了两种解决方案(用户esmiralha和lordvlad)来获得一个函数,该函数对于支持js函数reduce()并仍与旧浏览器兼容的浏览器应该更快:
String.prototype.hashCode = function() {
if (Array.prototype.reduce) {
return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
} else {
var hash = 0, i, chr, len;
if (this.length == 0) return hash;
for (i = 0, len = this.length; i < len; i++) {
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
};
例:
my_string = 'xyz';
my_string.hashCode();
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3157703