Back to Blog
Java

Java IntStream Range: Using range() and rangeClosed()

java intstream range: Learn how to generate numeric sequences with Java IntStream.range, including rangeClosed, practical examples, and performance considerations.

IntStreamJava StreamsrangeClosedJava 8
Diagram showing Java IntStream range generating a sequence of integers from a start to an end value

Java's IntStream.range() provides a concise way to generate a sequence of integers for use in stream pipelines. The method is part of the java.util.stream.IntStream interface, introduced in Java 8, and is often used to replace traditional for loops with functional-style iteration. This article covers the syntax, behavior, and practical applications of java intstream range, including the related rangeClosed method and performance tradeoffs.

IntStream.range() vs IntStream.rangeClosed()

The IntStream interface offers two static methods for creating sequential ranges:

  • range(int start, int end) generates a stream of integers from start (inclusive) to end (exclusive).
  • rangeClosed(int start, int end) generates a stream from start to end (both inclusive).

The difference is subtle but important. For example, IntStream.range(1, 5) produces 1, 2, 3, 4, while IntStream.rangeClosed(1, 5) produces 1, 2, 3, 4, 5. This mirrors the exclusive upper bound used by many loop constructs and the inclusive bound used when the endpoint must be included.

Creating an IntStream from a Range

Both methods return an IntStream instance that is sequential and ordered. The stream is lazy: no values are computed until a terminal operation is invoked. Here is a minimal example:

import java.util.stream.IntStream; public class RangeExample { public static void main(String[] args) { IntStream.range(1, 4) .forEach(System.out::println); } }

This prints 1, 2, and 3. The forEach terminal operation triggers the stream pipeline. Because range is exclusive, the value 4 is not included.

When you need the endpoint included, use rangeClosed:

IntStream.rangeClosed(1, 4) .forEach(System.out::println);

This prints 1, 2, 3, and 4.

Using IntStream.range with Stream Operations

The real value of IntStream.range appears when you combine it with other stream operations. For example, you can map each integer to a transformed value, filter based on a condition, or collect results into a collection.

List<String> labels = IntStream.rangeClosed(1, 5) .mapToObj(i -> "Item " + i) .toList();

This produces a list containing "Item 1" through "Item 5". The mapToObj method converts the primitive int to a String object, which is necessary because IntStream works with primitives.

You can also use range to generate indexes for iterating over an array or list:

String[] names = {"Alice", "Bob", "Carol"}; IntStream.range(0, names.length) .mapToObj(i -> i + ": " + names[i]) .forEach(System.out::println);

This prints each index and name, demonstrating how range can replace a traditional indexed for loop while keeping the logic inside a stream pipeline.

Converting an IntStream to Other Types

IntStream is a primitive stream, so it does not support all operations directly. When you need a Stream<Integer>—for example, to use methods that require object references—you can call boxed():

Stream<Integer> boxed = IntStream.rangeClosed(1, 10).boxed();

You can also convert the stream to an array:

int[] numbers = IntStream.rangeClosed(1, 5).toArray();

The toArray() method returns an int[], which is useful when you need to pass the sequence to a method that expects an array.

Performance and Memory Considerations

IntStream.range is not a magic performance replacement for a simple for loop. In fact, for trivial operations like summing a fixed range, a traditional loop may be faster because it avoids the overhead of stream machinery, such as object allocation for the stream pipeline and potential lambda invocation overhead. However, the difference is often negligible for small ranges.

The stream approach becomes more compelling when you need to chain multiple operations, such as filtering, mapping, and reducing, because the pipeline is evaluated lazily and can be optimized by the JVM. The range method itself does not allocate an array; it generates values on demand, so memory usage remains constant regardless of the range size.

If performance is critical and the range is large, consider whether the stream pipeline can be parallelized. You can call parallel() on the stream, but this only helps if the operations are independent and the workload is large enough to justify thread coordination overhead.

Common Pitfalls and Edge Cases

One common mistake is assuming that range is inclusive at both ends. Remember that the second argument is exclusive. Another issue is passing a start value greater than the end value. In that case, both range and rangeClosed produce an empty stream, not an error.

IntStream.range(5, 1).forEach(System.out::println); // no output

If you need a descending sequence, you cannot use range directly because it only steps upward. You would need to use iterate or rangeClosed with a reverse mapping:

IntStream.rangeClosed(1, 5) .map(i -> 6 - i) .forEach(System.out::println); // prints 5,4,3,2,1

This works for small, known ranges, but for arbitrary ranges you may prefer IntStream.iterate with a decrementing step.

Alternatives to IntStream.range

For simple numeric loops, a traditional for loop is often clearer and more familiar:

for (int i = 0; i < 10; i++) { System.out.println(i); }

When you need to generate a range of values for a stream pipeline, IntStream.range is the idiomatic choice. However, if you need a range of long values, use LongStream.range or LongStream.rangeClosed. If you need a stream of arbitrary objects, consider Stream.iterate or Stream.generate.

The decision between range and rangeClosed depends entirely on whether the endpoint should be included. In most loop scenarios, the exclusive bound matches the common i < n pattern, so range is the natural equivalent. When you need to include the endpoint, such as when generating indices from 0 to length - 1, range is appropriate; but when the endpoint represents the last valid item, rangeClosed may be more direct.

Ultimately, IntStream.range is a compact, readable way to express numeric sequences in a functional style. It integrates cleanly with the Stream API and can improve code clarity when used appropriately, without introducing significant memory overhead.

java intstream range: Practical Usage and Code Examples | RYUSLOG DEV