1. 概述

本文将探讨在Java中从唯一字符串生成唯一整数的几种实现方案。每种方案在速度、简单性和唯一性保证上各有侧重,需要根据实际场景权衡选择。

2. 唯一性的含义

唯一性要求不同字符串映射到不同整数,但受限于int的2^32取值范围,当处理海量字符串时冲突不可避免。⚠️

  • 概率唯一性:如hashCode()方法,冲突概率低但存在理论可能
  • 绝对唯一性:如查找映射方案,通过额外存储保证无冲突

选择方案时需评估:

  • 输入字符串规模
  • 可接受的冲突概率
  • 性能与内存限制

2.1. 验证方案

使用参数化测试验证各实现方案:

private static Stream<Arguments> implementations() {
    return Stream.of(Arguments.of(Named.<Function<String, Integer>> of("toIntByHashCode", StringToUniqueInt::toIntByHashCode)),
      Arguments.of(Named.<Function<String, Integer>> of("toIntByCR32", StringToUniqueInt::toIntByCR32)),
      Arguments.of(Named.<Function<String, Integer>> of("toIntByCharFormula", StringToUniqueInt::toIntByCharFormula)),
      Arguments.of(Named.<Function<String, Integer>> of("toIntByMD5", StringToUniqueInt::toIntByMD5)),
      Arguments.of(Named.<Function<String, Integer>> of("toIntByLookup", StringToUniqueInt::toIntByLookup))
    );
}
@ParameterizedTest
@MethodSource("implementations")
public void given1kElements_whenMappedToInt_thenItShouldHaveNoDuplicates(Function<String, Integer> implementation) {
    Stream<String> strings = uniqueStringsOfSize(1_000); // 可增大数量提高验证强度

    List<Integer> integers = strings.map(implementation)
      .toList();

    assertThat(integers).doesNotHaveDuplicates();
}

测试逻辑:

  1. 生成大量唯一字符串
  2. 应用各转换方案
  3. 验证结果整数列表无重复

3. 实现方案

3.1. 使用String.hashCode()

最直接的方案,利用Java内置方法:

public static int toIntByHashCode(String value) {
    return value.hashCode();
}

优点

  • 极致性能(JVM原生优化)
  • 零依赖

缺点

  • 存在冲突可能(如"Aa"和"BB"的hashCode相同)
  • 不适合关键业务场景

适用场景:缓存键、非关键哈希计算等性能敏感场景

3.2. 自定义字符公式

通过字符运算实现可控哈希:

public static int toIntByCharFormula(String value) {
    return value.chars()
      .reduce(17, (a, b) -> a * 13 + (b / (a + 1))); // 可替换为其他公式
}

优点

  • 完全可控的哈希逻辑
  • 可针对特定数据优化

缺点

  • 冲突风险与hashCode()相当
  • 需要充分测试验证

适用场景:教学演示、特殊数据分布优化

3.3. 使用CRC32校验和

基于java.util.zip的校验算法:

public static int toIntByCR32(String value) {
    CRC32 crc32 = new CRC32();
    crc32.update(value.getBytes());
    return (int) crc32.getValue();
}

优点

  • hashCode()更低的冲突率
  • 专为数据校验设计

缺点

  • 性能低于hashCode()
  • 仍非绝对唯一

适用场景:文件索引、数据完整性校验等可靠性要求场景

3.4. 使用MD5与位运算

通过加密哈希提取整数:

public static int toIntByMD5(String value) {
    try {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        byte[] hash = digest.digest(value.getBytes());
        return ((hash[0] & 0xFF) << 24) | ((hash[1] & 0xFF) << 16) 
          | ((hash[2] & 0xFF) << 8) | (hash[3] & 0xFF);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("MD5 not supported", e);
    }
}

优点

  • 极低冲突概率(128位哈希截取)
  • 适合高可靠性场景

缺点

  • 性能开销最大
  • 对简单场景过度设计

适用场景:唯一键生成、高可靠性标识系统

3.5. 查找映射方案

通过内存存储保证绝对唯一:

private static final Map<String, Integer> lookupMap = new HashMap<>();
private static final AtomicInteger counter = new AtomicInteger(Integer.MIN_VALUE);
public static int toIntByLookup(String value) {
    var found = lookupMap.get(value);
    if (found != null) {
        return found;
    }
    var intValue = counter.incrementAndGet();
    lookupMap.put(value, intValue);
    return intValue;
}

优点

  • 绝对唯一性保证
  • 线程安全实现

缺点

  • 内存消耗随字符串数量线性增长
  • 需要持久化机制(否则重启失效)

适用场景:数据库主键、持久化标识符等关键业务

4. 总结

方案 速度 唯一性 内存消耗 适用场景
hashCode() ⚡⚡⚡⚡⚡ 概率唯一 极低 缓存、非关键哈希
自定义公式 ⚡⚡⚡⚡ 概率唯一 极低 教学、特殊数据优化
CRC32 ⚡⚡⚡ 低冲突概率 文件索引、数据校验
MD5 ⚡⚡ 极低冲突概率 高可靠性唯一键
查找映射 绝对唯一 数据库键、持久化标识

选择建议:

  • 性能优先hashCode()或自定义公式
  • 可靠性优先:CRC32或MD5
  • 绝对唯一:查找映射(注意内存管理)

完整代码示例见GitHub仓库


原始标题:Generate a Unique Integer From a Unique String | Baeldung