Generating random values with the Stream API

The Stream API is one of the great features introduced in the JDK 8 release. Most of the time you use the Stream API to operate on values that come from standard collections, files, databases or other data structures. However, sometimes you need to create a new stream from scratch and in some situations, as a special case, you may need to create a stream composed of random values. In this article I will explain this latter case, that is, how to create a stream of random values.

Note: some knowledge of the Stream API, even at basic level, is a prerequisite to read this article. If you know what is the java.util.stream.Stream class (and variants for primitive values like IntStream) and what are the mapToObj and forEach operations, then you shouldn’t have any problems.

First of all, we must distinguish between two slightly different cases: generating a stream of random numbers and generating a stream of random objects (of any type). These two cases are not totally unrelated, as you will see shortly, but it’s good to treat them separately.

Stream of random numbers

When the Stream API was added in JDK 8, the java.util.Random class and its subclasses (SecureRandom and ThreadLocalRandom) were also updated as well. In particular, these classes have got 12 new methods, which are as follows:

public DoubleStream doubles()
public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound)
public DoubleStream doubles(long streamSize)
public DoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberBound)

public IntStream ints()
public IntStream ints(int randomNumberOrigin, int randomNumberBound)
public IntStream ints(long streamSize)
public IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound)

public LongStream longs()
public LongStream longs(long randomNumberOrigin, long randomNumberBound)
public LongStream longs(long streamSize)
public LongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound)

These are all public “instance” methods, thus you must first get an instance of Random (or subclass of Random) before you can use them. Each of these methods creates a new stream of random numbers that is configured according to the argument values. You can create a random stream of only three primitive types, int, long and double. These are generally sufficient in most cases.

At this point you just need to carefully understand the meaning of the streamSize, randomNumberOrigin and randomNumberBound parameters.

The streamSize parameter

The streamSize parameter specifies the exact number of values to generate. In other words, the stream has a well known, finite, size. However, if you use one of the variants without the streamSize parameter, the resulting stream is virtually “infinite”. In this latter case you need to limit the stream size in some other way, for example using the limit(maxSize) operation provided by the Stream API.

The randomNumberOrigin and randomNumberBound parameters

These two parameters specify the range of values to use for generating the random numbers. Pay attention here, because randomNumberOrigin is inclusive while randomNumberBound is exclusive (this value is never generated). If you are more mathematically inclined, we can say that for any value x randomly generated, the following is always satisfied:

randomNumberOriginx < randomNumberBound

If you use one of the variants without the randomNumberOrigin/randomNumberBound parameters, the range of values is the widest possible based on the chosen data type (e.g. from -2147483648 to +2147483647 for a stream of int values).

A concrete example

At this point you should notice that generating a stream of random numbers is a very easy task! The following is a concrete example: we want to generate a stream of 30 int values between -100 and 100 (both extremes included).

import java.util.Random;
import java.util.stream.IntStream;

public class RandomIntStreamDemo {
    public static void main(String[] args) {
        Random rnd = new Random();
        IntStream numStream = rnd.ints(30, -100, 101);    // 101 is NOT included!
        numStream.forEach(n -> System.out.print(n + "  "));
    }
}

This is an example of the output produced:

81  83  -40  -21  -20  -2  -49  14  -73  3  55  7  -63  96  -14  97  85  -3  92  -12  -52  -47  95  -88  100  -19  -51  -93  -63  27  

Stream of random objects

What if we want to generate a stream of random objects? Before we continue, it’s necessary to clarify the context. Let’s imagine we have the following array:

String[] colorNames = { "red", "green", "blue", "yellow" };

Given this array, we would like to create a Stream<String> that generates, say, 30 strings randomly taken from the array, e.g.: blue red yellow red green green yellow red blue green ……

Unfortunately, this feature is not directly provided by the Java SE (Standard Edition) framework. I have tried looking for this functionality in some of the well known “utility” libraries like the Apache Commons Lang and Google Guava but I didn’t find anything useful.

However, don’t worry! This type of generation is very easy to implement. We have an array, so we perfectly know the length and also the last index. We can simply generate a stream of random indexes with values ranging from 0 inclusive to array.length exclusive (sounds familiar?) and then “map” each index to the object at that index. Simple, isn’t it?

Note that in my example above I have used an array of String objects but I could have used any other type of array. For istance, I could have used an array of java.awt.Color, java.math.BigInteger, java.time.Year, an enum type and so on. This is a good clue to take advantage of another great feature of Java: generics and generic methods.

The following example is one possible solution.

import java.util.Random;
import java.util.stream.Stream;

public class RandomObjectStreamDemo {
    public static void main(String[] args) {
        String[] colorNames = { "red", "green", "blue", "yellow" };

        Random rnd = new Random();
        Stream<String> stream = randomObjects(rnd, 30, colorNames);
        stream.forEach(s -> System.out.print(s + "  "));
    }

    public static <T> Stream<T> randomObjects(Random rnd, long streamSize, T[] objects) {
        return rnd.ints(streamSize, 0, objects.length).mapToObj(i -> objects[i]);
    }
}

This is an example of the output produced:

red  blue  blue  blue  green  green  yellow  red  yellow  yellow  blue  green  yellow  blue  yellow  green  green  red  red  yellow  green  red  red  red  blue  green  red  blue  yellow  yellow  

If you notice, randomObjects is a “generic” method (in the sense of Java generics) because it declares the type variable T. Thanks to generics, if you pass a String[] to randomObjects you get a Stream<String>, if you pass a Color[] you get a Stream<Color> and so on.

The most relevant part in the code is the mapToObj(i -> objects[i]). It “maps” (=transforms) the index i to the object at that index. And that’s really all!

Conclusions

In this article we have seen how to create a stream of random numbers, which is very easy, and also a stream of random objects, which is just as simple.

If you have understood the general concept, you can also apply as many variations as you like. For example, you can also generate a stream of random objects from a list (I mean java.util.List, and preferably the ArrayList implementation). The concept is exactly the same, you just use list.size() instead of objects.length and then list.get(i) instead of objects[i].

Furthermore, if you want to create something more reusable, you can also think to encapsulate this functionality in an appropriate class that may be used, for example, in the following way:

StreamRandomizer streamRnd = new StreamRandomizer();
Stream<String> stream = streamRnd.randomObjects(30, colorNames);

The creation of such StreamRandomizer class is left to the reader as an “exercise” (if you have doubts, feel free to contact me).

Similar Posts