Java 8 LongStream – A Complete Guide

Introduction

We will learn about the Java 8 LongStream in this post. A LongStream is a sequence of primitive long-valued elements. It is a long primitive specialization of Stream and is not the same as a Stream<Long>.

The methods and operations supported in a LongStream are similar to that of an IntStream.

Java 8 LongStream creation

Let us now see various ways to create a LongStream. The LongStream class has static utility methods using which we can create finite and infinite, ordered and unordered streams.

Creating finite LongStream

LongStream#of

The LongStream of static method takes in a var-args of long values and returns a LongStream backed by them (ordered stream).

LongStream.of(1, 2, 3)
        .forEach(System.out::println);

Prints,

1
2
3

LongStream range and rangeClosed

The range method accepts start and end values and returns a sequential ordered LongStream from start (inclusive) and end (exclusive).

The rangeClosed is similar to range method but the end is inclusive as well.

LongStream.range(1, 5)
        .forEach(System.out::println);

Prints,

1
2
3
4
LongStream.rangeClosed(1, 5)
        .forEach(System.out::println);

Outputs,

1
2
3
4
5

Creating infinite LongStream

LongStream.generate

The LongStream.generate method takes a LongSupplier as input and returns an infinite sequential unordered stream. The elements of the stream are generated by the passed Supplier. 

Since it is an infinite stream, I have added a limit intermediate operation to limit the stream to a finite size.

AtomicLong atomicLong = new AtomicLong(0);
LongStream.generate(atomicLong::incrementAndGet)
        .limit(5)
        .forEach(System.out::println);

The source of the supplier is an AtomicLong in the above example. Each time the supplier is invoked, it will call incrementAndGet of the AtomicLong to return the next value.

Note: The method reference atomicLong::incrementAndGet when written as a lambda expression will be atomicLong -> incrementAndGet().

The above code will print from 1 to 5 (inclusive).

LongStream.iterate

As the name suggests, the iterate method is similar to a traditional for loop iteration. It takes two parameters of type long and LongUnaryOperator

The long value is the seed which is the starting element of the stream. A LongUnaryOperator is a functional interface which takes a long and returns a long. Hence, the iterate static method generates a stream with elements by iteratively applying the LongUnaryOperator function, starting with the seed element.

The elements generated (in order) are shown below:

First element - seed
Second element - f(seed)
Third element - f(f(seed))
Fourth element - f(f(f(seed)))
... and so on

Note: It returns an infinite sequential ordered LongStream.

LongStream.iterate(0, element -> element + 1)
        .limit(5)
        .forEach(System.out::println);

In the above code, we start with a seed value of 0 and on each iteration we add 2 to the previous element. Since it will produce an infinite stream, I have limited it to 5 values. This will output,

0
2
4
6
8

LongStream.iterate with a LongPredicate

Since Java 9, there is an overloaded iterate method which also accepts a LongPredicate. A LongPredicate is a predicate specialized for long values (takes a long and returns a boolean).

This iterate method returns a sequential ordered LongStream by iteratively applying of the LongUnaryOperator function as seen before. In addition to this, it will evaluate the LongPredicate each time. Only if the passed predicate is true, it will produce the element returned by the LongUnaryOperator.

The stream will terminate as soon as the LongPredicate returns false for an element computed by the LongUnaryOperator function.

Hence, this iterate method is similar to a traditional for loop with 

  • A starting value (seed)
  • A termination condition (LongPredicate)
  • An Update/Increment part (LongUnaryOperator)

Hence, the LongPredicate parameter is called as hasNext (Similar to the hasNext of the Iterator Pattern).

LongStream.iterate(0, element -> element <= 5, element -> element + 1)
        .forEach(System.out::println);
  1. We start with seed value of 0.
  2. Increment previous value by 1 each time.
  3. Till the stream element is less than or equal to 5.

This thus prints from 0 to 5 (inclusive).

When the hasNext predicate evaluation fails on the seed element itself, the generated LongStream will be empty.

System.out.println(
        LongStream.iterate(10, element -> element <= 5, element -> element + 1)
                .count()); //Prints 0

LongStream intermediate operations

We will now learn about the intermediate operations available that can be used on a LongStream. 
Note: Many will be similar to that available on a Java 8 Stream.

filter

The filter takes a LongPredicate and returns a new LongStream with elements that satisfy the predicate. 

The below code creates a LongStream with values 0 to 10, filters even numbers alone, and prints them.

LongStream.rangeClosed(0, 10)
        .filter(e -> e % 2 == 0)
        .forEach(System.out::println);

map

The map step takes a LongUnaryOperator Function and applies it to each element of the stream. Let us take add a map step to double each even number before printing it.

LongStream.rangeClosed(0, 10)
        .filter(e -> e % 2 == 0)
        .map(e -> e * 2)
        .forEach(System.out::println);

Prints,

0
4
8
12
16
20

flatMap

flatMap is useful to flatten when we map each element to a LongStream. It flattens and returns a LongStream with individual elements.

As an example, let us create a LongStream of range 10 to 15. For each of them, we will generate a LongStream with two values – one multiplied by 2 and another multiplied by 4.

ElementGenerated Value
1020, 40
1122, 44
1224, 48
1325, 52
1428, 56
1530, 60

Since the mapping is one to many, we can use a flatMap to flatten the generated multiple values into a single stream. 

LongStream.rangeClosed(10, 15)
        .flatMap(longValue -> LongStream.of(2, 4)
                .map(multiplier -> longValue * multiplier))
        .forEach(System.out::println);

This will output:

20
40
22
44
24
48
26
52
28
56
30
60

mapToDouble, mapToInt and mapToObj

The mapToDouble accepts a LongToDoubleFunction function and mapToInt takes a LongToIntFunction which maps a long to a double and an int, respectively.

LongStream.of(1, 2, 3)
        .mapToDouble(longValue -> Math.pow(2, longValue))
        .forEach(System.out::println);

We created a LongStream, mapped each value to a double (2i). This prints,

2.0
4.0
8.0

We have to pass a LongFunction to the mapToObj method. A LongFunction maps a long primitive to an object. This method is very useful because once we use this method we will have a Stream<T> where T is the type to which we map the long value.

Let us map long values to a String objects to result in a Stream<String>.

LongStream.of(1, 2, 3)
        .mapToObj(element -> "A-" +  element)
        .forEach(System.out::println);

Prints,

A-1
A-2
A-3

limit and skip

We have already seen the application of limit before this in this post. 

The limit returns a new stream after removing/truncating the elements so that the stream size is at most the specified size.

The skip() method is the inverse of limit() where it returns a stream consisting of the remaining elements of the stream after skipping/discarding the first n elements (where n is the specified number of elements to skip).

LongStream.rangeClosed(0, 10)
        .limit(2)
        .forEach(System.out::println); //Prints 0 and 1

LongStream.rangeClosed(0, 10)
        .skip(8)
        .forEach(System.out::println); //Prints 8, 9 and 10

takeWhile and dropWhile (Java 9+)

The takeWhile and dropWhile methods were added in Java 9.

  • The takeWhile method takes a LongPredicate and returns a new stream picking the elements as long as the predicate evaluates to true. It stops once it sees an element for which the predicate returns false.
  • On the other hand, the dropWhile method takes a LongPredicate and returns a new stream with the left over elements after dropping a subset of elements that satisfy the predicate. It stops dropping (and returns the rest of the elements) once it sees an element for which the predicate returns false.

It might be confusing to understand the working of these methods (especially with ordered and unordered streams). So I recommend reading the post on Stream takeWhile and DropWhile.

LongStream.boxed

The boxed method returns a Stream of type Stream<Long>. Use this when you want to convert the LongStream into a normal Stream of Long values. This involves boxing to convert the primitive long values to wrapper Long class objects.

List<Long> result = LongStream.rangeClosed(1, 5)
        .map(e -> e * 2)
        .boxed()
        .collect(Collectors.toList());

We used boxed to convert the LongStream to a Stream<Long> so that we can collect them into a list (as we cannot declare a list of primitive types in Java).

Terminal operations

allMatch, anyMatch, noneMatch

The allMatch, anyMatch and noneMatch methods take a LongPredicate and returns a boolean. 

  • allMatch – returns true if all the elements match the predicate.
  • anyMatch – returns true if any of the elements match the predicate.
  • noneMatch – returns true only if none of the elements match the predicate.

Read the Java Streams allMatch, anyMatch and noneMatch post for a detailed explanation of these methods.

findAny, findFirst

The findFirst method evaluates a LongPredicate on each element of the stream and returns the first element that satisfies the predicate. 

The findAny method does the same but can returns any element that satisfies the predicate.

You can read the post on Java streams FindFirst and FindAny where I have explained in detail about these methods in the context of a Java stream. 

LongStream min, max, sum, average

These are simple terminal operations.

  • min and max return the minimum and maximum long in the stream as an OptionalLong.
  • sum returns the sum of all elements as a primitive long value
  • Average returns an OptionalDouble.
System.out.println(LongStream.of(1, 2, 3)
        .min()); //OptionalLong[1]

System.out.println(LongStream.of(1, 2, 3)
        .max()); //OptionalLong[3]

System.out.println(LongStream.of(1, 2, 3)
        .sum()); //6

System.out.println(LongStream.of(1, 2, 3)
        .average()); //OptionalDouble[2.0]

LongStream.reduce

The reduce method performs mutable reduction on the stream elements. There are two overloaded reduce methods.

We can reduce a LongStream by using the reduce method with a LongBinaryOperator. A LongBinaryOperator takes two primitive long values and returns a long. The reduce method returns an OptionalLong.

Example: Let us sum the values of the stream using reduce.

OptionalLong sum = LongStream.rangeClosed(1, 5)
        .reduce((v1, v2) -> v1 + v2); //.reduce(Long::sum);
System.out.println(sum); //OptionalLong[15]

Another overloaded reduce method allows passing an identity value during the reduction. The identity value must be an identity for the accumulator function.

long sum = LongStream.rangeClosed(1, 5)
        .reduce(0, Long::sum);
System.out.println(sum); //15

LongStream.collect

This also performs a mutable reduction on the stream elements by using a mutable result container. It takes three arguments:

  • Supplier – to create a new mutable result container.
  • Accumulator of type ObjLongConsumer
  • Combiner of type BiConsumer

A ObjLongConsumer is a Consumer accepting a value of type T and a long value. In this context, it takes the result container and an element from the LongStream.

The combiner is a BiConsumer which is of the type of the result container i.e., it takes two instances of the result container. Note that this will be called only in case of parallel streams to combine the partial results from multiple threads. The combiner function must fold the elements from the second result container into the  first container.

Let us use the collect method to collect the elements of the LongStream into a List.

List<Long> res = LongStream.range(1, 5)
        .collect(() -> new ArrayList<>(),
                (list, longValue) -> list.add(longValue),
                (list1, list2) ->  list1.addAll(list2));
System.out.println(res); //[1, 2, 3, 4]

Using method references, we can write the above as:

List<Long> res = LongStream.range(1, 5)
        .collect(ArrayList::new,
                ArrayList::add,
                ArrayList::addAll);

Conclusion

In this post we learned about the Java 8 LongStream. First, we saw how to use static factory methods in the LongStream class to create a LongStream. Second, we learnt about the various intermediate operations. Finally, we saw about the terminal operations supported on a LongStream.

Leave a Reply