Index

Working with Primitive Streams

Java Streams

5.1 IntStream, LongStream, DoubleStream

Java provides specialized stream interfacesβ€”IntStream, LongStream, and DoubleStreamβ€”to efficiently handle primitive data types without the overhead of boxing and unboxing that occurs when using generic Stream<T> with wrapper classes like Integer or Double.

Why Use Primitive Streams?

Key Differences from StreamT

Feature Stream<T> Primitive Streams (IntStream, etc.)
Element Type Object (e.g., Integer) Primitive (int, long, double)
Boxing/Unboxing Overhead Yes No
Specialized Methods General methods Numeric-specific (e.g., sum(), average())
Conversion Methods mapToInt(), mapToLong(), etc. Can convert to/from Stream<T>

Creating and Using Primitive Streams

IntStream example: sum and average

import java.util.stream.IntStream;

public class IntStreamExample {
    public static void main(String[] args) {
        IntStream numbers = IntStream.of(1, 2, 3, 4, 5);

        int sum = numbers.sum();
        System.out.println("Sum: " + sum); // Output: Sum: 15

        // To reuse the stream, recreate it:
        double average = IntStream.of(1, 2, 3, 4, 5).average().orElse(0);
        System.out.println("Average: " + average); // Output: Average: 3.0
    }
}

LongStream example: generating ranges

import java.util.stream.LongStream;

public class LongStreamExample {
    public static void main(String[] args) {
        long sum = LongStream.rangeClosed(1, 5).sum();
        System.out.println("Sum of 1 to 5: " + sum); // Output: 15
    }
}

DoubleStream example: statistics

import java.util.stream.DoubleStream;

public class DoubleStreamExample {
    public static void main(String[] args) {
        DoubleStream doubles = DoubleStream.of(1.5, 2.5, 3.5);

        double max = doubles.max().orElse(Double.NaN);
        System.out.println("Max: " + max); // Output: Max: 3.5
    }
}

Performance Benefits

Primitive streams avoid the overhead of boxing/unboxing that occurs in generic streams, which can significantly improve performance in numerical computations, especially on large datasets or in tight loops.

Using these specialized streams is a best practice when dealing with primitive data, combining clean code with efficient execution.

Index

5.2 Conversion Between Object and Primitive Streams

Java Streams provide convenient methods to convert between object streams (e.g., Stream<Integer>) and primitive streams (IntStream, LongStream, DoubleStream). This conversion is essential to combine the expressive power of object streams with the performance benefits of primitive streams.

Converting Object Streams to Primitive Streams

These methods apply a mapping function that extracts the primitive value from each object and returns the corresponding primitive stream.

Boxing Primitive Streams Back to Object Streams

Example 1: Object Stream to IntStream and Back

import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class ConversionExample1 {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4);

        // Convert Stream<Integer> to IntStream
        IntStream intStream = numbers.stream()
                                    .mapToInt(Integer::intValue);

        // Sum of primitive ints
        int sum = intStream.sum();
        System.out.println("Sum: " + sum); // Output: Sum: 10

        // Convert IntStream back to Stream<Integer>
        Stream<Integer> boxedStream = IntStream.range(1, 5).boxed();
        boxedStream.forEach(System.out::println);
    }
}

Example 2: Mapping Objects to DoubleStream

import java.util.List;

public class ConversionExample2 {
    public static void main(String[] args) {
        List<String> prices = List.of("9.99", "19.95", "5.50");

        // Convert Stream<String> to DoubleStream
        double total = prices.stream()
                             .mapToDouble(Double::parseDouble)
                             .sum();

        System.out.println("Total price: " + total); // Output: Total price: 35.44
    }
}

Example 3: Handling Nulls When Mapping to Primitive Streams

import java.util.List;
import java.util.Objects;

public class ConversionExample3 {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, null, 3, 4);

        // Map to IntStream carefully, filtering out nulls first
        int sum = numbers.stream()
                         .filter(Objects::nonNull)
                         .mapToInt(Integer::intValue)
                         .sum();

        System.out.println("Sum ignoring nulls: " + sum); // Output: 8
    }
}

Summary

Index

5.3 Primitive Stream Operations and Examples

Primitive streams such as IntStream, LongStream, and DoubleStream provide specialized operations tailored to numeric data processing. These operations simplify common statistical calculations, making the code more concise and efficient by avoiding boxing overhead.

Key Operations

Example 1: Sum and Average of IntStream

import java.util.stream.IntStream;

public class PrimitiveOperations1 {
    public static void main(String[] args) {
        IntStream numbers = IntStream.of(10, 20, 30, 40, 50);

        int sum = numbers.sum();
        System.out.println("Sum: " + sum); // Output: Sum: 150

        // Need to recreate stream for average, as streams are single-use
        double average = IntStream.of(10, 20, 30, 40, 50).average().orElse(0);
        System.out.println("Average: " + average); // Output: Average: 30.0
    }
}

Example 2: Finding Min and Max in a LongStream

import java.util.stream.LongStream;

public class PrimitiveOperations2 {
    public static void main(String[] args) {
        LongStream values = LongStream.of(100L, 200L, 50L, 400L, 300L);

        long min = values.min().orElseThrow();
        System.out.println("Min value: " + min); // Output: Min value: 50

        // Recreate stream for max
        long max = LongStream.of(100L, 200L, 50L, 400L, 300L).max().orElseThrow();
        System.out.println("Max value: " + max); // Output: Max value: 400
    }
}

Example 3: Using range() to Generate and Sum a Range of Numbers

import java.util.stream.IntStream;

public class PrimitiveOperations3 {
    public static void main(String[] args) {
        int sumRange = IntStream.range(1, 6)  // 1 to 5 inclusive of 1, exclusive of 6
                               .sum();
        System.out.println("Sum of range 1 to 5: " + sumRange); // Output: 15
    }
}

Summary

Primitive stream operations provide a clean and efficient way to perform numeric calculations without manual looping or boxing. Methods like sum(), average(), min(), and max() are concise and return meaningful results wrapped in optionals when necessary, reducing boilerplate and improving readability. Additionally, range() and rangeClosed() help quickly generate numeric sequences for processing, enhancing productivity when working with numeric datasets.

Index