May 30, 2024

Java Stream sorted() With Examples

In this tutorial we’ll see how to use Java Stream sorted() method with the help of few examples.

Syntax of sorted() method in Java Stream API

There are two variants of sorted() method.

  1. Stream<T> sorted()- Used to sort the elements of the stream according to natural order. If the elements of this stream are not Comparable, a java.lang.ClassCastException may be thrown when the terminal operation is executed.
  2. Stream<T> sorted(Comparator<? super T> comparator)- Used to sort the elements of the stream according to the provided Comparator.

sorted() in Java Stream is a stateful intermediate operation which means it may incorporate state from previously seen elements when processing new elements.

Let us try to understand sorted() method better with the help of some Java examples.

Sort List of integers in natural order

import java.util.List;
import java.util.stream.Collectors;

public class SortedDemo {

  public static void main(String[] args) {
    // Till Java 8
    //List<Integer> myList = Arrays.asList(11, 1, 9, 1, 4, 11, 17);
    // From Java 9
    List<Integer> myList = List.of(11, 1, 9, 1, 4, 11, 17);
    List<Integer> sortedList = myList.stream().sorted().collect(Collectors.toList());
    System.out.println(sortedList);
  }
}
Output
[1, 1, 4, 9, 11, 11, 17]

Since Integer class implements Comparable (compareTo() method) so that becomes the natural ordering for the element while sorting them using sorted() method.

Sort List of integers in reverse order

Sort List of integers in reverse order using sorted() method where Comparator is passed.

public class SortedDemo {

  public static void main(String[] args) {
    // Till Java 8
    //List<Integer> myList = Arrays.asList(11, 1, 9, 1, 4, 11, 17);
    // From Java 9
    List<Integer> myList = List.of(11, 1, 9, 1, 4, 11, 17);
    List<Integer> sortedList = myList.stream()
                     .sorted(Comparator.reverseOrder())
                     .collect(Collectors.toList());
    System.out.println(sortedList);
  }
}
Output
[17, 11, 11, 9, 4, 1, 1]

Sort List of Strings

Since String class in Java also implements Comparable interface so that becomes the natural ordering for the element while sorting them using sorted() method.

public class SortedDemo {

  public static void main(String[] args) {
    // Till Java 8
    //List<String> myList = Arrays.asList("Ram", "Madan", "Jack", "Ram", "Jack");
    // From Java 9
    List<String> myList = List.of("Ram", "Madan", "Jack", "Ram", "Jack");
    List<String> sortedList = myList.stream()
                    .sorted()
                    .collect(Collectors.toList());
    System.out.println(sortedList);
  }
}
Output
[Jack, Jack, Madan, Ram, Ram]

Sorting List of custom objects

With List containing Integers and Strings sorting doesn’t require any extra effort as both of these classes already implement Comparable defining the natural ordering. With custom objects you do need to implement Comparable and provide natural ordering.

Student class used in the example is as given below-

public class Student implements Comparable<Student> {
  private int rollNo;
  private String name;
  private String stream;
  private int marks;
  Student(int rollNo, String name, String stream, int marks){
    this.rollNo = rollNo;
    this.name = name;
    this.stream = stream;
    this.marks = marks;
  }
  public int getRollNo() {
    return rollNo;
  }
  public void setRollNo(int rollNo) {
    this.rollNo = rollNo;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }

  public String getStream() {
    return stream;
  }
  public void setStream(String stream) {
    this.stream = stream;
  }
  public int getMarks() {
    return marks;
  }
  public void setMarks(int marks) {
    this.marks = marks;
  }
  @Override
  public String toString() {
    return "Roll Number: " +  getRollNo() 
        + " Name: " + getName() + " Marks: " + getMarks();
  }
  @Override
  public int compareTo(Student student) {
    return this.getName().compareTo(student.getName());
  }
}

As you can see compareTo() method implementation provides sorting on name so that is the natural ordering for the Student class objects.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SortedDemo {

  public static void main(String[] args) {
    List<Student> studentList = Arrays.asList(new Student(1, "Peter", "Science", 75),
              new Student(2, "Ram", "Science", 99),
              new Student(3, "Priscilla", "Art", 68),
              new Student(4, "Ajay", "Art", 67),
              new Student(5, "Dan", "Biology", 77));
    List<Student> sortedList = studentList.stream()
                                          .sorted()
                                          .collect(Collectors.toList());
    for(Student student: sortedList) {
      System.out.println(student);
    }  
  }
}
Output
Roll Number: 4 Name: Ajay Marks: 67
Roll Number: 5 Name: Dan Marks: 77
Roll Number: 1 Name: Peter Marks: 75
Roll Number: 3 Name: Priscilla Marks: 68
Roll Number: 2 Name: Ram Marks: 99

You can also provide your own Comparator implementation by using the sorted method that takes Comparator as an argument. For example suppose you want the List of students sorted by marks.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SortedDemo {

  public static void main(String[] args) {
    List<Student> studentList = Arrays.asList(new Student(1, "Peter", "Science", 75),
              new Student(2, "Ram", "Science", 99),
              new Student(3, "Priscilla", "Art", 68),
              new Student(4, "Ajay", "Art", 67),
              new Student(5, "Dan", "Biology", 77));
    List<Student> sortedList = studentList.stream()
                                          .sorted((s1, s2) -> s1.getMarks()-s2.getMarks())
                                          .collect(Collectors.toList());
    for(Student student: sortedList) {
      System.out.println(student);
    }  
  }
}
Output
Roll Number: 4 Name: Ajay Marks: 67
Roll Number: 3 Name: Priscilla Marks: 68
Roll Number: 1 Name: Peter Marks: 75
Roll Number: 5 Name: Dan Marks: 77
Roll Number: 2 Name: Ram Marks: 99

Sorting a Set using Java Stream sorted() method

Sorting a Set is similar to sorting a List. You can create a Stream using the Set and call the sorted() method.

import java.util.HashSet;
import java.util.Set;

public class SortedDemo {
  public static void main(String[] args) {
      Set<String> nameSet = new HashSet<>();
      nameSet.add("Ram");
      nameSet.add("Peter");
      nameSet.add("Ajay");
      nameSet.add("Priscilla");
      nameSet.add("Dan");
      
      nameSet.stream()
             .sorted()
             .forEach(System.out::println);
  }
}
Output
Ajay
Dan
Peter
Priscilla
Ram

Sorting a Map using Java Stream sorted() method

You can sort a HashMap using sorted() method too. In the following example a HashMap is sorted on its values.

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class SortedDemo {

  public static void main(String[] args) {
    Map<Integer, String> nameMap = new HashMap<>();
      nameMap.put(1, "Ram");
      nameMap.put(2, "Peter");
      nameMap.put(3, "Ajay");
      nameMap.put(4, "Priscilla");
      nameMap.put(5, "Dan");
      
      System.out.println("-- Original Map --");
      for(Map.Entry<Integer, String> name : nameMap.entrySet()) {
        System.out.println("Key- " + name.getKey() + 
                        " Value- " + name.getValue());
      }
      
      Map<Integer, String> newMap = nameMap.entrySet()
                         .stream()
                         .sorted(Map.Entry.comparingByValue())
                         .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, 
                                  (k,v)->k, LinkedHashMap<Integer, String>::new));
      System.out.println("-- Sorted Map --");
      newMap.entrySet().forEach((e)->{System.out.println("Key- " + e.getKey() + " Value- " + e.getValue());});
  }
}
Output
-- Original Map --
Key- 1 Value- Ram
Key- 2 Value- Peter
Key- 3 Value- Ajay
Key- 4 Value- Priscilla
Key- 5 Value- Dan
-- Sorted Map --
Key- 3 Value- Ajay
Key- 5 Value- Dan
Key- 2 Value- Peter
Key- 4 Value- Priscilla
Key- 1 Value- Ram

To sort on key you can use Map.Entry.comparingByKey() method.

That's all for the topic Java Stream sorted() With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

May 29, 2024

Java Stream Sort on Multiple Fields

In this tutorial you’ll see how to sort stream of objects on multiple fields.

Sort stream of objects on multiple fields

To sort a stream of objects on multiple fields you need to use two methods-

1. Stream<T> sorted(Comparator<? super T> comparator)- sorts the elements of this stream according to the provided Comparator.

2. Since sorting is to be done on multiple fields for that you can compose several Comparators using thenComparing(Comparator<? super T> other) method.

Comparator.comparing(COMPARISON_LOGIC)
          .thenComparing(COMPARISON_LOGIC);

Java Stream sorting with multiple fields example

For the example we’ll use the object of User class which has two fields name and age.

public class User {
  private String name;
  private int age;
  User(String name, int age){
    this.name = name;
    this.age = age;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  @Override
  public String toString() {
    return getName() + " " + getAge() + " \n";
  } 
}

If we want to sort on name as well as on age in descending order then it can be done using sorted method of the Java Stream API as given below.

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class StreamSort {

  public static void main(String[] args) {
      List<User> userList = Arrays.asList(new User("Peter", 75),
              new User("Ram", 19),
              new User("Peter", 68),
              new User("Mahesh", 32),
              new User("Scott", 32));
      userList = userList.stream()
                         .sorted(Comparator.comparing(User::getName)
                                  .thenComparing(Comparator.comparingInt(User::getAge).reversed()))
                         .collect(Collectors.toList());
      System.out.println(userList);

  }
}
Output
[Mahesh 32 
, Peter 75 
, Peter 68 
, Ram 19 
, Scott 32 
]

To make it clearer here is the elongated version with pre-defined Comparators.

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class StreamSort {

  public static void main(String[] args) {
      List<User> userList = Arrays.asList(new User("Peter", 75),
              new User("Ram", 19),
              new User("Peter", 68),
              new User("Mahesh", 32),
              new User("Scott", 32));
      
      Comparator<User> compByName = Comparator.comparing(User::getName);
      Comparator<User> compByAge = Comparator.comparingInt(User::getAge).reversed();
      userList = userList.stream()
                         .sorted(compByName
                               .thenComparing(compByAge))
                         .collect(Collectors.toList());
      System.out.println(userList);
  }
}

That's all for the topic Java Stream Sort on Multiple Fields. If something is missing or you have something to share about the topic please write a comment.


You may also like

May 23, 2024

ExpressJS Middleware Functions

If you have started reading about ExpressJS you would have definitely come across a term called middleware in ExpressJS. You might have thought what is this new complexity in ExpressJS but fear not middleware is just a function with access to request and response objects and it has logic like any other function.

Middleware in ExpressJS

Middleware functions are the functions that take request object, response object and next function as parameters in the application's request-response cycle. The next() function, when invoked with in a middleware, moves the execution to the next middleware.

Using middleware functions you can do the following tasks-

  1. Write any logic which will be executed.
  2. Modify the request and response objects like parsing request body, setting response header.
  3. Call the next middleware function by invoking next() function.
  4. End the request-response cycle by calling response.send()

Let's try to understand middleware function using the following code-

const express = require('express');
const app = express();
const port = 3000;
app.get('/', function(req, res, next){
    console.log('In a middleware');
    next();
})
app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

In the above code app.get() is the HTTP method.

With in the app.get() method middleware function is the given code.

function(req, res, next){
    console.log('In a middleware');
    next();
}

If the current middleware function does not end the request-response cycle be sending a response back, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.

If you run the above example and access the URL http://localhost:3000/ you will get a message on the console 'In a middleware' but the browser won't show you any result as no response is sent back from the server.

Example with middleware functions

Here is another example with three middleware functions. Note the use of next() in the first two middleware functions so that control is passed from first middleware to second. From the third middleware function a response is sent back which also stops the request-response cycle.

const express = require('express');

const app = express();
const port = 3000;

// specific to root path
app.use('/', function(req, res, next){
    console.log('Logging a request for root path');
    next();
})

// middleware applicable to all requests (any path)
app.use(function(req, res, next){
    console.log('In middleware-2');
    next();
})

app.get('/', function(req, res, next){
    console.log('In middleware-3');
    res.send('<h3>Hello from ExpressJS</h3>');
})

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

On running this example and accessing the URL http://localhost:3000/ you'll see the following messages in the given order.

Logging a request for root path

In middleware-2

In middleware-3

Browser also displays the response sent from the server.

This chaining of middleware functions can be described using the following image.

ExpressJS Middleware Functions

In the above code you can remove the next() function from the first middleware.

app.use('/', function(req, res, next){
    console.log('Logging a request for root path');
    //next();
})

After restarting the server if you access the URL http://localhost:3000/ you won't get any response now as code execution is stuck at the first middleware.

Types of middleware

In an Express application you can use the following types of middlewares.

  1. Application-level middleware- You can bind application-level middleware to an app object (which you get by calling express() function) by using the app.use() and app.METHOD() functions, where METHOD is the HTTP method of the request that the middleware function handles (such as GET, PUT, or POST) in lowercase. We have already seen examples of application-level middleware.
  2. Router-level middleware- Router-level middleware works in the same way as application-level middleware, difference being that it is bound to an instance of express.Router(). You can bind the router-level middleware by using the router.use() and router.METHOD() functions. Read more about express.Router() in this post- express.Router() Function With Examples
  3. Error-handling middleware- You can define error-handling middleware functions too. In that case function takes four arguments instead of three, specifically with the signature (err, req, res, next).
  4. Built-in middleware- Express also has built-in middleware functions like-
    1. express.static- Serves static assets such as HTML files, images, and so on.
    2. express.json- Parses incoming requests with JSON payloads.
    3. express.urlencoded- Parses incoming requests with URL-encoded payloads.
  5. Third-party middleware- You can also use third-party middleware to add functionality to Express apps. For example body-parser which parses HTTP request body, morgan which is a HTTP request logger.

That's all for the topic ExpressJS Middleware Functions. If something is missing or you have something to share about the topic please write a comment.


You may also like

May 22, 2024

express.Router() Function With Examples

In the post ExpressJS Routing With Examples we have already seen how to set up basic routing in ExpressJS. In this post we'll see how to use express.Router() function to create modular route definitions.

The Router instance you get by calling express.Router() function is a complete middleware and routing system on its own. It helps you to create modular route definitions meaning you can create route definitions in separate files and export it. Later you can import the router module in the file where it is needed. Let's see it with the help of an example.

express.Router() example

In the code there is a GET method route which sends a form as response with method as 'POST'. This request should be called first.

Once you have the form and you click the submit button that triggers the post request.

This example uses body-parser package which is used to parse the request body as we want to extract the entered name from the request.

Create a separate routes folder to keep all the route definitions, with in this folder create a file named route.js and write the routes there.

route.js

const express = require('express');

const router = express.Router();

router.get('/hello', (req, res) => {
    res.send('<form action="/name" method="post"><input type="text" name="name"><button type="submit">Submit</button></form>');
})

router.post('/name', (req, res) => {
    console.log(req.body.name);
    const name = req.body.name;
    res.send('<h3>Your name is ' + name + '</h3>');
})

module.exports = router;

Important points about the code.

  1. express.Router() function is used to create a new router object.
  2. Once you’ve created a router object, you can add middleware and HTTP method routes (such as get, put, post, and so on) to it. In the code router.get() and router.post() are used.
  3. Router object is exported using module.exports() so that it can be used in other files.

With that a modular route definition is ready. You can use it in any other file.

Importing modular routes created using express.Router()

We'll import the route definitions in a file named app.js.

app.js

const express = require('express');
const appRoutes = require('./routes/route');
const bodyParser = require('body-parser'); 
const app = express();
const port = 3000;

app.use(bodyParser.urlencoded({extended: true}));
// routes
app.use(appRoutes);
//Server
app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

Important points about the code.

  1. Route definition file is imported by giving the relative path of the file.
    const appRoutes = require('./routes/route');
    
  2. Using app.use() method you can mount the specified middleware function so app.use() is used to mount the route definitions.
  3. With that your app will now be able to handle requests to /hello and /name that are specified in route module.

You can run the application using the following command

node app.js

Example app listening on port 3000

If you go to the URL http://localhost:3000/hello, server gets a GET request for the path ('/hello') and executes the callback function which sends the form as response.

Express routing

Enter a name and press submit which should send a POST request to the server. In the callback function name is extracted from the parsed request body and a response is sent to the user.

That's all for the topic express.Router() Function With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

May 21, 2024

ExpressJS Routing With Examples

With routing you can determine how your web application responds to a client request coming through a particular endpoint (path) and a specific HTTP request method (GET, POST, PUT and so on). In this post we'll see how to set up basic routing in ExpressJS.

Routing in Express

Using the object, conventionally named 'app', you get from the express() function you get access to the methods that corresponds to the HTTP methods.

Format for calling methods is as given below

app.METHOD(path, callback [, callback ...])

where METHOD is the HTTP method of the request, such as GET, PUT, POST, and so on, in lowercase.

  • app.get() to handle GET requests (fetching resource)
  • app.post() to handle POST requests (adding new resource)
  • app.put() to handle PUT requests (updating resource)
  • app.delete() to handle DELETE requests (deleting resource)

Thus the methods you'll actually call are app.get(), app.post(), app.put() and so on.

path- The path for which the routing function is invoked, Default is '/' (root path).

callback- A callback functions which will be executed when a matching path and request type is found. Routing methods can have more than one callback function as arguments.

GET method route example

Here is a simple GET method example using Express routing.

app.js

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
    res.send("<h3>Hello World from ExpressJS</h3>")
})

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

As you can see here app.get(path, callback) method is used that routes HTTP GET requests to the specified path with the specified callback functions.

Specified path in our code is root path ('/'), callback function receives two arguments request and response. The request object (req) represents the HTTP request and the response object (res) represents the HTTP response.

Using res.send(body) function a HTTP response is sent back from the server.

You can run the application using the following command-

node app.js

Example app listening on port 3000

If you go to the URL http://localhost:3000/, server gets a GET request for the root path (‘/’) and executes the callback function which sends the "<h3>Hello World from ExpressJS</h3>" as response.

If you change the path in get method to ‘hello’

app.get('/hello', (req, res) => {
    res.send("<h3>Hello World from ExpressJS</h3>")
})

Then you'll get back the response when you hit the URL- http://localhost:3000/hello

POST method route example in Express

Here is a POST method example using Express routing. In the code there is a GET method route which sends a form as response with method as 'POST'. This request should be called first.

Once you have the form and you click the submit button that triggers the post request.

This example uses body-parser package which is used to parse the request body as we want to extract the entered name from the request.

App.js

const express = require('express');
const bodyParser = require('body-parser'); 

const app = express();
const port = 3000;

app.use(bodyParser.urlencoded({extended: true}));

app.get('/hello', (req, res) => {
    res.send('<form action="/name" method="post"><input type="text" name="name"><button type="submit">Submit</button></form>');
})

app.post('/name', (req, res) => {
    console.log(req.body.name);
    const name = req.body.name;
    res.send('<h3>Your name is ' + name + '</h3>');
})

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

If you go to the URL http://localhost:3000/hello, server gets a GET request for the path ('/hello') and executes the callback function which sends the form as response.

Enter a name and press submit which should send a POST request to the server. In the callback function name is extracted from the parsed request body and a response is sent to the user.

Here is another example showing the most popular HTTP methods GET, POST, PUT, PATCH and DELETE in use.

const express = require('express');

const app = express();
const port = 3000;

app.get('/', (req, res) => {
    res.send('Processing GET request');
})

app.post('/', (req, res) => {
    res.send('Processing POST request');
})

app.put('/', (req, res) => {
    res.send('Processing PUT request');
})

app.delete('/', (req, res) => {
    res.send('Processing DELETE request');
})

app.patch('/', (req, res) => {
    res.send('Processing PATCH request');
})

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

You can test it using curl command from terminal.

C:\>curl -X GET http://localhost:3000
Processing GET request

C:\>curl -X POST http://localhost:3000
Processing POST request

C:\>curl -X PUT http://localhost:3000
Processing PUT request

C:\>curl -X DELETE http://localhost:3000
Processing DELETE request

C:\>curl -X PATCH http://localhost:3000
Processing PATCH request

Refer this post express.Router() Function With Examples to see how to create module route definitions using express.Router() function.

Multiple callbacks with a request

You can provide multiple callback functions to handle a request. These callback functions must be separated by comma and make sure you specify the next object to go to the next callback.

app.get('/', (req, res, next) => {
    console.log('In first callback');
    next();
},
(req, res) => {
    res.send('From second callback');
}
)

Using regular expression with paths

You can use regular expressions while constructing paths.

Regular expression A? means A once or not at all.

For example-

app.get('/ab?cd', (req, res) => {
  res.send('ab?cd')
})

This route path will match acd and abcd.

Regular expression A+ means A one or more times

app.get('/ab+cd', (req, res) => {
  res.send('ab+cd')
})

This route path will match abcd, abbcd, abbbcd, and so on.

Regular expression A* means any number of characters after A

app.get('/ab*cd', (req, res) => {
  res.send('ab*cd')
})

This route path will match abcd, abxcd, abANYcd, ab123cd, and so on.

Using app.all() method

app.all() method matches all HTTP verbs which means same method will be called for GET, POST, PUT and so on.

The app.all() method is useful if you want to process some logic for any type of request.

For example, consider the following route

app.all('*', requireAuthentication, fetchUser)

It requires that all routes irrespective of path and type of HTTP request require authentication, and automatically load a user.

That's all for the topic ExpressJS Routing With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

May 2, 2024

Java Stream skip() Method With Examples

The skip(long n) method in the Java Stream API skips the first n element of the stream and returns a new stream consisting of the remaining elements of this stream.

skip method in Java Stream

Syntax of the method is as given below.

Stream<T> skip(long n)

Here n is the number of leading elements to skip. If you pass n as negative then IllegalArgumentException is thrown.

Points about skip method

  1. It is a stateful intermediate operation which means it will return a new Stream.
  2. If number of elements to skip (n) is greater than the elements contained in the Stream then an empty stream will be returned.
  3. skip(n) is constrained to skip not just any n elements, but the first n elements in the encounter order.
  4. skip() is generally a cheap operation on sequential stream pipelines.
  5. skip() can be quite expensive on ordered parallel pipelines, if n is a fairly large values, because of the constraint to skip first n elements in the encounter order.

skip() Java Example

Here we’ll try to get a sublist from a List using skip method. Method getSubListBySkipping() is a generic method that can work with any type of List, second argument passed to the method is the number of elements to be skipped. Results of the stream returned by skip() method are collected to a list and that new list is returned.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SkipDemo {

  public static void main(String[] args) {
    SkipDemo sd = new SkipDemo();
    // Used with list of Strings
    List<String> cityList = Arrays.asList("Delhi", "Mumbai", "London", "New York","Bengaluru");
    List<String> newList = sd.getSubListBySkipping(cityList, 3);
    System.out.println("List after skipping elements- " + newList);
    // Used with list of Integers
    List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    List<Integer> tempList = sd.getSubListBySkipping(numList, 7);
    System.out.println("List after skipping elements- " + tempList);
  }

  // This method uses skip method to skip n elements
  public <T> List<T> getSubListBySkipping(List<T> originalList, long n){
    return originalList.stream().skip(n).collect(Collectors.toList());
  }
}
Output
List after skipping elements- [New York, Bengaluru]
List after skipping elements- [8, 9, 10]
That's all for the topic Java Stream skip() Method With Examples. If something is missing or you have something to share about the topic please write a comment.
You may also like

May 1, 2024

Java Stream API With Examples

The way Lambda expressions in Java brought functional programming to Java another addition in Java 8, Stream API in Java brought functional approach to processing collections of objects. Using Java Stream API you can create a stream over an object and then you just need to specify what needs to be done not how it has to be done.

For example if you want to count the elements in the stream, you need to specify the source for obtaining a stream and the function to count the elements. Stream API takes care of executing stream pipeline in an optimized way.

List<Integer> myList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  
long count = myList.stream().count();
System.out.println("Count of elements in the list- " + count);

In the above example List myList is the data source for the stream and count is the stream operation performed on the stream.

In this Java Stream API tutorial we’ll see how to create streams, types of streams and various stream operation examples. Since the examples use lambda expressions and functional interfaces so make sure that you are familiar with those topics.

Java Stream API – Interfaces and Classes

Java Stream API contains several interfaces and classes which are packaged with in the java.util.stream package. At the top of the hierarchy is interface BaseStream providing basic functionality for all the Streams.

BaseStream interface is extended by interfaces- DoubleStream, IntStream, LongStream and Stream. Stream interface is a generic interface which is used for all reference types.

DoubleStream, IntStream and LongStream are primitive specializations of Stream that can store primitive values.

Read more about Primitive Streams in this post- Primitive Type Streams in Java

One of the important class with in Java Stream API is Collectors class that is an implementation of Collector interface, this class implements various useful reduction operations.

Read more about Collectors class in this post- Collectors Class And collect() Method in Java

Creating a Stream

With in the Java Stream API there are number of ways to obtain a stream.

1. Stream from Collection- Stream can be created from any type of Collection via the stream() and parallelStream() methods.

List<Integer> myList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  
Stream<Integer> myStream = myList.stream();

2. Stream from an array- Stream can be obtained from an array via Arrays.stream(Object[]);

String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);

3. Using Stream.of() method- Stream can also be created using static factory method Stream.of(Object[])

Stream<String> stream = Stream.of("a", "b", "c");

4. Using range and iterate methods- Using range method you can get a primitive stream.

IntStream stream = IntStream.range(1, 10);

Same thing (getting stream of integers 1-9) can be achieved by using the iterate method-

Stream<Integer> stream = Stream.iterate(1, n-> n < 10, n->n+1);

5. Creating empty stream- You can create an empty stream using empty method.

Stream<Integer> stream = Stream.empty();

6. To get lines of a file as a stream you can use BufferedReader.lines() method.

Path path = Paths.get("D:\\KnpCode\\test.txt");
Stream<String> lines = Files.newBufferedReader(path).lines();

Types of Stream operations

Stream operations are divided into two types-

  • intermediate operations
  • terminal operations

Intermediate operations in Java Streams

Intermediate operations return a new stream. This new stream is a result of applying intermediate operation on the source stream. Intermediate operations are always lazy and these operations are executed only when a terminal operation is executed.

For example executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.

Intermediate operations are further divided into two categories-

  1. Stateless operations- Stateless operations, such as filter and map, retain no state from previously seen element when processing a new element, each element can be processed independently of operations on other elements.
  2. Stateful operations- Stateful operations, such as distinct and sorted, may incorporate state from previously seen elements when processing new elements.

Some of the examples of intermediate operations in Java Stream API are filter, map, flatMap, distinct, sorted, limit.

Examples of intermediate stream operations

1. filter()- In filter method Predicate is passed as an argument and method returns a stream consisting of the elements of this stream that match the given predicate. Predicate is a functional interface which can be implemented as a lambda expression.

From an ArrayList you want to filter elements less than 5.

List<Integer> myList = Arrays.asList(11, 1, 9, 4, 98, 0, 17, 8, 2, 3);  
Stream<Integer> myStream = myList.stream().filter(n -> n > 5);
myStream.forEach(System.out::println);
Output
11
9
98
17
8

2. map()- Using map method you can map (convert) each element in the stream to another object. Method returns a stream consisting of the results of applying the given function to the elements of this stream.

If you have a List of Strings and you want to apply upper case function to each string.

List<String> myList = Arrays.asList("rose", "lotus", "lily", "orchid");  
myList.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
Output
ROSE
LOTUS
LILY
ORCHID

3. flatMap()- The flatMap() operation applies a one-to-many transformation to the elements of the stream and also flattens the resulting structure into a new stream. If you have an object with many nested objects flatMap() operation brings all the nested level objects to the same level by flattening the structure.

If you want to produce a stream of the words contained in the Strings in the array.

Stream<String> lineStream = Arrays.stream(lines);
Stream<String> str = lineStream.flatMap(line -> Stream.of(line.split(" +")));

4. distinct()- Returns a stream consisting of the distinct elements of this stream.

distinct() method of the Java Stream API provides a best option to remove duplicates from a List.

List<Integer> ListWithDuplicates = Arrays.asList(7, 8, 9, 7, 4, 2, 4, 1);
List<Integer> uniqueElementList = ListWithDuplicates.stream().distinct().collect(Collectors.toList());
System.out.println("List after removing duplicates : " + uniqueElementList);
Output
List after removing duplicates : [7, 8, 9, 4, 2, 1]

5. sorted()- Returns a stream consisting of the elements of this stream, sorted according to natural order. This is a stateful intermediate operation.

List<Integer> myList = Arrays.asList(7, 8, 9, 7, 4, 2, 4, 1);
myList.stream().sorted().forEach(System.out::println);
Output
1
2
4
4
7
7
8
9

6. limit()- Using limit() method you can limit the number of elements in a Stream to the size passed with limit() method.

List<Integer> myList = Arrays.asList(7, 8, 9, 7, 4, 2, 4, 1);
myList.stream().limit(3).forEach(System.out::println);
Output
7
8
9

Terminal operations in Java Streams

Once the terminal operation is performed in a Stream, the stream pipeline is considered consumed. Same stream can no longer be used; if you need to traverse the same data source again, you must return to the data source to get a new stream.

Terminal operations are eager (except iterator() and spliterator() methods) , completing their traversal of the data source and processing of the pipeline before returning.

Some of the examples of terminal operations in Java Stream API are forEach, reduce, collect, min, max, count, findFirst.

Examples of Terminal stream operations

1. forEach()- In the forEach method a Consumer is passed as an argument and this Consumer action is performed on each element of this stream.

List<String> myList = Arrays.asList("rose", "lotus", "lily", "orchid");
myList.stream().forEach(System.out::println);
Output
rose
lotus
lily
orchid

2. reduce()- Reduce operation reduce the stream to a single non-stream value. This method returns an Optional describing the result of the reduction.

You can find max element in a Collection using reduce method.

List<Integer> myList = Arrays.asList(11, 1, 9, 4, 98, 0, 17, 8, 2, 3);
Optional<Integer> max = myList.stream().reduce((n1, n2) -> n1 > n2 ? n1:n2);
if(max.isPresent()){
  System.out.println("Max element in the List " + max.get());
}
Output
Max element in the List 98

3. collect()- Java Stream API’s collect() method is used to collect the Stream elements in a mutable container like a List, StringBuilder.

In the example list is filtered to filter out all the elements less than 5 and the stream elements are collected in a TreeSet which sorts the resulting elements.

List<Integer> myList = Arrays.asList(11, 1, 9, 4, 98, 0, 17, 8, 2, 3);
Set<Integer> sortedSet = myList.stream().filter(n->n>5).collect(Collectors.toCollection((TreeSet::new)));
System.out.println("Sorted set- " + sortedSet);
Output
Sorted set- [8, 9, 11, 17, 98]

4. min()- Returns the minimum element of this stream according to the provided Comparator. This method returns an Optional describing the minimum element of this stream.

List<Integer> myList = Arrays.asList(11, 1, 9, 4, 98, 0, 17, 8, 2, 3);
Optional<Integer> min = myList.stream().min(Integer::compare);
if(min.isPresent()){
  System.out.println("Minimum element in the List " + min.get());
}
Output
Minimum element in the List 0

5. max()- Returns the maximum element of this stream according to the provided Comparator. This method returns an Optional describing the maximum element of this stream.

List<Integer> myList = Arrays.asList(11, 1, 9, 4, 98, 0, 17, 8, 2, 3);
Optional<Integer> max = myList.stream().max(Integer::compare);
if(max.isPresent()){
  System.out.println("Maximum element in the List " + max.get());
}
Output
Maximum element in the List 98

6. count()- Returns the count of elements in this stream.

List<Integer> myList = Arrays.asList(11, 1, 9, 4, 98, 0, 17, 8, 2, 3);
long count = myList.stream().count();
System.out.println("Count of elements in the list- " + count);
Output
Count of elements in the list- 10

7. findFirst()- Java Stream API's findFirst() method returns an Optional describing the first element of this stream, or an empty Optional if the stream is empty.

List<Integer> myList = Arrays.asList(11, 1, 9, 4, 98, 0, 17, 8, 2, 3);
Optional<Integer> value = myList.stream().findFirst();
if(value.isPresent()){
  System.out.println("First Element- " + value.get());
}
Output
First Element- 11

Stream pipeline in Java Stream

A data source (such as a Collection, an array, a generator function, or an I/O channel) followed by zero or more intermediate operations and a terminal operation together form a stream pipeline.

Java Stream API
Stream pipeline example

In the following example stream source is an ArrayList. First intermediate operation is a map operation which adds 10 to each element in the Stream, in another operation elements are sorted. Then the elements are displayed using forEach terminal operation, at this point the stream is consumed.

List<Integer> myList = Arrays.asList(11, 1, 9, 4, 98, 0, 17, 8, 2, 3);
myList.stream().map(n -> n + 10).sorted().forEach(System.out::println);

Collection Vs Stream API

Streams differ from collections on several points-

  1. No Storage- Unlike Collections a stream is not a data structure that stores elements. In a stream, elements from a data source are moved through a pipeline while going through computational operations at each intermediate step.
  2. Functional in nature- Java Stream API is functional in nature bringing functional approach to processing collections of objects. An operation on a stream produces a result, but does not modify its source. For example, filtering a Stream obtained from a collection produces a new Stream without the filtered elements, rather than removing elements from the source collection.
  3. Lazy invocation- Intermediate operations in the Java Stream API are always lazy providing opportunities for optimization.
  4. Possibly unbounded- While collections have a finite size, streams need not. Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time.
  5. Streams are Consumable- The elements of a stream are only visited once during the life of a stream, once a terminal operation is encountered with in a Stream pipeline, stream is considered consumed. After a stream is consumed a new stream must be generated to revisit the same elements of the source.
Reference: https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/util/stream/package-summary.html

That's all for the topic Java Stream API With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like