Java8::Steeam ::
Stream :: A stream is a sequence of data elements supporting sequential and parallel aggregate operations.To perform a computation, stream operations are composed into a stream pipeline. A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as
filter(Predicate)
), and a terminal operation (which produces a result or side-effect, such as count()
or forEach(Consumer)
). how do streams differ from collections?
Both are abstractions for a collection of data elements. Collections focus on storage of data elements
for efficient access whereas streams focus on aggregate computations on data elements from a data source that is typically, but not necessarily, collections.
features of streams, comparing them with collections when necessary:
Streams have no storage : A collection is an in-memory data structure that stores all its elements. All elements must exist in memory before they are added to the collection. A stream has no storage; it does not store elements. A stream pulls elements from a data source on-demand and passes them to a pipeline of operations for processing.
Streams can represent a sequence of infinite elements : A collection cannot represent a group of infinite elements whereas a stream can. A collection stores all its elements in memory, and therefore, it is not possible to have an infinite number of elements in a collection. Having a collection of an infinite number of elements will require an infinite amount of memory and the storage process will continue forever. A stream pulls its elements from a data source that can be a collection, a function that generates data, an I/O channel, etc.
The design of streams is based on internal iteration.
Streams are designed to be processed in parallel with no additional work from the developers.
Streams are designed to support functional programming.
Streams support lazy operations.
Streams can be ordered or unordered.
Streams cannot be reused.
How Streams Work Internally
Streams process data in a pipeline consisting of:
Source: Collection, arrays, or any sequence.
Intermediate Operations: Transformations like
filter()
andmap()
(Lazy execution).Terminal Operation: Triggers execution, e.g.,
forEach()
,reduce()
.
Streams do not store data and process elements one at a time, improving memory efficiency. They often use Spliterators and internal iteration mechanisms, which differ from external loops.
Creating Streams:
There are many ways to create streams. Many existing classes in the Java libraries have received new methods that return a stream. Based on the data source, stream creation can be categorized as follows:
Streams from values: The Stream interface contains the following two static of() methods to create a sequential Stream from a single value and multiple values:
• <T> Stream<T> of(T t)
• <T> Stream<T> of(T...values)
public class Stram {
public static void main(String[] args) {
Stream<String> singleValue=Stream.of("Strmewithsinglevalue");
Stream<String> MutpleValues=Stream.of("x","y","zz","aa");
Stream<Integer> MultPleIntValues=Stream.of(2,4,6,8,10);
}
}
The following snippet of code creates a stream of strings from a String array returned from the split() method of the String class:
String str = "Ken,Jeff,Chris,Ellen";
// The stream will contain fur elements: "Ken", "Jeff", "Chris", and "Ellen"
Stream<String> stream = Stream.of(str.split(","));
Empty Streams::
An empty stream is a stream with no elements. The Stream interface contains an empty() static method to create an empty sequential stream.
// Creates an empty stream of strings
Stream<String> stream = Stream.empty();
The IntStream, LongStream, and DoubleStream interfaces also contain an empty() static method to create an empty stream of primitive types.
// Creates an empty stream of integers
IntStream numbers = IntStream.empty();
The Stream interface also supports creating a stream using the builder pattern using the Stream.Builder<T> interface whose instance represents a stream builder. The builder() static method of the Stream interface returns a stream builder.
// Gets a stream builder
Stream.Builder<String> builder = Stream.builder();
The Stream.Builder<T> interface contains the following methods:
• void accept(T t)
• Stream.Builder<T> add(T t)
• Stream<T> build()
Stream<String> stream = Stream.<String>builder()
.add("Ken")
.add("Jeff")
.add("Chris")
.add("Ellen")
.build();
(or )
// Obtain a builder
Stream.Builder<String> builder = Stream.builder();
// Add elements and build the stream
Stream<String> stream = builder.add("Ken")
.add("Jeff")
.add("Chris")
.add("Ellen")
.build();
Streams from Functions :: An infinite stream is a stream with a data source capable of generating infinite number of elements. aying that the data source should be “capable of generating” infinite number of elements, rather the data source should have or contain an infinite number of elements. It is impossible to generate and store an infinite number of elements of any kind because of memory and time constraints. However, it is possible to have a function that can generate infinite number of values on demand.
The Stream interface contains the following two static methods to generate an infinite stream:
• <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
• <T> Stream<T> generate(Supplier<T> s)
The iterator() method creates a sequential ordered stream whereas the generate() method creates a
sequential unordered stream.
Using the Stream.iterate() Method::
The iterator() method takes two arguments: a seed and a function. The first argument is a seed that is the first element of the stream. The second element is generated by applying the function to the first element. The third element is generated by applying the function on the second element and so on. Its elements are seed, f(seed), f(f(seed)), f(f(f(seed))), and so on.
// Creates a stream of natural numbers
Stream<Long> naturalNumbers = Stream.iterate(1L, n -> n + 1);
// Creates a stream of odd natural numbers
Stream<Long> oddNaturalNumbers = Stream.iterate(1L, n -> n + 2);
Stream<Integer> streamIterated = Stream.iterate(40, n -> n + 2).limit(20);
streamIterated.forEach(System.out::println);
Streams from Arrays::
The Arrays class in the java.util package contains an overloaded stream() static method to create sequential streams from arrays. You can use it to create an IntStream from an int array, a LongStream from a long array, a DoubleStream from a double array, and a Stream<T> from an array of the reference type T. The following snippet of code creates an IntStream and a Stream<String> from an int array and a String array:
// Creates a stream from an int array with elements 1, 2, and 3
IntStream numbers = Arrays.stream(new int[]{1, 2, 3});
// Creates a stream from a String array with elements "Ken", and "Jeff"
Stream<String> names = Arrays.stream(new String[] {"Ken", "Jeff"});
Streams from Collections ::
The Collection interface contains the stream() and parallelStream() methods that create sequential and parallel streams from a Collection, respectively.
import java.util.ArrayList;
import java.util.List;
public class LstStrm {
public static void main(String args[]) {
List<String> names=new ArrayList<String>();
names.add("one");
names.add("two");
names.add("three");
names.add("four");
names.add("five");
System.out.println(" display list of elements in java6 ::"+names);
names.stream().forEach(x->System.out.println("displaying list elements in java8" +x));
}
}
Stream Operations ::
A stream supports two types of operations:
• Intermediate operations
• Terminal operations
Intermediate operations are also known as lazy operations. Terminal operations are also known as eager operations. Operations are known as lazy and eager based on the way they pull the data elements from the data source. A lazy operation on a stream does not process the elements of the stream until another eager operation is called on the stream.
Streams connect though a chain of operations forming a stream pipeline. A stream is inherently lazy until you call a terminal operation on it. An intermediate operation on a stream produces another stream. When you call a terminal operation on a stream, the elements are pulled from the data source and pass through the stream pipeline.
Each intermediate operation takes elements from an input stream and transforms the elements to produce an output stream. The terminal operation takes inputs from a stream and produces the result.
There are multiple ways to create a stream in Java 8. Here are some common approaches:
1 . From a Collection (
List
, Set
, etc.)import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamFromCollection {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> nameStream = names.stream(); // Create stream from List
nameStream.forEach(System.out::println);
}
}
2. Using
Stream.of()
import java.util.stream.Stream;
public class StreamOfExample { public static void main(String[] args) { Stream<String> stream = Stream.of("Apple", "Banana", "Cherry");
stream.forEach(System.out::println); }}
3 . Using Array
import java.util.Arrays;import java.util.stream.Stream;
public class StreamFromArray { public static void main(String[] args) { String[] fruits = {"Mango", "Orange", "Grapes"};
Stream<String> fruitStream = Arrays.stream(fruits); fruitStream.forEach(System.out::println); }}
4 . Using IntStream
, LongStream
, DoubleStream
(Primitive Streams)
import java.util.stream.IntStream;
public class PrimitiveStreamExample { public static void main(String[] args) { IntStream.range(1, 6).forEach(System.out::println); }}