Java 实现 .NET 字符串HashCode算法

发布于:2/25/2022, 11:11:00 AM @孙博
技术分享 | Java,Hash
许可协议:署名-非商业性使用(by-nc)

早前曾经分享过一篇《TypeScript 实现 .NET 字符串HashCode算法》,将 .NET Framework 中曾使用的字符串哈希算法翻译成了 Typescript 表达。

有朋友向我咨询是否存在 Java 版的,其实是有的,而且也在生产运行了很多年,只是 Java 比较容易翻译此前我就没发出来,现在再额外补充一篇吧。

与Typescript那篇一样,首先是 C# 源码。

// HashUtils.cs
public sealed class HashUtils
{
    private HashUtils() { }
    /// <summary>
    /// 计算哈希值
    /// </summary>
    /// <param name="text">对象字符串</param>
    /// <returns>哈希结果</returns>
    public static int CalcHashCode(string text)
    {
        var chars = Encoding.Unicode.GetBytes(text);
        var half_pre = 0x15051505;
        var half_post = half_pre;
        var index = 0;
        for (int i = text.Length; i > 0; i -= 4)
        {
            var data = new int[]
            {
                    calc(chars, index + 3, 24) + calc(chars, index + 2, 16) + calc(chars, index + 1, 8) + calc(chars, index + 0, 0),
                    calc(chars, index + 7, 24) + calc(chars, index + 6, 16) + calc(chars, index + 5, 8) + calc(chars, index + 4, 0),
            };
            half_pre = (((half_pre << 5) + half_pre) + (half_pre >> 0x1b)) ^ data[0];
            if (i <= 2)
            {
                break;
            }
            half_post = (((half_post << 5) + half_post) + (half_post >> 0x1b)) ^ data[1];
            index += 8;
        }
        return half_pre + (half_post * 0x5d588b65);
    }

    private static int calc(byte[] bytes, int index, int offset)
    {
        if (index >= bytes.Length)
        {
            return 0;
        }
        return bytes[index] << offset;
    }
}

然后下面是 Java 版

package com.luckystarry.utility;

import java.nio.charset.StandardCharsets;

public class HashUtils {

    /**
     * C-Shape 实现
     * 
     * @param text 要计算哈希值的字符串
     * @return 字符串的哈希值
     */
    private static int getHashCode(String text) {
        byte[] bytes = text.getBytes(StandardCharsets.UTF_16LE);
        int[] ints = new int[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            ints[i] = bytes[i] & 0xff;
        }
        int half_pre = 0x15051505;
        int half_post = half_pre;
        int index = 0;
        for (int i = text.length(); i > 0; i -= 4) {
            int[] data = new int[] {
                    calc(ints, index + 3, 24) + calc(ints, index + 2, 16) + calc(ints, index + 1, 8)
                            + calc(ints, index + 0, 0),
                    calc(ints, index + 7, 24) + calc(ints, index + 6, 16) + calc(ints, index + 5, 8)
                            + calc(ints, index + 4, 0), };
            half_pre = (((half_pre << 5) + half_pre) + (half_pre >> 0x1b)) ^ data[0];
            if (i <= 2) {
                break;
            }
            half_post = (((half_post << 5) + half_post) + (half_post >> 0x1b)) ^ data[1];
            index += 8;
        }

        return (half_pre + (half_post * 0x5d588b65));
    }

    private static int calc(int[] ints, int index, int offset) {
        if (index >= ints.length) {
            return 0;
        }
        return ints[index] << offset;
    }
}