With all due respect, I doubt you will generate random numbers

Keywords: Java Apache Lambda xml

Once, when I was browsing Stack Overflow, I found a problem: "how to generate a random number within a specified range in Java". I thought to myself, "there are 3.98 million readings for this problem. Are you sure there are any mistakes in the statistics? It's just about Math.random(). "

So I directly used my power to vote against it. As a result, the joy of not waiting for the power to be implemented received a reminder: "people with a reputation value lower than 125 have the right to vote, but will not show it publicly." Oh, me, I'm going. I'm heartbroken. Just because I'm so angry, I don't need code to prove my strength. Do I have the face to say that I have ten years of development experience? So I excitedly opened IDEA and typed the following code:

public class GenerateMathRandomInteger {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Runnable r = () -> {
            int generatedInteger = leftLimit + (int) (Math.random() * rightLimit);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

Is this code OK with me? At first glance, the names of parameters and classes are reasonable, even Lambda expressions. But the output of the program is unexpected:

8
10
10
4
3
4
6
12
3

Where did 12 come from? Of course, it's from the bug s in the program. The random number generated by leftLimit + (int) (Math.random() * rightLimit) may be out of the specified range. No, Math.random() doesn't believe it. It has to be a different way. In a flash, I thought of the random class, so I wrote a new code:

public class GenerateRandomInteger {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Random random = new Random();
        int range = rightLimit - leftLimit + 1;

        Runnable r = () -> {
            int generatedInteger = leftLimit + random.nextInt() % range;
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

This time, I'm confident that if Math.random() can't solve the problem, random.nextInt() will be able to solve it. As a result, the output hit my handsome face again.

0
-3
10
2
2
-4
-4
-6
6

There are even negative numbers, which is really a cruel reality. I was educated and seemed to get back to the feeling that I would be devastated by the leaders when I just joined the company. Fortunately, I'm not as irritable as I was when I was young. I'm stable: it doesn't matter if something goes wrong. It's right to find a solution.

So five minutes later, I wrote the following code:

public class GenerateRandomInteger {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Random random = new Random();
        int range = rightLimit - leftLimit;

        Runnable r = () -> {
            int generatedInteger = leftLimit + (int)(random.nextFloat() * range);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

Whether it's adjusting the number of threads or rerunning multiple times, the result is expected to be between 2 - 11.

7
2
5
8
6
2
9
9
7

The nextFloat() method returns a random floating-point number (including 0.0f, but excluding 1.0f) evenly distributed between 0 and 1, multiplied by the difference between the maximum value and the minimum value, and then converted to int type to ensure that the random number is between 0 and (maximum minimum value), and finally added with the minimum value, you can get the number within the specified range.

If you are willing to read the source code, you will find that the Random class has a nextInt(int bound) method, which will return a Random integer, evenly distributed between 0 - bound (including 0, but not the specified bound). Then using this method can also get an effective Random number, see the example code.

public class GenerateRandomNextInt {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Random random = new Random();
        Runnable r = () -> {
            int generatedInteger = leftLimit + random.nextInt(rightLimit - leftLimit + 1);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

+ 1 is required because nextInt() does not contain a bound. The results of the program operation also meet the expectations:

8
2
9
8
4
6
4
5
7

You see, my previous two attempts ended in failure, but I still didn't give up hope. After careful consideration, I found two feasible solutions. This reminds me of a poem by Pushkin:

If life deceives you, don't be sad, don't be anxious. You need to be calm in the melancholy days. Everything will pass, everything will pass, everything will pass. The fire of hope needs to be rekindled and cared for, so as not to let the storm extinguish it, or to let oneself despair in the dark, cold and helpless.

After reciting a good poem, let's think about other plans. Anyway, I thought that Java 7 can use the ThreadLocalRandom class later. The code example is as follows:

public class GenerateRandomThreadLocal {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Runnable r = () -> {
            int generatedInteger = ThreadLocalRandom.current().nextInt(leftLimit, rightLimit +1);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

The output of the program is as follows:

11
9
6
10
6
6
10
7
3

The ThreadLocalRandom class inherits from the Random class. It uses internally generated seeds to initialize (it can't be set externally, so it can't reproduce the test scenario), and it doesn't need to explicitly use the new keyword to create objects (Random can set seeds through the construction method), and it can directly obtain objects at the local thread level through the static method current():

static final ThreadLocalRandom instance = new ThreadLocalRandom();

static final void localInit() {
    int p = probeGenerator.addAndGet(PROBE_INCREMENT);
    int probe = (p == 0) ? 1 : p; // skip 0
    long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
    Thread t = Thread.currentThread();
    U.putLong(t, SEED, seed);
    U.putInt(t, PROBE, probe);
}

public static ThreadLocalRandom current() {
    if (U.getInt(Thread.currentThread(), PROBE) == 0)
        localInit();
    return instance;
}

The advantage of this is that in the multi-threaded or thread pool environment, unnecessary memory overhead can be saved.

Finally, I provide another solution, using the RandomDataGenerator class of the Apache Commons Math class library. Before using the class library, the dependency of the class library needs to be introduced into the pom.xml file.

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-math3</artifactId>
    <version>3.6.1</version>
</dependency>

When you need to generate a random number in the specified range, use the new RandomDataGenerator() to get the random generator instance, and then use the nextInt() method to directly get the random number between the maximum value and the minimum value. Take a look at the example.

public class RandomDataGeneratorDemo {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Runnable r = () -> {
            int generatedInteger = new RandomDataGenerator().nextInt( leftLimit,rightLimit);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

The output is as follows:

8
4
4
4
10
3
10
3
6

The result is exactly what we expected - this is my last move, I didn't expect to give it all to you so happily.

Well, my dear readers, that's all of this article. If you think the article is helpful to you, please search "silent King II" on wechat and read it first time. The sample code has been uploaded to GitHub, Portal

I'm silent Wang Er, an interesting programmer. It's not easy to be original. Don't ask for a blank ticket. Please comment on this article. It will be the strongest driving force for me to write more high-quality articles.

Posted by dinger on Mon, 13 Apr 2020 23:59:43 -0700