The Stream interface factory methods are the methods that create a Stream object. Their implementation includes a call to a class constructor and that is the advantage of factory methods: they can do much more than a constructor. They can return any type as needed, not just the instance of the constructed class. They can control the number of instances, use a pool, re-use the instances, and similar. They can have different names even as they produce instances of the same type but constructed in a particular manner or with different parameters/values.
The following are factory methods of the Stream interface:
static Stream<T> generate(Supplier<T> s);
static Stream<T> iterate(T seed, UnaryOperator<T> f);
static Stream<T> iterate(T seed, Predicate<T> hasNext,
UnaryOperator<T> next);
static Stream.Builder<T> builder();
static Stream<T> of(T... values);
static Stream<T> empty();
static Stream<T> ofNullable(T t);
static Stream<T> concat(Stream<T> a, Stream<T> b);
The first five we have discussed already in this series:
static Stream<T> generate(Supplier<T> s);
static Stream<T> iterate(T seed, UnaryOperator<T> f);
static Stream<T> iterate(T seed, Predicate<T> hasNext, UnaryOperator<T> next);
static Stream.Builder<T> builder();
static Stream<T> of(T… values);
The other three – empty(), ofNullable(), and concat() – we will discuss now.
Stream.empty()
The empty() method creates an empty Stream object that does not emit any element:
Stream.empty().forEach(System.out::print); //prints nothing
In the above example, the forEach() method acts similarly to the Collection method forEach() and applies the passed-in function to each of the stream element. It is exactly what a regular for statement does when it applies a block of code (that would be the function with one input parameter—the element produced by the for statement. For example, here how the elements of a List can be processed:
List<String> list = List.of("1","2");
for(String e:list){
System.out.print(e); //prints: 12
}
An empty List object behaves exactly like an empty Stream object:
List<String> list = new ArrayList<>();
for(String e:list){
System.out.print(e); //prints nothing
}
Here is another example of an empty stream:
new ArrayList().stream()
.forEach(System.out::print); //prints nothing
Stream.ofNullable(T t)
The ofNullable(T t) method returns a Stream<T> object emitting a single element of type T if the passed-in parameter t is not null; otherwise, it returns an empty Stream object. To demonstrate the usage of the ofNullable(T t) method, we have created the following method:
void printList1(List<String> list){
list.stream().forEach(System.out::print);
}
Let’s execute this method three times—with the parameter list equals to null and to a List object, empty and not. Here are the results:
//printList1(null); //NullPointerException
printList1(new ArrayList()); //prints nothing
List<String> list = List.of("1", "2");
printList1(list); //prints: 12
Notice how the first call to the printList1() method generates NullPointerException. To avoid the exception, we could implement the method as follows:
void printList2(List<String> list){
(list == null ? Stream.empty() : list.stream())
.forEach(System.out::print);
}
The same functionality can be achieved using the ofNullable(T t) method like this:
void printList3(List<String> list){
Stream.ofNullable(list)
.flatMap(l -> l.stream())
.forEach(System.out::print);
}
Notice how we have added flatMap() because, otherwise, the Stream element that flows into the forEach() method would be a List object. We will talk more about the flatMap() method in the next article Stream intermediate operations. The function passed into the flatMap() operation in the preceding code can be expressed as a method reference too:
void printList4(List<String> list){
Stream.ofNullable(list).flatMap(Collection::stream)
.forEach(System.out::print);
}
Stream.concat(Stream a, Stream b)
The Stream<T> concat(Stream<T> a, Stream<T> b) method of the
Stream interface creates a stream of values based on two streams, a and b, passed in as parameters. The newly created stream consists of all the elements of the first parameter a, followed by all the elements of the second parameter b. The following code demonstrates this method:
Stream<Integer> stream1 = List.of(1, 2).stream();
Stream<Integer> stream2 = List.of(2, 3).stream();
Stream.concat(stream1, stream2)
.forEach(System.out::print); //prints: 1223
Notice that element 2 is emitted by both original streams and consequently is emitted twice by the resulting stream.
In the next post, we will talk about some of the intermediate operations – sorting, skipping, and filtering.
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.