The static Stream.generate(Supplier<T>) method returns Stream<T> object that emits an infinite number of values of type T, each produced by the implementation of the Supplier<T>interface that was passed into the method as a parameter.
The Supplier<T> is a functional interface, which means it has only one abstract method (a method that has to be implemented). In the case of the Supplier<T> the only abstract method is T get().
The simplest T get() method implementation is the one that always returns the same value. For example, in the case when T is String, we can implement it as follows:
String get(){
return "x";
}
To pass such implementation into a method, we would need, first, to create a class:
class SupplierImpl implements Supplier<String>{
public String get(){
return "x";
}
}
Then we would need to create an object of this class and pass it into the method:
Stream<String> streamX = Stream.generate(new SupplierImpl());
streamX.limit(5).forEach(System.out::print); //prints: xxxxx
Please, notice how we have limited the number of generated values to 5, thus avoiding the need to process an infinite stream. The method (operator) Stream<T> limit(long) is an intermediate one (because it returns Stream<T>, as we discussed in the introduction).
Yet lambda expressions allow us to avoid creating a class.
A functional interface can be implemented in-line using a lambda expression. We just provide the method implementation, while the compiler “knows” which method it is because it is the only one not implemented (abstract):
Stream<String> streamX2 = Stream.generate(() -> "x");
streamX2.limit(5).forEach(System.out::print); //prints: xxxxx
As you may have noticed, we have used lambda expressions already in the previous posts about streams. The operation forEach(Consumer<T>) takes the Consumer<T> implementation as a parameter, which is a functional interface too. Its only abstract method is void accepts(T). We have implemented it as a lambda expression s -> System.out.print(s) and used its abbreviated version (called method reference) System.out::print.
The compiler also “knows” the type of the value that is passed into the method (operator) forEach() from the previous operator. In our case, it is String. When we type System.out, we identify the class we are going to use (PrintStream in this case). Then we put two columns “::” to indicate a method reference and type print as a method name. In the PrintStream class, the print() method is overloaded for different types, one of them is String. The compiler was able to figure out which method to use unambiguously and did not complain.
When I first saw such a notation, I felt it was overcomplicated and difficult to read. But after using it for some time, my perception has changed and now I feel very comfortable and use lambda expression and method reference whenever I can. It is compact and allows you to write only implementation-specific code without any plumbing overhead. Besides, all modern Java editors provide contextual support and can help to convert the object of a functional interface to a lambda expression and even to its method reference format, if such a conversion is possible.
The following is another Supplier<T> implementation that uses a lambda expression:
Stream<Integer> stream = Stream.generate(new Random()::nextInt);
stream.limit(2)
.map(i -> i.toString() + " ")
.forEach(System.out::print); //prints: -1097315144 1639784147
It uses a random number generator to produce stream values. So, every time you run this example, the output will be different. By the way, as you have probably guessed already, we could write the same code as follows:
Stream.generate(new Random()::nextInt)
.limit(2)
.map(i -> i.toString() + " ")
.forEach(System.out::print); //prints: -288126400 -872136567
I like this style even better as being more compact. Well, those who read my code have to feel comfortable with lambda expression too. That is one of the motivations I am writing these posts.
And, before concluding the generate() method discussion, here is another possible Supplier<T>implementation:
class SupplierImpl2 implements Supplier<String>{
private static int count = 1;
public String get(){
return String.valueOf(count++);
}
}
As you can see, we created a class in this case because we wanted to maintain the state in it (the property count in this example). We can use this implementation as follows:
Stream.generate(new SupplierImpl2()::get)
.limit(5)
.forEach(System.out::print); //prints: 12345
However, there is no need to implement the Supplier<T> interface explicitly. Look at this class, for example:
class DemoClass {
private static int count = 1;
public String calculateString(){
return String.valueOf(count++);
}
}
It does not have any reference to the Supplier<T> interface and resembles it only in the sense that it has a method that takes no parameters and returns a value. Yet, we can use it as the Supplier<T> interface implementation:
Stream.generate(new DemoClass()::calculateString)
.limit(5)
.forEach(System.out::print); //prints: 12345
It tells us that, behind the scene, the compiler implements the Supplier<T> interface and uses the provided code as the implementation body. I hope that at this point you experience the same Wow! moment I had when I first realized the power of such an approach. Now, if you see a method that accepts a functional interface (an interface with one abstract method), you can pass to it a method of any class that has the same signature and return value. If that is not a power engine added to Java, I don’t know what power engine is!
If the method you would like to call is static, as in the following class, you can use it too:
class DemoClass2 {
private static int count = 1;
public static String calculateString(){
return String.valueOf(count++);
}
}
You just slightly change the way you refer to it:
Stream.generate(DemoClass2::calculateString)
.limit(5)
.forEach(System.out::print); //prints: 12345
There is no need to create an object of a class if the method you call is static, so we provide the class name only.
As you have probably realized by now, such an approach can be used to refer to any method of any class, not necessarily because of maintaining a state.
But let us get back to the need of maintaining a state. Alternatively, we could maintain state in the same class where the code belongs:
import java.util.stream.Stream;
public class S07_CreateUsingGenerate2 {
private static int count = 1;
private static String calculateString(){
return String.valueOf(count++);
}
public static void main(String... args){
Stream.generate(() -> calculateString())
.limit(5)
.forEach(System.out::print); //prints: 12345
}
}
If the method you would like to call is not static, you refer to it using the class instance:
import java.util.stream.Stream;
public class S07_CreateUsingGenerate3 {
private int count = 1;
public String calcString(){
return String.valueOf(count++);
}
public static void main(String... args){
Stream.generate(new S07_CreateUsingGenerate3()::calcString)
.limit(5)
.forEach(System.out::print); //prints: 12345
}
}
And if you refer to it from a non-static context, you can use the keyword this as well:
import java.util.stream.Stream;
public class S07_CreateUsingGenerate4 {
private int count = 1;
public String calculateString(){
return String.valueOf(count++);
}
public static void main(String... args){
new S07_CreateUsingGenerate4().demo();
}
public void demo(){
Stream.generate(this::calculateString)
.limit(5)
.forEach(System.out::print); //prints: 12345
}
}
One important point to remember about maintaining a state: in the case of multithreading, it requires careful design and implementation. We will talk about it in details during the discussion of parallel streams. It is easy to do, but one has to be aware of possible issues and the ways to avoid them.
And, finally, we would like just to mention that there are corresponding generate() methods that create numeric streams
— IntStream.generate(IntSupplier s)
— LongStream.generate(LongSupplier s)
— DoubleStream.generate(DoubleSupplier s)
In the next post, we will talk about creating a stream using method Stream.iterate().
See other posts on Java 8 streams and posts on other topics.
You can also use navigation pages for Java stream related blogs:
— Java 8 streams blog titles
— Create stream
— Stream operations
— Stream operation collect()
The source code of all the code examples is here in GitHub.
Send your comments using the link Contact or in response to my newsletter.
If you do not receive the newsletter, subscribe via link Subscribe under Contact.