Skip to content

Commit 8be75b3

Browse files
author
Nicolai Parlog
committed
Demonstrate random number generation
1 parent d163b8a commit 8be75b3

File tree

6 files changed

+207
-0
lines changed

6 files changed

+207
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ Check out the [jpms](http://blog.codefx.org/tag/jpms/) tag on my blog, [this dem
8989

9090
## New APIs
9191

92+
*[random generator](src/main/java/org/codefx/demo/java17/api/random) ([article](https://nipafx.dev/java-random-generator/), [JEP 356](https://openjdk.java.net/jeps/356), [very good Javadoc](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/random/package-summary.html))
9293
*[Unix domain sockets](src/main/java/org/codefx/demo/java16/api/unix_sockets) ([article](https://nipafx.dev/java-unix-domain-sockets/), [JEP-380](https://openjdk.java.net/jeps/380))
9394
* ⑪ HTTP/2 client: [simple](src/main/java/org/codefx/demo/java11/api/http2/Http2Api.java), [more formal](src/main/java/org/codefx/demo/java11/api/http2/formalized) ([tutorial](https://blog.codefx.org/java/http-2-api-tutorial/), [reactive request/response bodies](https://blog.codefx.org/java/reactive-http-2-requests-responses/))
9495
* ⑩⑨ version API, [introduced in Java 9](src/main/java/org/codefx/demo/java9/api/version/VersionApi.java),

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
<compilerArgs>
5252
<!-- get access to `@ReservedStackAccess` in `ReservedStackAccess` -->
5353
<arg>--add-exports=java.base/jdk.internal.vm.annotation=org.codefx.demo.java_x</arg>
54+
<!-- get access to `@RandomGeneratorProperties` in `Xkcd` -->
55+
<arg>--add-exports=java.base/jdk.internal.util.random=org.codefx.demo.java_x</arg>
5456
</compilerArgs>
5557
</configuration>
5658
</plugin>
@@ -104,6 +106,10 @@
104106
<configuration>
105107
<sourcepath>src/main/java</sourcepath>
106108
<subpackages>org.codefx.demo.java18.jvm.javadoc</subpackages>
109+
<additionalJOptions>
110+
<!-- get access to `@RandomGeneratorProperties` in `Xkcd` -->
111+
<additionalJOption>--add-exports=java.base/jdk.internal.util.random=org.codefx.demo.java_x</additionalJOption>
112+
</additionalJOptions>
107113
</configuration>
108114
<executions>
109115
<execution>

src/main/java/module-info.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import org.codefx.demo.java17.api.random.Xkcd;
2+
3+
import java.util.random.RandomGenerator;
4+
15
/**
26
* Project demonstrating various Java features introduced since Java 9.
37
*/
@@ -6,4 +10,6 @@
610

711
requires java.desktop;
812
requires java.net.http;
13+
14+
provides RandomGenerator with Xkcd;
915
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.codefx.demo.java17.api.random;
2+
3+
import java.util.random.RandomGenerator;
4+
import java.util.random.RandomGenerator.ArbitrarilyJumpableGenerator;
5+
import java.util.random.RandomGeneratorFactory;
6+
import java.util.stream.Stream;
7+
8+
public class RandomGeneratorFactories {
9+
10+
// to run this, you need to add the following command line option:
11+
// --add-exports=java.base/jdk.internal.util.random=org.codefx.demo.java_x
12+
public static void main(String[] args) {
13+
RandomGenerator with128StateBits = createWithAtLeastStateBits(128);
14+
15+
System.out.println("ARBITRARILY JUMPABLE GENERATORS:");
16+
// if everything went well, this should show the custom XKCD generator,
17+
// which is integrated with the generator factory API
18+
createArbitrarilyJumpableGenerators()
19+
.forEach(g -> System.out.println(" - " + g.getClass().getName()));
20+
21+
System.out.println("STOCHASTIC GENERATORS:");
22+
createStochasticGenerators()
23+
.forEach(g -> System.out.println(" - " + g.getClass().getName()));
24+
}
25+
26+
/*
27+
* The casts aren't ideal. If you don't want to call the interface-specific
28+
* methods on the created instances, they're not needed.
29+
*/
30+
31+
private static RandomGenerator createWithAtLeastStateBits(int stateBits) {
32+
return RandomGeneratorFactory.all()
33+
.filter(factory -> factory.stateBits() >= stateBits)
34+
.findAny()
35+
.map(RandomGeneratorFactory::create)
36+
.orElseThrow();
37+
}
38+
39+
40+
private static Stream<ArbitrarilyJumpableGenerator> createArbitrarilyJumpableGenerators() {
41+
return RandomGeneratorFactory.all()
42+
.filter(RandomGeneratorFactory::isArbitrarilyJumpable)
43+
.map(RandomGeneratorFactory::create)
44+
.map(ArbitrarilyJumpableGenerator.class::cast);
45+
}
46+
47+
private static Stream<RandomGenerator> createStochasticGenerators() {
48+
return RandomGeneratorFactory.all()
49+
.filter(RandomGeneratorFactory::isStochastic)
50+
.map(RandomGeneratorFactory::create);
51+
}
52+
53+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package org.codefx.demo.java17.api.random;
2+
3+
import java.security.SecureRandom;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
import java.util.Random;
7+
import java.util.SplittableRandom;
8+
import java.util.concurrent.ThreadLocalRandom;
9+
import java.util.random.RandomGenerator;
10+
import java.util.random.RandomGenerator.JumpableGenerator;
11+
import java.util.random.RandomGenerator.LeapableGenerator;
12+
import java.util.random.RandomGenerator.SplittableGenerator;
13+
14+
public class RandomGenerators {
15+
16+
public static void main(String[] args) {
17+
newInterface();
18+
simpleCreation();
19+
moreInterfaces();
20+
}
21+
22+
private static void newInterface() {
23+
// `RandomGenerator` is a new interface that combines the APIs
24+
// of pre-existing random classes, which all implement it
25+
RandomGenerator random = new Random();
26+
RandomGenerator threadLocal = ThreadLocalRandom.current();
27+
RandomGenerator secure = new SecureRandom();
28+
RandomGenerator splittable = new SplittableRandom();
29+
}
30+
31+
private static void simpleCreation() {
32+
// if you have no requirements
33+
var generator = RandomGenerator.getDefault();
34+
// if you know which algorithm you need
35+
// (be careful, this is fragile as algorithms can be removed;
36+
// use RandomGeneratorFactory instead - see other demo class)
37+
// (for implemented algorithms, see Javadoc of java.util.random)
38+
var lxm = RandomGenerator.of("L128X128MixRandom");
39+
var xoshiro = RandomGenerator.of("Xoshiro256PlusPlus");
40+
}
41+
42+
private static void moreInterfaces() {
43+
// There are many more new interfaces. They all extend `RandomGenerator`
44+
// and only differ in how you can create a new instance from an existing one.
45+
46+
// The casts aren't ideal. If you don't want to call the interface-specific
47+
// methods on the newly-created instances, they're not needed.
48+
49+
JumpableGenerator jumpable = JumpableGenerator.of("Xoroshiro128PlusPlus");
50+
JumpableGenerator newJumpable = (JumpableGenerator) jumpable.copyAndJump();
51+
52+
LeapableGenerator leapable = LeapableGenerator.of("Xoshiro256PlusPlus");
53+
LeapableGenerator newLeapable = (LeapableGenerator) leapable.copyAndLeap();
54+
55+
// there's no `ArbitrarilyJumpableGenerator` (in JDK 18)
56+
// ArbitrarilyJumpableGenerator arbitrarilyJumpable = ArbitrarilyJumpableGenerator.of("Xoshiro256PlusPlus");
57+
58+
SplittableGenerator splittable = SplittableGenerator.of("L128X1024MixRandom");
59+
SplittableGenerator newSplittable = splittable.split();
60+
}
61+
62+
private long randomPercentage(Random random) {
63+
// compile error on JDK <17 :( because there,
64+
// `nextLong(bound)` only exists on
65+
// `SplittableRandom` and `ThreadLocalRandom`
66+
return random.nextLong(101);
67+
}
68+
69+
private record User() {
70+
71+
// nay (don't pass specific classes)
72+
User randomUser(ArrayList<User> users, Random random) {
73+
int index = random.nextInt(users.size());
74+
return users.get(index);
75+
}
76+
77+
// yay (instead pass the new interface)
78+
User randomUser(List<User> users, RandomGenerator random) {
79+
int index = random.nextInt(users.size());
80+
return users.get(index);
81+
}
82+
83+
}
84+
85+
86+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.codefx.demo.java17.api.random;
2+
3+
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
4+
5+
import java.util.random.RandomGenerator.ArbitrarilyJumpableGenerator;
6+
7+
/*
8+
* It's possible to provide custom implementations of the generator interfaces that
9+
* get picked up by the generator factory API (including the `.of("name")` methods),
10+
* but that requires use of the JDK-internal annotation `@RandomGeneratorProperties`.
11+
*
12+
* While this works, the integration mechanism isn't meant for public use.
13+
*/
14+
15+
@RandomGeneratorProperties(
16+
name="xkcd",
17+
group="fancypants",
18+
equidistribution = 0,
19+
i = 1,
20+
k = -1
21+
)
22+
public class Xkcd implements ArbitrarilyJumpableGenerator {
23+
24+
@Override
25+
public ArbitrarilyJumpableGenerator copy() {
26+
return this;
27+
}
28+
29+
@Override
30+
public double jumpDistance() {
31+
return Math.pow(2, 64);
32+
}
33+
34+
@Override
35+
public double leapDistance() {
36+
return Math.pow(2, 128);
37+
}
38+
39+
@Override
40+
public void jumpPowerOfTwo(int logDistance) { }
41+
42+
@Override
43+
public void jump(double distance) { }
44+
45+
@Override
46+
public long nextLong() {
47+
return 4;
48+
}
49+
50+
@Override
51+
public String toString() {
52+
return "XKCD Random Number Generator - chosen by fair dice roll";
53+
}
54+
55+
}

0 commit comments

Comments
 (0)