一、方法定义
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()
始终将操作数解释为无符号数,结果也非负。
- Java 原生的
- 适用场景:
- 处理底层协议、哈希计算、位运算、与 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("除数不能为零!"); // 正确捕获
}
四、使用技巧
与
divideUnsigned()
配合使用
两者成对出现,分别对应无符号的除法和取余。long quotient = Long.divideUnsigned(dividend, divisor); long remainder = Long.remainderUnsigned(dividend, divisor); // 验证:dividend == quotient * divisor + remainder
哈希表索引计算
当使用long
哈希码定位数组索引时,避免负索引:int index = (int) Long.remainderUnsigned(hashCode, array.length);
模拟无符号算术
在需要处理 64 位无符号整数的场景中,替代第三方库。与 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)
范围内。
七、最佳实践
✅ 推荐做法:
需要无符号语义时使用
明确场景需要将long
视为无符号整数。哈希与索引计算
安全地将long
哈希码映射到数组索引。public int getIndex(long hash, int capacity) { return (int) Long.remainderUnsigned(hash, capacity); }
与
divideUnsigned()
成对使用
保持代码语义清晰。避免手动模拟
不要写(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() |