Back to Stream Creation & Utilities
Q9Question 9 of 18

Generate account numbers and iterate by index using IntStream.rangeClosed

Key Concepts
  • IntStream.rangeClosed(start, end) is an inclusive range that yields start through end, including end
  • IntStream.range(start, end) is exclusive and stops at end-1, which is ideal for list indices
  • mapToObj turns each primitive int into an object, for example building an account-number String
  • IntStream.range(0, list.size()) lets you iterate by index and pair each element with its position
  • Index-based iteration is appropriate when you need the position, not just the element
  • IntStream avoids autoboxing, so it is more efficient than Stream<Integer> for numeric work
  • Use rangeClosed when the upper bound is a real value (1 to 100) and range when it is a count or size

Definition

IntStream.rangeClosed(1, n) produces an inclusive int sequence you map to account numbers with mapToObj, while IntStream.range(0, list.size()) gives exclusive indices to pair each element with its position.

IntStream range methods produce a sequence of primitive ints without boxing.

  • IntStream.range(start, end) – yields start up to end - 1, so end is excluded.
  • IntStream.rangeClosed(start, end) – yields start up to end, so end is included.
  • mapToObj(IntFunction) – converts each int into an object, turning an int stream into a Stream.
  • Index iteration – IntStream.range(0, list.size()) gives valid indices 0 .. size-1 to access the list by position.
  • No boxing – IntStream works on primitive int, avoiding the Integer wrapper overhead of Stream.

At the National Bank of India, Rajesh opens a block of new savings accounts numbered from one to ten. rangeClosed(1, 10) is the clerk counting out account numbers and including the last one, ten, because account ten is a real account. When Priya hands him a list of employees to seat, Rajesh instead counts seat positions from zero up to but not including the number of employees, which is range(0, size), because there is no seat at position equal to the count. Inclusive when the end is a real thing, exclusive when the end is just how many there are.

Understanding the Concepts

What is IntStream.rangeClosed?

IntStream.rangeClosed(start, end) creates a stream of primitive int values from start to end, with end included. It is the natural way to count over an inclusive interval.

JAVA
package com.bank.account;

import java.util.stream.IntStream;

public class RangeClosedBasics {
    public static void main(String[] args) {
        IntStream.rangeClosed(1, 5)
                .forEach(System.out::println);
    }
}
TEXT
1
2
3
4
5

How do you generate a sequence of account numbers?

Map each number in the range to an account-number String with mapToObj. Here the bank issues ten accounts with a fixed prefix.

JAVA
package com.bank.account;

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

public class AccountNumberGenerator {
    public List<String> generate(int count) {
        return IntStream.rangeClosed(1, count)
                .mapToObj(i -> String.format("ACC-%05d", i))
                .collect(Collectors.toList());
    }

    public static void main(String[] args) {
        new AccountNumberGenerator().generate(5)
                .forEach(System.out::println);
    }
}
TEXT
ACC-00001
ACC-00002
ACC-00003
ACC-00004
ACC-00005

The %05d format zero-pads each number to five digits for a tidy account code.

What is the difference between range and rangeClosed?

range excludes the upper bound; rangeClosed includes it. This single difference decides which one you use.

| Method | Produces | Upper bound | Count | Best for |
|--------|----------|-------------|-------|----------|
| range(1, 5) | 1, 2, 3, 4 | excluded | 4 | list indices, counts |
| rangeClosed(1, 5) | 1, 2, 3, 4, 5 | included | 5 | real value bounds like 1 to 100 |

JAVA
package com.bank.account;

import java.util.Arrays;
import java.util.stream.IntStream;

public class RangeVsRangeClosed {
    public static void main(String[] args) {
        int[] exclusive = IntStream.range(1, 5).toArray();
        int[] inclusive = IntStream.rangeClosed(1, 5).toArray();
        System.out.println("range(1,5)        = " + Arrays.toString(exclusive));
        System.out.println("rangeClosed(1,5)  = " + Arrays.toString(inclusive));
    }
}
TEXT
range(1,5)        = [1, 2, 3, 4]
rangeClosed(1,5)  = [1, 2, 3, 4, 5]

What does mapToObj do?

IntStream carries primitive ints, so you cannot directly build objects from it. mapToObj converts each int into an object, changing the stream type from IntStream to Stream.

JAVA
package com.bank.account;

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

public class MapToObjDemo {
    public static void main(String[] args) {
        // int -> String (object), so the result is Stream<String>
        List<String> labels = IntStream.rangeClosed(1, 3)
                .mapToObj(i -> "Branch #" + i)
                .collect(Collectors.toList());
        System.out.println(labels);
    }
}
TEXT
[Branch #1, Branch #2, Branch #3]

Without mapToObj you would be stuck with primitive ints and could not collect into a List.

How do you iterate a list by index?

Use IntStream.range(0, list.size()), which yields valid indices 0 through size - 1. You can then read list.get(i) and pair each element with its position. Here range is correct, not rangeClosed, because index size would be out of bounds.

JAVA
package com.bank.hr;

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

public class IndexIteration {
    public static void main(String[] args) {
        List<String> employees = List.of("Rajesh", "Priya", "Pankaj");

        List<String> seated = IntStream.range(0, employees.size())
                .mapToObj(i -> (i + 1) + ". " + employees.get(i))
                .collect(Collectors.toList());

        seated.forEach(System.out::println);
    }
}
TEXT
1. Rajesh
2. Priya
3. Pankaj

What are common mistakes with range and rangeClosed?

The classic bug is using rangeClosed for list indices, which reaches size and throws IndexOutOfBoundsException.

JAVA
// WRONG: rangeClosed reaches index == size, which is out of bounds
List<String> employees = List.of("Rajesh", "Priya", "Pankaj");
IntStream.rangeClosed(0, employees.size())   // 0,1,2,3 -> index 3 fails
        .mapToObj(employees::get)
        .forEach(System.out::println);
JAVA
// CORRECT: range stops at size - 1, which is the last valid index
IntStream.range(0, employees.size())         // 0,1,2
        .mapToObj(employees::get)
        .forEach(System.out::println);

The wrong version throws on the final iteration. The correct version stops at the last valid index.

What exception can index iteration throw?

An off-by-one error throws IndexOutOfBoundsException. The code below shows the exception and then prevents it.

JAVA
package com.bank.hr;

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

public class IndexException {
    public static void main(String[] args) {
        List<String> employees = List.of("Rajesh", "Priya");
        try {
            IntStream.rangeClosed(0, employees.size())  // reaches index 2
                    .forEach(i -> System.out.println(employees.get(i)));
        } catch (IndexOutOfBoundsException ex) {
            System.out.println("Caught: " + ex.getMessage());
        }

        // Prevention: use range, never rangeClosed, for indices
        IntStream.range(0, employees.size())
                .forEach(i -> System.out.println("ok: " + employees.get(i)));
    }
}
TEXT
Rajesh
Priya
Caught: Index 2 out of bounds for length 2
ok: Rajesh
ok: Priya

Why use IntStream instead of Stream?

IntStream holds primitive ints, so it avoids boxing each value into an Integer object. For numeric ranges and calculations this is faster and uses less memory, and it offers numeric terminal operations like sum, average, and max directly.

JAVA
package com.bank.account;

import java.util.stream.IntStream;

public class NoBoxing {
    public static void main(String[] args) {
        // IntStream gives sum() directly, no boxing to Integer
        int total = IntStream.rangeClosed(1, 100).sum();
        System.out.println("sum 1..100 = " + total);

        double avg = IntStream.rangeClosed(1, 10).average().orElse(0);
        System.out.println("average 1..10 = " + avg);
    }
}
TEXT
sum 1..100 = 5050
average 1..10 = 5.5

When is index-based iteration appropriate?

Use index iteration when you genuinely need the position, not just the element: numbering rows, pairing an element with its index, or stepping through two parallel lists together. When you only need the elements, a plain for-each or list.stream() is clearer.

JAVA
package com.bank.hr;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.IntStream;

public class ParallelLists {
    public static void main(String[] args) {
        List<String> names = List.of("Rajesh", "Priya");
        List<BigDecimal> salaries = List.of(
                new BigDecimal("120000"), new BigDecimal("95000"));

        // Index lets us read matching entries from two lists at once.
        IntStream.range(0, names.size())
                .mapToObj(i -> names.get(i) + " earns " + salaries.get(i))
                .forEach(System.out::println);
    }
}
TEXT
Rajesh earns 120000
Priya earns 95000

When should you avoid index iteration?

Avoid it when the position is irrelevant. Reaching for indices to just read elements is more error-prone and less readable than a direct stream.

JAVA
// AVOID: index iteration when you only need the elements
List<String> names = List.of("Rajesh", "Priya");
IntStream.range(0, names.size())
        .mapToObj(names::get)
        .forEach(System.out::println);
JAVA
// PREFER: iterate elements directly when position does not matter
names.forEach(System.out::println);
// or
names.stream().forEach(System.out::println);

How do boxed and unboxed conversions work?

You can convert between IntStream and Stream with boxed() and mapToInt. Use this when an API needs boxed Integer objects.

JAVA
package com.bank.account;

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

public class BoxingConversion {
    public static void main(String[] args) {
        // IntStream -> Stream<Integer> via boxed()
        List<Integer> ids = IntStream.rangeClosed(1, 3)
                .boxed()
                .collect(Collectors.toList());
        System.out.println(ids);

        // Stream<Integer> -> IntStream via mapToInt for sum()
        int total = ids.stream().mapToInt(Integer::intValue).sum();
        System.out.println("sum = " + total);
    }
}
TEXT
[1, 2, 3]
sum = 6

What are common interview follow-up questions?

Interviewers ask you to state the one-element difference between range and rangeClosed, show why rangeClosed(0, size) breaks list iteration, explain what mapToObj returns, justify IntStream over Stream on boxing grounds, and say when index iteration is worth it. A crisp answer: range is exclusive and perfect for indices, rangeClosed is inclusive and perfect for real value bounds, mapToObj turns ints into objects, and you use index iteration only when the position itself matters.

Visual Overview

DIAGRAM
  range vs rangeClosed

  IntStream.range(1, 5)          IntStream.rangeClosed(1, 5)
       │                              │
       ▼                              ▼
   1 2 3 4                        1 2 3 4 5
   (end 5 excluded)               (end 5 included)
DIAGRAM
  Account numbers via rangeClosed + mapToObj

  rangeClosed(1, 5)
       │  mapToObj(i -> "ACC-" + pad(i))
       ▼
  ┌──────────────────────────┐
  │ ACC-00001                 │
  │ ACC-00002                 │
  │ ACC-00003                 │
  │ ACC-00004                 │
  │ ACC-00005                 │
  └──────────────────────────┘
DIAGRAM
  Index iteration pairs element with position

  employees = [ Rajesh , Priya , Pankaj ]
  index       =   0        1        2

  IntStream.range(0, size)   // 0,1,2  (NOT rangeClosed)
       │  mapToObj(i -> (i+1) + ". " + list.get(i))
       ▼
   1. Rajesh
   2. Priya
   3. Pankaj

Key Takeaway

At the National Bank of India, Rajesh opens a block of new savings accounts numbered one through ten. He uses IntStream.rangeClosed(1, 10) because account ten is a real account and must be included, then mapToObj turns each number into a formatted account code like ACC-00010. The inclusive end is right when the upper bound is a genuine value.

When Priya hands him a list of employees to number, the situation flips. Now he counts positions with IntStream.range(0, employees.size()), which stops one before the size, because there is no element at index equal to the count. Using rangeClosed here would reach that missing index and throw IndexOutOfBoundsException. Exclusive when the end is merely a count, inclusive when it is a real value.

mapToObj is the bridge from primitive ints to objects. An IntStream holds plain ints, which keeps numeric work fast by avoiding the Integer wrapper, but to build Strings or model objects you call mapToObj, and the stream becomes a Stream. For pure arithmetic, IntStream also hands you sum, average, and max directly.

Index iteration earns its place only when the position matters: numbering rows, pairing an element with its index, or walking two parallel lists together. When you only need the elements, a direct for-each or list.stream() is clearer and safer.

Remember one sentence: rangeClosed includes the end for real value bounds, range excludes it for list indices, and mapToObj turns each int into the object you really need.

---

Share
Use ← → arrow keys to navigate