笔者最近在练习Mysql语句优化,奈何年少不懂,找不到百万级别的测试数据,只好用java随机生成数据凑合用一下,所以写下此篇博客,经测试生成500万条数据后台用了9秒,完全可以接受
1. Random
random伪随机数类在 java.util 包下,是最常用的随机数生成器,其使用线性同余公式来生成随机数,所以才说是伪随机。该类的实例是线程安全的,多线程并发使用可能会遇到争用问题,这时可用 ThreadLocalRandom 来解决这个问题,此外还有 SecureRandom 、SplittableRandom 随机生成器,这里就不扩展说明了
2. 构造方法与常用方法
类型 | 名字 | 解释 |
---|---|---|
Random() | 默认构造函数 | |
Random(long seed) | 有参构造,用种子创建伪随机生成器 | |
int | nextInt | 返回生成器中生成表序列中的下一个伪随机数 |
int | nextInt(int n) | 返回均匀分布于区间 [0,n)的伪随机数 |
double | nextDouble | 返回下一个伪随机数 [0.0,1.0) |
3. 具体分析
先看无参构造,直接上源码
// 无参构造也是调用有参构造的,那么放出有参构造,再看里面具体内容
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
// 有参构造接收长整型种子参数
public Random(long seed) {
// 判断是否本类
if (getClass() == Random.class)
// 可以看出长整型种子是Atomic原子型的,即线程安全
// initialScramble() 是seed与两个具体数值运算,这里不给出了
this.seed = new AtomicLong(initialScramble(seed));
else {
// subclass might have overriden setSeed
// 翻译:子类可能重写setSeed方法
this.seed = new AtomicLong();
setSeed(seed);
}
}
// 再回无参构造内部
// 其中 ^ System.nanoTime() 表示与系统纳秒异或运算,也就是说随机数依赖于时间
this(seedUniquifier() ^ System.nanoTime());
private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
private static long seedUniquifier() {
// L'Ecuyer, "Tables of Linear Congruential Generators of
// Different Sizes and Good Lattice Structure", 1999
// 翻译:不同大小结构良好的线性同余生成元表,
for (;;) {
long current = seedUniquifier.get();
long next = current * 181783497276652981L;
// 用到了CAS轻量锁
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
再看nextInt方法,有参的方法用逻辑运算把范围指定,这里就不介绍了
public int nextInt() {
return next(32);
}
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask; // 都是具体的值位运算
} while (!seed.compareAndSet(oldseed, nextseed)); // 改变值
return (int)(nextseed >>> (48 - bits)); // 可能这些位运算就是线性同余把
}
简单使用
Random r1 = new Random();
Random r2 = new Random();
Random r3 = new Random();
Random r4 = new Random(1000);
Random r5 = new Random(1000);
System.out.println(r1.nextInt());
System.out.println(r2.nextInt());
System.out.println(r3.nextInt(100));
System.out.println(r4.nextInt());
System.out.println(r5.nextInt());
491030142
2021835847
49
-1244746321
-1244746321
从结果和源码可以看出:
- 这里补充一下seed是final类型,线程更安全
- 给定seed之后,伪随机数的序列是确定的
- 而没有给seed因为依赖于变化的时间,所以每次的序列是不确定的
- 常用
new Random.nextInt(int n)
来生成伪随机数
4. Math.random
我们最常用还是这个函数,静态调用方便简单
// 底层还是用了Random类
public static double random() {
return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
// 新建一个依赖时间的随机数生成器
private static final class RandomNumberGeneratorHolder {
static final Random randomNumberGenerator = new Random();
}
// 位运算加强随机
public double nextDouble() {
return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;
}
从源码可以看出:
- 这个类方便我们使用伪随机数,每次调用就新建一个Random类
- 也知道区间为 [0.0,1.0)
生成给定范围的伪随机数
// 给定范围
int min = 10;
int max = 15;
// 生成伪随机小数
double num = Math.random();
// 范围逻辑运算,想一下很简单的
int rs = (int)(num * (max - min + 1) + min);
System.out.println(rs); // 需要整数的位数
5. 这里贴一下生成测试数据中密码的逻辑
// 密码字符范围
String range = "0[email protected]#$%^&*()_+[];',.<>?:{}|";
// 生成100个伪随机密码
for(int i = 0; i < 100; i++){
// 字符串
StringBuffer bf = new StringBuffer();
// 密码长度8~20
int len = (int)(Math.random() * (20 - 8 + 1) + 8);
for(int j = 0; j < len; j++){
int index = new Random().nextInt(range.length());
bf.append(range.charAt(index));
}
System.out.println(bf.toString());
}
[email protected]<s
|4z$1sDIDRt_o{PR
H_}z;A9;K74amjb2r
O;*89#b!|4w|;z?~
s+EmeTCdpJ9?W8,lNNl|
o2#[email protected],hFT
{+})BECM.Jf|&
// 完全看不懂,还可以加上MD5加密
原文地址:https://www.cnblogs.com/Howlet/p/12319487.html
时间: 2024-10-12 10:12:51