Factorial Program in Java: Loop, Recursion, Streams, BigInteger

Write a factorial program in Java with a loop, recursion, streams and BigInteger, detect long overflow after 20!, reject negatives and count combinations.

Call stack of the recursive factorial(4) in Java, going down to factorial(0) and returning 1, 1, 2, 6 and 24

The factorial of a non-negative integer n, written n!, is the product of all integers from 1 to n, and 0! is 1 by definition. In Java, a loop over long values gives exact results up to 20!, and BigInteger handles anything larger.

We need factorials for counting problems, such as the number of ways to order a playlist or to pick lottery numbers, and interviewers use the factorial program to check recursion and overflow handling.

The following example calls the factorial() method from section 2.

long five = factorial(5);          // 120
long zero = factorial(0);          // 1
long twenty = factorial(20);       // 2432902008176640000
long tooBig = factorial(21);       // ArithmeticException: long overflow

The call with 21 throws an exception instead of returning a wrong number, because the method multiplies with Math.multiplyExact(). After the loop, we write the recursive and stream versions, switch to BigInteger for large numbers, and use factorials in a real counting problem.

1. How Fast n! Grows

Each step multiplies the result by a larger number, so factorials grow faster than any power of 2. The value 13! is already larger than Integer.MAX_VALUE, and 21! is larger than Long.MAX_VALUE. Knowing these limits tells us which type to use before we write any code.

TypeLargest factorial it holdsValue
int12!479001600
long20!2432902008176640000
double170! (approximate, about 16 significant digits)7.257415615307994E306
BigIntegerNo fixed limit, only memory100! has 158 digits

Java does not report an overflow for int and long multiplication. The result wraps around, so a careless program prints a negative or a plainly wrong factorial for 21!.

long wrapped = 2432902008176640000L * 21;   // -4249290049419214848
int intWrapped = 479001600 * 13;            // 1932053504, the real 13! is 6227020800

2. Factorial Program in Java with a Loop

The loop starts with 1 and multiplies it by 2, 3 and so on up to n. For 0 and 1, the loop body never runs, and the method returns 1, which matches the definition. Math.multiplyExact() throws an ArithmeticException instead of wrapping around, which is the exact arithmetic that Java has had since Java 8. The Core-Java repository has every version with a runnable Java 25 demo.

static long factorial(int n) {
    if (n < 0) {
        throw new IllegalArgumentException("n must not be negative: " + n);
    }
    long result = 1;
    for (int i = 2; i <= n; i++) {
        result = Math.multiplyExact(result, i);
    }
    return result;
}
long one = factorial(1);           // 1
long ten = factorial(10);          // 3628800
long negative = factorial(-3);     // IllegalArgumentException: n must not be negative: -3

A factorial method must reject negative input and detect overflow, because both cases otherwise return a number that looks valid. The loop is the version we write in an interview and in production, because it needs no stack and no extra objects.

3. Factorial Using Recursion

The recursive definition says that the factorial of n is n times the factorial of n – 1, and the factorial of 0 is 1. The method calls itself with n – 1 until it reaches the base case 0, and the results are multiplied on the way back. For a refresher on base cases and the call stack, see our recursion tutorial.

Call stack of the recursive factorial(4) in Java, going down to factorial(0) and returning 1, 1, 2, 6 and 24
Each call waits on the stack until the call below it returns, so factorial(4) uses five stack frames
static long factorialRecursive(int n) {
    if (n < 0) {
        throw new IllegalArgumentException("n must not be negative: " + n);
    }
    if (n == 0) {
        return 1;                       // base case
    }
    return Math.multiplyExact(n, factorialRecursive(n - 1));
}
long rec4 = factorialRecursive(4);      // 24
long rec0 = factorialRecursive(0);      // 1

The base case must cover 0. Many published versions stop at n == 1, so a call with 0 went on to -1, -2 and so on until the stack ran out.

static long oldRecursive(long n) {
    return n == 1 ? 1 : n * oldRecursive(n - 1);
}
long crash = oldRecursive(0);           // StackOverflowError

Recursion is fine for long results, because the depth never goes past 21 calls. With BigInteger and a large n, every call adds a stack frame, and the HotSpot JVM does not perform tail-call optimization, so a recursive 100000! fails with a StackOverflowError on the default stack size. The tail vs non-tail recursion article explains why rewriting the method as a tail call does not help in Java.

4. Factorial with Java Streams

A LongStream, one of the primitive streams, can produce the numbers 1 to n with rangeClosed(), and reduce() multiplies them, starting from the identity value 1. Passing Math::multiplyExact as the reducer keeps the overflow check.

static long factorialStream(int n) {
    if (n < 0) {
        throw new IllegalArgumentException("n must not be negative: " + n);
    }
    return LongStream.rangeClosed(1, n).reduce(1, Math::multiplyExact);
}
long stream6 = factorialStream(6);      // 720
long stream0 = factorialStream(0);      // 1

For 0, rangeClosed(1, 0) is empty, and reduce() returns the identity 1. The stream version is shorter than the loop but not faster, so we choose it for style, not for speed.

5. Factorial of Large Numbers with BigInteger

The BigInteger class stores integers of any size, limited only by memory. Its objects are immutable, so multiply() returns a new object, and we assign it back to the result variable in each step.

static BigInteger bigFactorial(int n) {
    if (n < 0) {
        throw new IllegalArgumentException("n must not be negative: " + n);
    }
    BigInteger result = BigInteger.ONE;
    for (int i = 2; i <= n; i++) {
        result = result.multiply(BigInteger.valueOf(i));
    }
    return result;
}
BigInteger f25 = bigFactorial(25);                  // 15511210043330985984000000
int digits100 = bigFactorial(100).toString().length();   // 158
int digits1000 = bigFactorial(1000).toString().length(); // 2568
BigInteger viaStream = LongStream.rangeClosed(1, 25).mapToObj(BigInteger::valueOf).reduce(BigInteger.ONE, BigInteger::multiply);   // 15511210043330985984000000

Each multiplication gets slower as the number grows, so the cost of n! with BigInteger rises faster than O(n). In practice, the loop is fast enough for n in the thousands, and the 2,568-digit result of 1000! is no problem for BigInteger.

6. Precomputed Factorials for Repeated Calls

Only 21 factorials fit in a long, so a program that needs many of them can compute all 21 once and read them from an array. A lookup costs one array access, and the class rejects every n outside 0 to 20 with a clear message.

final class FactorialTable {
    private static final long[] VALUES = new long[21];

    static {
        VALUES[0] = 1;
        for (int i = 1; i < VALUES.length; i++) {
            VALUES[i] = VALUES[i - 1] * i;
        }
    }

    static long of(int n) {
        if (n < 0 || n >= VALUES.length) {
            throw new IllegalArgumentException("n must be 0 to 20: " + n);
        }
        return VALUES[n];
    }
}
long table15 = FactorialTable.of(15);   // 1307674368000
long table21 = FactorialTable.of(21);   // IllegalArgumentException: n must be 0 to 20: 21

7. Using Factorials to Count Arrangements

Factorials answer counting questions. A music app that shuffles a playlist of 10 songs can produce 10! = 3,628,800 different orders, so the chance of hearing the same order twice in a row is about one in 3.6 million.

Combinations are a common next step, for example choosing 6 lottery numbers out of 49. The textbook formula divides n! by k! and by (n – k)!, but computing 49! first overflows a long, so we use the multiplicative form, which multiplies and divides in turns and keeps every intermediate value small and exact.

static long choose(int n, int k) {
    long result = 1;
    for (int i = 1; i <= k; i++) {
        result = result * (n - k + i) / i;   // always an exact division
    }
    return result;
}
long playlists = factorial(10);         // 3628800
long lottery = choose(49, 6);           // 13983816
long naive = factorial(49);             // ArithmeticException: long overflow

The division in each step is exact, because after step i the result is the number of ways to choose i items from n – k + i, which is always an integer.

8. Loop, Recursion, Stream or BigInteger

All the versions compute the same values, so the choice depends on the range of n and on the rules of the task. The loop with long covers most needs, and BigInteger takes over above 20.

ApproachExact up toTimeStack useGood for
Loop with multiplyExact()20!O(n)O(1)Default choice, interviews
Recursion20!O(n)O(n) framesShowing recursion and base cases
LongStream.reduce()20!O(n)O(1)Functional style
BigInteger loopAny n, limited by memoryGrows faster than O(n)O(1)Large factorials, exact digits
Lookup table20!O(1) per callO(1)Many calls in a hot path

9. Factorial FAQs

Beginners tend to ask these questions after writing their first factorial program.

9.1. Why is the factorial of 0 equal to 1?

Because there is one way to arrange zero items, namely the empty arrangement. The value 1 also keeps the rule “factorial of n equals n times the factorial of n – 1” true for n = 1, because 1 x 1 = 1.

9.2. What is the largest factorial that fits in a long?

20!, which is 2432902008176640000. 21! is about 5.1 x 1019, larger than Long.MAX_VALUE (about 9.2 x 1018), so we switch to BigInteger above 20.

9.3. Is recursion or a loop better for factorial in Java?

The loop. Both run in O(n) time, but recursion adds a stack frame for every call, and Java does not remove tail calls. For long results the depth is at most 21, so recursion works there too, but the loop has no stack limit and is what most reviewers expect.

9.4. How do we calculate the factorial of 100 in Java?

With BigInteger, as in section 5. The result has 158 digits, far beyond the 19 digits that a long can hold.

9.5. What should a factorial method do with negative numbers?

Throw an IllegalArgumentException. The factorial is not defined for negative integers, and returning 1 or 0 hides the bug in the calling code.

10. Conclusion

A factorial program in Java is a loop that multiplies 1 to n, with a check for negative input. Using Math.multiplyExact() turns the silent overflow after 20! into an ArithmeticException, and BigInteger gives exact results for any larger n.

Recursion and streams produce the same values and are good exercises, as long as the base case covers 0. For counting problems, we often do not need the full factorial at all, because the multiplicative formula for combinations stays within a long where n! does not.

11. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. Why should the perfomance of the first method be better than the second one? It follows the dynamic programming method, one calculates the partial results with the space of one variable and the iteration variable. Yes each time the <-Test has to be resolved, but thats much "cheaper" than to initiate the function/method each time, create the new parameterset on the stack and generating more memory until the last factorial is resolved.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.