DEV Community

Cover image for Improve Your Java Code Using Streams
Abdulcelil Cercenazi
Abdulcelil Cercenazi

Posted on

Improve Your Java Code Using Streams

What are streams? 🌊

Streams were introduced in Java 8. According to Oracle docs, it’s:

Classes to support functional-style operations on streams of elements, such as map-reduce transformations on collections

Let’s put it in more practical terms, a stream consists of a data source, followed by zero or more intermediate operations and then a terminal operation.

What data sources? 🗃

  • Collections, Lists, Sets, ints, longs, doubles, arrays, lines of a file.

what intermediate operations?🪓

  • filter, map, sort, etc.
  • Those operations return a stream, so they can be chained with other operations.

What terminal operations?

  • forEach, collect, reduce, findFirst, etc.
  • They return a void or non-stream result.
  • If a stream doesn’t have a terminal operation, the intermediate ones aren’t going to get called.

Let’s convert a function from imperative to a declarative style using Streams

The imperative function (No Streams) 🦨

private int getResult_imperative(List<String> strings) {  
    int result = 0;  
    for (String string : strings){  
        if(isDigit(string.charAt(0))) continue;  
        if (string.contains("_")) continue;  
        result += string.length();  
    }  
    return result;  
}
Enter fullscreen mode Exit fullscreen mode

We notice that we manually have to do a couple of things 😤

  • Declare a result variable to keep track of the result
  • Loop through the Strings
  • Write two if statements (which could be much more complex than this case)
  • Add the length of each one to the result

Let’s check the declarative style (Streams) 🤩

private int getResult_Declarative(List<String> strings){  
    return strings.  
            stream().  
            filter(s -> !isDigit(s.charAt(0))).  
            filter(s -> !s.contains("_")).  
            mapToInt(String::length).  
            sum();  
}
Enter fullscreen mode Exit fullscreen mode

So, what is different? 👀

  • we get a Stream object by calling the stream() function
  • (Intermediate operation) we apply to the filter function twice, for each time we specify a condition that only the elements we want to proceed to the next phase should meet.
  • (Intermediate operation) we map each String object to an int by calling the length method (using method reference style)
  • (terminal operation) we sum all the previous int values

Observations🧠

Didn’t the second approach feel more straightforward? We specified what we wanted and not how we want to do it. That is the spirit of declarative programming and the purpose of the Stream API in modern Java applications.

Here is an excellent source to get more in-depth regarding streams in Java.

Discussion (0)