web
 

Java 8 Stream - Java Stream


Welcome to Java 8 Stream API tutorial. In the last few java 8 posts, we looked into Java 8 Interface Changes and Functional Interfaces and Lambda Expressions. Today we will look into one of the major API introduced in Java 8 – Java Stream.

Java Stream

Before we look into Java Stream API Examples, let’s see why it was required. Suppose we want to iterate over a list of integers and find out sum of all the integers greater than 10.

Prior to Java 8, the approach to do it would be:

private static int sumIterator(List<Integer> list) { Iterator<Integer> it = list.iterator(); int sum = 0; while (it.hasNext()) { int num = it.next(); if (num > 10) { sum += num; } } return sum; }

There are three major problems with the above approach:

We just want to know the sum of integers but we would also have to provide how the iteration will take place, this is also called external iteration because client program is handling the algorithm to iterate over the list.
  • The program is sequential in nature, there is no way we can do this in parallel easily.
  • There is a lot of code to do even a simple task.
  • To overcome all the above shortcomings, Java 8 Stream API was introduced. We can use Java Stream API to implement internal iteration, that is better because java framework is in control of the iteration.

    Internal iteration provides several features such as sequential and parallel execution, filtering based on the given criteria, mapping etc.

    Most of the Java 8 Stream API method arguments are functional interfaces, so lambda expressions work very well with them. Let’s see how can we write above logic in a single line statement using Java Streams.

    private static int sumStream(List<Integer> list) { return list.stream().filter(i -> i > 10).mapToInt(i -> i).sum(); }

    Notice that above program utilizes java framework iteration strategy, filtering and mapping methods and would increase efficiency.

    First of all we will look into the core concepts of Java 8 Stream API and then we will go through some examples for understanding most commonly used methods.

    Collections and Java Stream

    A collection is an in-memory data structure to hold values and before we start using collection, all the values should have been populated. Whereas a java Stream is a data structure that is computed on-demand.

    Java Stream doesn’t store data, it operates on the source data structure (collection and array) and produce pipelined data that we can use and perform specific operations. Such as we can create a stream from the list and filter it based on a condition.

    Java Stream operations use functional interfaces, that makes it a very good fit for functional programming using lambda expression. As you can see in the above example that using lambda expressions make our code readable and short.

    Java 8 Stream internal iteration principle helps in achieving lazy-seeking in some of the stream operations. For example filtering, mapping, or duplicate removal can be implemented lazily, allowing higher performance and scope for optimization.

    Java Streams are consumable, so there is no way to create a reference to stream for future usage. Since the data is on-demand, it’s not possible to reuse the same stream multiple times.

    Java 8 Stream support sequential as well as parallel processing, parallel processing can be very helpful in achieving high performance for large collections.

    All the Java Stream API interfaces and classes are in the java.util.stream package. Since we can use primitive data types such as int, long in the collections using auto-boxing and these operations could take a lot of time, there are specific classes for primitive types – IntStream, LongStream and DoubleStream.

    Functional Interfaces in Java 8 Stream

    Some of the commonly used functional interfaces in the Java 8 Stream API methods are:

    1. Function and BiFunction: Function represents a function that takes one type of argument and returns another type of argument. Function<T, R> is the generic form where T is the type of the input to the function and R is the type of the result of the function.

      For handling primitive types, there are specific Function interfaces – ToIntFunction, ToLongFunction, ToDoubleFunction, ToIntBiFunction, ToLongBiFunction, ToDoubleBiFunction, LongToIntFunction, LongToDoubleFunction, IntToLongFunction, IntToDoubleFunction etc.

      Some of the Stream methods where Function or it’s primitive specialization is used are:

      • <R> Stream<R> map(Function<? super T, ? extends R> mapper)
      • IntStream mapToInt(ToIntFunction<? super T> mapper) – similarly for long and double returning primitive specific stream.
      • IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) – similarly for long and double
      • <A> A[] toArray(IntFunction<A[]> generator)
      • <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
    2. Predicate and BiPredicate: It represents a predicate against which elements of the stream are tested. This is used to filter elements from the java stream. Just like Function, there are primitive specific interfaces for int, long and double.

      Some of the Stream methods where Predicate or BiPredicate specializations are used are:

      • Stream<T> filter(Predicate<? super T> predicate)
      • boolean anyMatch(Predicate<? super T> predicate)
      • boolean allMatch(Predicate<? super T> predicate)
      • boolean noneMatch(Predicate<? super T> predicate)
    3. Consumer and BiConsumer: It represents an operation that accepts a single input argument and returns no result. It can be used to perform some action on all the elements of the java stream.

      Some of the Java 8 Stream methods where Consumer, BiConsumer or it’s primitive specialization interfaces are used are:

      • Stream<T> peek(Consumer<? super T> action)
      • void forEach(Consumer<? super T> action)
      • void forEachOrdered(Consumer<? super T> action)
    4. Supplier: Supplier represent an operation through which we can generate new values in the stream. Some of the methods in Stream that takes Supplier argument are:
      • public static<T> Stream<T> generate(Supplier<T> s)
      • <R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner)

    java.util.Optional

    ---

    Java Optional is a container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value. Stream terminal operations return Optional object. Some of these methods are:





    journaldev is optimized for learning.© journaldev .
    All Right Reserved and you agree to have read and accepted our term and condition