Generate a Hash from string in Javascript

前端 未结 22 997
不知归路
不知归路 2020-11-22 03:34

I need to convert strings to some form of hash. Is this possible in JavaScript?

I\'m not utilizing a server-side language so I can\'t do it that way.

22条回答
  •  心在旅途
    2020-11-22 04:23

    Note: Even with the best 32-bit hash, collisions will occur sooner or later.

    The hash collision probablility can be calculated as 1 - e ^ (-k(k-1) / 2N, aproximated as k^2 / 2N (see here). This may be higher than intuition suggests:
    Assuming a 32-bit hash and k=10,000 items, a collision will occur with a probablility of 1.2%. For 77,163 samples the probability becomes 50%! (calculator).
    I suggest a workaround at the bottom.

    In an answer to this question Which hashing algorithm is best for uniqueness and speed?, Ian Boyd posted a good in depth analysis. In short (as I interpret it), he comes to the conclusion that Murmur is best, followed by FNV-1a.
    Java’s String.hashCode() algorithm that esmiralha proposed seems to be a variant of DJB2.

    • FNV-1a has a a better distribution than DJB2, but is slower
    • DJB2 is faster than FNV-1a, but tends to yield more collisions
    • MurmurHash3 is better and faster than DJB2 and FNV-1a (but the optimized implementation requires more lines of code than FNV and DJB2)

    Some benchmarks with large input strings here: http://jsperf.com/32-bit-hash
    When short input strings are hashed, murmur's performance drops, relative to DJ2B and FNV-1a: http://jsperf.com/32-bit-hash/3

    So in general I would recommend murmur3.
    See here for a JavaScript implementation: https://github.com/garycourt/murmurhash-js

    If input strings are short and performance is more important than distribution quality, use DJB2 (as proposed by the accepted answer by esmiralha).

    If quality and small code size are more important than speed, I use this implementation of FNV-1a (based on this code).

    /**
     * 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;
    }
    

    Improve Collision Probability

    As explained here, we can extend the hash bit size using this trick:

    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)
    }
    

    Use it with care and don't expect too much though.

提交回复
热议问题