random.nextint(n)生成[0, n)范围整数;生成[min, max]需用nextint(max - min + 1) + min,注意溢出与边界,多线程推荐threadlocalrandom.current().nextint(min, max + 1)。

Java 中 Random.nextInt(int bound) 的正确用法
直接结论:Random.nextInt(n) 生成的是 [0, n) 范围内的随机整数(含 0,不含 n),不是 [0, n]。想生成 [min, max](含两端)的整数,得手动做偏移和范围调整。
常见错误是写成 random.nextInt(max - min),结果永远漏掉 max;或者没加 min 偏移,导致下界不对。
- 要生成 [min, max](含)的随机整数:用
random.nextInt(max - min + 1) + min -
max - min + 1是合法取值个数,必须作为bound参数传入 - 如果
min > max,会抛出IllegalArgumentException - 注意整数溢出风险:当
max接近Integer.MAX_VALUE且min为负时,max - min + 1可能溢出 —— 这种边缘情况建议改用ThreadLocalRandom.current().nextInt(min, max + 1)
为什么不用 Math.random() 强转?
Math.random() 返回 double 类型 [0.0, 1.0),靠乘法缩放再强转 int 容易踩精度和边界坑。
例如:(int)(Math.random() * (max - min + 1)) + min 理论上可行,但 Math.random() 最大值严格小于 1.0,所以 Math.random() * N 最大值严格小于 N,对整数截断后仍能覆盖 [0, N) —— 表面看没问题。但实际中:
- double 乘法存在浮点误差,极小概率使结果等于
N(比如因舍入),强转后越界 - JVM 实现差异可能导致不同版本行为不一致
-
Math.random()内部其实也用Random,还多一层 synchronized 开销
所以除非遗留代码,否则别绕路。
多线程环境下优先用 ThreadLocalRandom
普通 Random 实例在高并发下会出现竞争,性能下降明显;而 ThreadLocalRandom 每线程独立实例,无锁。
生成 [min, max] 整数更简洁:ThreadLocalRandom.current().nextInt(min, max + 1) —— 注意第二个参数是 exclusive 上界,所以要写 max + 1。
- 该方法底层已处理溢出检查,比手算
max - min + 1更安全 - 不能用
new ThreadLocalRandom(),必须通过current()获取 - 它不支持设置种子(seed),所以不可重现 —— 测试时若需确定性随机,请坚持用
Random并传入固定 seed
边界值测试容易忽略的细节
写单元测试时,光测 min 和 max 两个值远远不够。真正容易出问题的是:
-
min == max:此时应稳定返回该值,但手误写成nextInt(max - min)就会除零异常 -
min == Integer.MIN_VALUE且max == Integer.MAX_VALUE:范围总数超过2^31,max - min本身就会溢出 —— 必须用ThreadLocalRandom.nextInt(min, max + 1),它内部用 long 运算规避了这个问题 - 负数范围如 [-5, -1]:确认是否真的包含 -1(即上界闭合),很多人误以为
nextInt(-1 + 5) - 5能覆盖到 -1,其实算出来是 [-5, -1),漏了 -1
边界永远比直觉更脆,尤其涉及整数溢出和 inclusive/exclusive 语义混用的时候。










