Introduction

Once you've learned how to check whether a single number is an Armstrong number, the next logical step is finding all Armstrong numbers within a given range. This is one of the most common follow-up questions in Java assignments and coding interviews because it tests whether you can reuse existing logic instead of rewriting it repeatedly.

Although the solution appears to be as simple as placing the Armstrong check inside a loop, a good implementation involves a few important design choices. Should the Armstrong check be extracted into a reusable method? Should the program print each Armstrong number immediately or store the results in a collection? What if you only need the total count rather than the numbers themselves?

In this guide, you'll learn the basic range-based solution, a reusable method approach, a counting-only variation, and a modern Java Streams implementation. You'll also discover why Armstrong numbers become surprisingly rare as the search range grows larger.

Advertisement

Quick Recap: What Is an Armstrong Number?

An Armstrong number (also called a narcissistic number) is a number whose digits, each raised to the power of the total number of digits, add up to the original number.

For example:

153

1³ + 5³ + 3³

=

1 + 125 + 27

=

153

Similarly,

1634

1⁴ + 6⁴ + 3⁴ + 4⁴

=

1634

The exponent must always equal the number of digits.


Method 1: Basic Range-Based Loop

The simplest approach checks every number in the range individually.

Java Program

public class ArmstrongRangeBasic {

    public static void main(String[] args) {

        int start = 1;
        int end = 1000;

        System.out.println("Armstrong numbers between " + start + " and " + end + ":");

        for (int num = start; num <= end; num++) {

            int digitCount = String.valueOf(num).length();

            int sum = 0;
            int temp = num;

            while (temp != 0) {

                int digit = temp % 10;

                sum += (int) Math.pow(digit, digitCount);

                temp /= 10;
            }

            if (sum == num) {
                System.out.print(num + " ");
            }
        }
    }
}

Output

Armstrong numbers between 1 and 1000:
1 2 3 4 5 6 7 8 9 153 370 371 407

How It Works

The outer loop iterates through every number between the starting and ending values.

For each number:

  1. Count its digits.

  2. Extract each digit.

  3. Raise each digit to the digit count.

  4. Add the powers together.

  5. Compare the sum with the original number.

If both values match, the number is printed.

Although this solution works, the Armstrong-checking logic is embedded directly inside main(), making it difficult to reuse elsewhere.


Method 2: Using a Reusable isArmstrong() Method

A cleaner solution extracts the Armstrong logic into a separate method.

Java Program

public class ArmstrongRangeReusable {

    static boolean isArmstrong(int num) {

        int digitCount = String.valueOf(num).length();

        int sum = 0;
        int temp = num;

        while (temp != 0) {

            int digit = temp % 10;

            sum += (int) Math.pow(digit, digitCount);

            temp /= 10;
        }

        return sum == num;
    }

    public static void main(String[] args) {

        int start = 1;
        int end = 10000;

        System.out.println("Armstrong numbers between " + start + " and " + end + ":");

        for (int num = start; num <= end; num++) {

            if (isArmstrong(num)) {
                System.out.print(num + " ");
            }
        }
    }
}

Output

Armstrong numbers between 1 and 10000:
1 2 3 4 5 6 7 8 9 153 370 371 407 1634 8208 9474

Why This Design Is Better

Separating the Armstrong logic from the range loop provides several advantages.

The isArmstrong() method can now be:

  • reused anywhere in the application

  • unit tested independently

  • called for printing

  • called for counting

  • called by Java Streams

without duplicating code.


Method 3: Counting Armstrong Numbers in a Range

Sometimes you only need the total number of Armstrong numbers rather than the actual list.

Java Program

public class ArmstrongRangeCount {

    static boolean isArmstrong(int num) {

        int digitCount = String.valueOf(num).length();

        int sum = 0;
        int temp = num;

        while (temp != 0) {

            int digit = temp % 10;

            sum += (int) Math.pow(digit, digitCount);

            temp /= 10;
        }

        return sum == num;
    }

    public static void main(String[] args) {

        int start = 1;
        int end = 100000;

        int count = 0;

        for (int num = start; num <= end; num++) {

            if (isArmstrong(num)) {
                count++;
            }
        }

        System.out.println("Number of Armstrong numbers between "
                + start + " and " + end + ": " + count);
    }
}

Output

Number of Armstrong numbers between 1 and 100000: 19

Instead of printing every Armstrong number, the program simply increments a counter whenever one is found.

This approach is ideal for:

  • statistics

  • validation

  • reporting

  • benchmarking


Method 4: Using Java Streams

Java Streams provide a concise, functional approach.

Java Program

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

public class ArmstrongRangeStreams {

    static boolean isArmstrong(int num) {

        int digitCount = String.valueOf(num).length();

        int sum = 0;
        int temp = num;

        while (temp != 0) {

            int digit = temp % 10;

            sum += (int) Math.pow(digit, digitCount);

            temp /= 10;
        }

        return sum == num;
    }

    public static void main(String[] args) {

        int start = 1;
        int end = 10000;

        List<Integer> armstrongNumbers = IntStream.rangeClosed(start, end)
                .filter(ArmstrongRangeStreams::isArmstrong)
                .boxed()
                .collect(Collectors.toList());

        System.out.println(armstrongNumbers);
    }
}

Output

[1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407, 1634, 8208, 9474]

How Streams Work

The pipeline executes in four stages:

  1. Generate every number in the range.

IntStream.rangeClosed(start, end)
  1. Keep only Armstrong numbers.

.filter(ArmstrongRangeStreams::isArmstrong)
  1. Convert primitive int values into Integer objects.

.boxed()
  1. Store everything inside a List.

.collect(Collectors.toList())

Why Armstrong Numbers Become Rare

Armstrong numbers become increasingly uncommon as numbers grow larger.

For a number with d digits, the largest possible digit-power sum is:

d × 9ᵈ

For example:

For four-digit numbers,

4 × 9⁴

=

4 × 6561

=

26244

This comfortably covers every four-digit number.

However, as digit counts continue increasing, the total number of possible numbers grows much faster than the maximum achievable digit-power sum.

Consequently, the probability of finding another Armstrong number becomes extremely small.

An interesting mathematical fact:

There are exactly 88 Armstrong numbers in base 10, and the largest one contains 39 digits.


How Java Handles This Internally

Methods 1–3

Variables like:

  • num

  • sum

  • temp

  • digitCount

are primitive values stored on the stack.

Each call to:

String.valueOf(num)

creates a temporary String object on the heap for digit counting.


Method 4

IntStream.rangeClosed() creates a stream pipeline.

Operations such as:

filter()

are lazy.

No filtering actually occurs until the terminal operation:

collect()

is executed.

The resulting List<Integer> is allocated on the heap, and each primitive int is automatically boxed into an Integer.


Real-Life Analogy

Imagine searching through millions of numbered lockers looking for a very unusual locker.

For each locker, you perform a calculation using only the digits printed on its own number.

Almost every locker fails.

Occasionally, however, one locker's number exactly equals the result of that calculation.

Those rare lockers are Armstrong numbers.

As the locker numbers become larger and larger, these special matches become increasingly difficult to find.


Comparison Table

Method Reusable Output Best Used When
Basic Loop No Printed directly Small standalone programs
Reusable Method Yes Printed directly Production-quality code
Counting Version Yes Count only Statistics and validation
Java Streams Yes List Functional programming style

Best Practices

  • Always place the Armstrong logic inside a reusable isArmstrong() method.

  • Calculate the digit count only once for each number.

  • Print results directly unless they must be reused later.

  • Use Streams only when the project already favors functional programming.

  • Remember that Armstrong numbers become extremely rare for large ranges.


Common Mistakes

Duplicating Armstrong Logic

Avoid copying the same digit-processing code into multiple places.

Create one reusable method instead.


Assuming Armstrong Numbers Are Common

They are not.

Large ranges often contain very few Armstrong numbers.


Recalculating the Digit Count

Compute it once before processing the digits.


Collecting Results When Only a Count Is Needed

If only the number of Armstrong values matters, maintain a counter instead of building a list.


Incorrect Range Boundaries

Using:

num < end

instead of

num <= end

can accidentally skip a valid Armstrong number at the upper limit.


Expert Tips

A strong interview answer is:

"I would first extract the Armstrong logic into a reusable isArmstrong() method. Then I'd iterate through the required range, calling that method for each number. Depending on the requirement, I could print the numbers, count them, or collect them into a list. The overall time complexity is O(n × d), where n is the number of values in the range and d is the average number of digits processed for each value."

Mentioning the reusable method and the time complexity demonstrates good software design as well as algorithmic understanding.


Pros and Cons

Method Advantages Disadvantages
Basic Loop Easy to understand Logic cannot be reused
Reusable Method Clean, modular, maintainable Slightly more code
Counting Version Uses very little memory Doesn't store the actual numbers
Streams Concise and modern Slightly more overhead and less beginner-friendly

Frequently Asked Questions

How do I print Armstrong numbers within a range in Java?

Loop through every number and call an isArmstrong() method for each one.


Why should I create a reusable isArmstrong() method?

It avoids duplicate code and allows the same logic to be reused for printing, counting, filtering, or testing.


How many Armstrong numbers exist?

There are 88 Armstrong numbers in base 10, and the largest one contains 39 digits.


Why do Armstrong numbers become rare?

As digit counts increase, the number of possible values grows much faster than the maximum achievable sum of powered digits.


Can I count Armstrong numbers without printing them?

Yes.

Increment a counter whenever isArmstrong() returns true.


Can Java Streams be used?

Yes.

Use:

IntStream.rangeClosed(start, end)

followed by:

filter()

and

collect()

to generate the list.


What is the time complexity?

O(n × d)

where:

  • n = numbers in the range

  • d = average number of digits


Is this a common interview question?

Yes.

It is a very common extension after implementing a single Armstrong-number check.


Can I search ranges larger than 100000?

Yes.

The same algorithm works for any range, although Armstrong numbers become increasingly sparse.


Should I print or store the results?

Print them for one-time output.

Store them in a list if additional processing is required later.


Does the reusable method work for single-digit numbers?

Yes.

Every single-digit number is an Armstrong number because:

digit¹ = digit

What is the largest Armstrong number?

The largest known Armstrong number in base 10 contains 39 digits, and mathematics proves that no larger Armstrong numbers exist.