top of page
hand-businesswoman-touching-hand-artificial-intelligence-meaning-technology-connection-go-

Come explore Java Stream API with me

Java Streams


Java Streams are a sequence of elements that represent a pipeline through which data flows. They allow you to perform different operations on data elements, such as filtering, mapping, and reducing, in a concise manner.

You can create a stream from various data sources, such as collections, arrays, or even I/O channels. Using streams, we can perform various aggregate operations on the data returned from the collections classes by drastically reducing the complexity of code.


For example, to create a stream from a list of integers:


List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

Stream<Integer> numberStream = numbers.stream();


Once you have a stream, you can chain multiple operations together using methods provided by the Stream API. Common stream operations include:

  • filter(Predicate<T> predicate): Filters elements based on a given predicate.

  • map(Function<T, R> mapper): Transforms elements using a given function.

  • reduce(BinaryOperator<T> accumulator): Performs a reduction operation on the elements of the stream.

  • forEach(Consumer<T> action): Performs an action on each element of the stream.

Lambda expressions


Lambda expressions are also called anonymous functions in Java. They do not need a name and can be implemented right in the body of a method. Lambda expressions introduce the new arrow operator -> into Java. It divides the lambda expression into 2 parts, the left side specifies the parameters required by the expression, which could also be empty if no parameters are required. The right side is the lambda body which specifies the actions of the lambda expression.


A lambda expression has the following syntax:

(parameter1, parameter2, ...) -> expression

Here's a simple example of a lambda expression that adds two numbers:

(int a, int b) -> a + b


Lambda expressions are often used in conjunction with functional interfaces. A functional interface is an interface that has exactly one abstract method. For example, the Runnable interface has a single abstract method run(), which can be implemented using a lambda expression:

Runnable runnable = () -> {

System.out.println("Hello, World!"); }

Lambda expressions are commonly used in Java Streams to define custom operations on data elements.


Lambda expressions and Java Streams make it easier to write clean, functional-style code for data processing tasks, leading to more readable and maintainable code in Java applications.


Different Operations On Streams

There are two types of Operations in Streams:

  1. Intermediate Operations

  2. Terminal Operations

Working of stream can be explained in 3 steps:

  1. Create a stream

  2. Perform intermediate operations on the initial stream to transform it into another stream for further operations. Some important intermediate operations are map(),filter() and sorted().

  3. Perform terminal operation on the final stream to get the result. (Final stream is output after filtering). Some important terminal operations are collect(), forEach() and reduce().

Let us consider a simple java program to count number of names starting with alphabet D in the given list.


@Test

public void regular(){

ArrayList<String> names = new ArrayList<String>():

names.add("David");

names.add("Robert");

names.add("Dylan");

names.add("Caroline");

int count=0;

for (int i=0;i<names.size();i++){

String actual =names.get(i);

if(actual.startsWith("D"){

count++;

}

}

System.out.println(count);

}


Expected Output: 2


Now, let us try to solve the same program using the stream API.


//method 1

@Test

public void streamFilter(){

ArrayList<String> names = new ArrayList<String>():

names.add("David");

names.add("Robert");

names.add("Dylan");

names.add("Caroline");

Long c=names.stream().filter(s->s.startsWith("D")).count();

System.out.println(c);

}


Expected Output: 2


//method 2

Another simpler way of declaring streams collection.

Stream.of("David","Robert","Dylan","Caroline").filter(s->s.startsWith("D")).count();


Here, we convert the existing Arraylist names to stream using .stream method. Then use the filter method which takes the lambda expression, left side is the variable s to be declared and right side is the expression. Filter method scans the whole collection in parallel unlike the traditional for loop method where it is done sequentially.

The execution time is less for streams as well as reduces complexity of code. Also, please note that if you do not perform terminal operation count() on the filtered stream, there will not be any output on the intermediate operation. It is called Lazy Evaluation.


Now that we saw a simple program using Java streams, we are ready to explore more operations.


Example 1 - forEach Method

Assume you have a scenario where you have to print all the elements of an ArrayList whose length is greater than 4. You can achieve it using streams in a single line of code.

names.stream().filter(s ->s.length() > 4).forEach(s->System.out.println(s));


Let us breakdown what we are doing here. From the main stream names we are creating a filtered stream which has length greater than 4 and forEach will scan the filtered stream entirely and print it.


Example 2 - map Method

We can also modify the stream filter results using map function.

Here we are going to print names which have last letter as "e" in Uppercase.


public void streamMap(){

Stream.of("David","Robert","Dylan","Caroline").filter(s->s.endsWith("e")).map(s->s.toUpperCase()).

forEach(s->System.out.println(s));

}


Expected Output:

CAROLINE


Here we are manipulating the streams output by converting the names ending with "e" to Upper case by using map.


Example 3 - sorted Method

We can convert Arrays to list using asList() method.

List<String> names = Arrays.asList("David","Robert","Dylan","Caroline");

names.stream().filter(s->s.startsWith("D").sorted(). map(s->s.toUpperCase()).forEach(s->System.out.println(s));


Expected Output:

DAVID

DYLAN


Streams are so powerful that all the operations like sorting, mapping and printing can all be done in a single line of code. We can avoid using for loops and if loops and reduce execution time by using streams.


Example 4 - concat Method

We can merge 2 streams into one and sort them using concat() method.

Assume names1 and names2 are two different streams with list of student names.


Stream<String> newStream = Stream.concat(names1.stream(), names2.stream());

newStream.sorted().forEach(s->System.out.println(s));


Expected Output:

New merged stream with sorted names


Example 5 - anyMatch Method

We can also check if there is any match in the stream with the given input.

boolean flag=newStream.anyMatch(s->s.equalsIgnoreCase("David"));

System.out.println(flag);

Assert.assertTrue(flag);


Expected Output:

True


The flag variable will be true if a matching name is found in the stream. Otherwise, it will return false.


Example 6 - collect Method

Using collect method, we can convert streams back to Lists.


public void streamCollect(){

List<String> ls=Stream.of("David","Robert","Dylan","Caroline","Rose").filter(s->s.endsWith("e")).

map(s->s.toUpperCase()).collect(Collectors.toList());

ls.get(0);

}


Expected Output: CAROLINE


The list ls will have the all the values from the stream that ends with e. In this snippet, we are getting the first index of the list so it will only return the value in the first position.


Java Stream Features

The features of Java stream are mentioned below:

  • A stream cannot be reused once it is consumed. You have to create a new stream for another operation.

  • Streams don’t alter the original data structure, they only provide the result as per the pipelined methods.

  • Each intermediate operation is lazily executed and returns a stream as a result, hence various intermediate operations can be pipelined. Terminal operations mark the end of the stream and return the result.

Now that have got some idea about how streams work, feel free to explore more intermediate and terminal operations that you can perform on your data elements.


Hope you find this blog useful.


Happy Learning!

105 views0 comments

+1 (302) 200-8320

NumPy_Ninja_Logo (1).png

Numpy Ninja Inc. 8 The Grn Ste A Dover, DE 19901

© Copyright 2022 by NumPy Ninja

  • Twitter
  • LinkedIn
bottom of page