The Java Stream API is a powerful tool introduced in Java 8. It is designed to process data in a declarative way. More specifically, it makes it easy to process sequences of data elements, such as collections or arrays.
Here are some key points about Java Stream API:
- Non-mutating: Operations on streams do not mutate the source of the stream, rather they produce a new stream that encapsulates the result.
- Functional in nature: An important concept in Stream API is that it allows computations on data to be expressed as lambda functions.
- Lazy computation: The computation on the source data is only performed when it’s actually needed. This can result in significant performance boosts.
- Parallelizable operations: Stream operations can transparently take advantage of multicore architectures, leading to significantly increased performance.
Here’s a simple example of how the Stream API might be used:
import java.util.List;
import java.util.stream.Stream;
public class Stream1 {
public static void main(String[] args) {
List<String> collected = Stream.of("Java", "Kotlin", "Scala")
.filter(lang -> lang.startsWith("J"))
.map(String::toUpperCase)
.toList();
}
}
In this example, we create a stream from a list of strings, filter to keep only those that start with “J”, convert them to uppercase, and then collect them into a new list.
In Java, there are several ways to create Streams. Here are some common methods:
- From Collection or Arrays: All collections in Java which extends Collection interface can be converted to Stream.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Stream2 {
public static void main(String[] args) {
List<String> myList = new ArrayList<>();
Stream<String> myStream = myList.stream();
String[] myArray = new String[]{"a", "b", "c"};
Stream<String> myArrayStream = Arrays.stream(myArray);
}
}
- Using
Stream.of()
: You can create a Stream from specific set of object references with Stream.of()
.
import java.util.stream.Stream;
public class Stream3 {
public static void main(String[] args) {
Stream<String> streamOfString = Stream.of("a", "b", "c");
}
}
- From File: In the
java.nio.file
package, you can use Files.lines()
, to read a file into a Stream of lines.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class Stream4 {
public static void main(String[] args) {
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
- Stream.iterate() and Stream.generate(): These methods let you generate streams in a programmatic way.
import java.util.stream.Stream;
public class Stream5 {
public static void main(String[] args) {
Stream<String> stringStream = Stream.generate(() -> "element").limit(10);
Stream<Integer> integerStream = Stream.iterate(0, n -> n + 1).limit(10);
}
}
In the above example, Stream.generate()
creates a Stream of specified lambda function (always “element” in this case) which can be limited using limit()
. Stream.iterate()
creates a Stream based on the initial element and a lambda function for subsequent elements.
- Using Stream.builder(): You can create streams using
Stream.builder()
where you can add elements in a Stream in a programmatic way.
import java.util.stream.Stream;
public class Stream6 {
public static void main(String[] args) {
Stream.Builder<String> myStreamBuilder = Stream.<String>builder().add("a").add("b").add("c");
Stream<String> stringStream = myStreamBuilder.build();
}
}
Remember, once a Stream is consumed, it can’t be reused. You have to create a new stream to perform any new computation.