一、方法定义

Long.remainderUnsigned(long dividend, long divisor)java.lang.Long 类的 静态方法,用于计算两个 long 值之间的 无符号取余运算

方法签名:

public static long remainderUnsigned(long dividend, long divisor)
  • 参数
    • dividend:被除数(无符号解释)。
    • divisor:除数(无符号解释)。
  • 返回值long 类型,表示无符号取余的结果。
  • 异常:当 divisor == 0 时,抛出 ArithmeticException

二、功能说明

  • 无符号取余:将 long 值视为 64 位无符号整数 进行取余运算。
  • 关键区别
    • Java 原生的 % 运算符是有符号的,负数取余结果可能为负。
    • remainderUnsigned() 始终将操作数解释为无符号数,结果也非负。
  • 适用场景
    • 处理底层协议、哈希计算、位运算、与 C/C++ 无符号整数交互等。
  • 数学定义
    结果 r 满足:0 <= r < |divisor|(除数按无符号解释),且 (dividend - r) % divisor == 0

三、示例代码

示例 1:基本用法(正数)

long result = Long.remainderUnsigned(10L, 3L);
System.out.println(result); // 输出: 1
// 与 10 % 3 相同

示例 2:被除数为负数(关键差异体现)

long dividend = -1L; // 无符号解释为 2^64 - 1 ≈ 1.84e19
long divisor = 10L;

// 有符号取余(Java 原生 %)
System.out.println(-1 % 10); // 输出: -1

// 无符号取余
long result = Long.remainderUnsigned(-1L, 10L);
System.out.println(result); // 输出: 9
// 因为 (2^64 - 1) % 10 = 9

示例 3:大数取余(模拟无符号行为)

// 最大有符号 long
long max = Long.MAX_VALUE; // 2^63 - 1
long result1 = Long.remainderUnsigned(max, 100L);
System.out.println(result1); // 99

// 最小有符号 long(即 -2^63)
long min = Long.MIN_VALUE; // 无符号解释为 2^63
long result2 = Long.remainderUnsigned(min, 100L);
System.out.println(result2); // 0 (因为 2^63 是 100 的倍数?需计算)
// 实际:2^63 % 100 = 8 (可通过计算器验证)

示例 4:除数为负数(也按无符号处理)

long result = Long.remainderUnsigned(100L, -7L);
// -7 的无符号值是 2^64 - 7
// 100 < (2^64 - 7),所以结果就是 100
System.out.println(result); // 输出: 100

示例 5:除零异常

try {
    Long.remainderUnsigned(100L, 0L);
} catch (ArithmeticException e) {
    System.out.println("除数不能为零!"); // 正确捕获
}

四、使用技巧

  1. divideUnsigned() 配合使用
    两者成对出现,分别对应无符号的除法和取余。

    long quotient = Long.divideUnsigned(dividend, divisor);
    long remainder = Long.remainderUnsigned(dividend, divisor);
    // 验证:dividend == quotient * divisor + remainder
    
  2. 哈希表索引计算
    当使用 long 哈希码定位数组索引时,避免负索引:

    int index = (int) Long.remainderUnsigned(hashCode, array.length);
    
  3. 模拟无符号算术
    在需要处理 64 位无符号整数的场景中,替代第三方库。

  4. 与 C/C++ 交互一致性
    确保 Java 与 native 代码在无符号运算上行为一致。


五、常见错误

错误 1:认为 remainderUnsigned(-1, n) 等于 -1
✅ 实际上,-1 无符号值极大,其对 n 取余的结果是 (2^64 - 1) % n,通常是一个正数。

错误 2:忽略除零异常
✅ 必须检查 divisor == 0,否则会抛出 ArithmeticException

错误 3:与 Math.floorMod() 混淆
Math.floorMod(-1, 10) 返回 9(向下取整模),但仅适用于 int/long 范围内的正模数,而 remainderUnsigned 是真正的无符号语义。


六、注意事项

  • 性能:比原生 % 稍慢(因需处理无符号逻辑),但仍是高效操作。
  • 结果非负:无论输入如何,结果始终在 [0, |divisor|) 范围内。
  • 除数为负时divisor 被当作无符号数处理,若其无符号值大于被除数,则结果为被除数本身。
  • 溢出无关:无符号运算不涉及“溢出”概念,所有值都在 [0, 2^64) 范围内。

七、最佳实践

推荐做法

  1. 需要无符号语义时使用
    明确场景需要将 long 视为无符号整数。

  2. 哈希与索引计算
    安全地将 long 哈希码映射到数组索引。

    public int getIndex(long hash, int capacity) {
        return (int) Long.remainderUnsigned(hash, capacity);
    }
    
  3. divideUnsigned() 成对使用
    保持代码语义清晰。

  4. 避免手动模拟
    不要写 (a % b + b) % b 这类复杂表达式,直接用 remainderUnsigned 更清晰、正确。


八、性能优化

方法 性能 说明
a % b ⚡ 极快 CPU 原生指令
Long.remainderUnsigned(a, b) 🚀 快 JVM 内置优化,接近原生
手动无符号模拟 🐢 慢 多次运算与条件判断

💡 提示:JVM 会对 remainderUnsigned 进行优化,无需担心性能瓶颈。


九、总结

Long.remainderUnsigned() 是 Java 8 引入的重要工具,用于执行 64 位无符号取余运算,弥补了 Java 缺乏无符号类型的语言限制。

✅ 核心要点:

项目 说明
功能 无符号 long 取余
关键优势 结果非负,支持负数输入的无符号解释
典型用途 哈希索引、底层计算、与无符号系统交互
异常 除数为 0 时抛 ArithmeticException
配对方法 Long.divideUnsigned()