What is BigInteger in Java, when would you use it instead of long, and what does it mean that it is immutable?
answer
- Arbitrary precision, never overflows
- Immutable — capture the result, like String
- valueOf(long), new BigInteger("..."), ZERO/ONE/TWO/TEN
- intValueExact throws if it won't fit
- Allocation cost in hot loops
basics
~10 sBigInteger holds whole numbers of any size, so it never overflows like long does. It is immutable: every operation returns a new BigInteger instead of changing the original.
solid answer
~40 sBigInteger (java.math.BigInteger) represents signed integers of arbitrary size, limited only by available memory, so it never overflows the way the 64-bit long does once a value exceeds about 9.2 quintillion. You reach for it for factorials, very large counters, cryptographic keys, or exact math where wrap-around would be a silent bug. It is immutable, like String: methods such as add or multiply do not mutate the receiver; they return a brand-new BigInteger. That makes instances safe to share across threads but means you must always capture the result (b = b.add(x), not just b.add(x)). You create one with BigInteger.valueOf(long) for small values, new BigInteger("...") from a decimal string, and there are cached constants ZERO, ONE, TWO, TEN.
go deeper
Knows BigInteger holds huge numbers and never overflows, and that you must reassign the result of add/multiply.
Explains immutability vs long, the overflow semantics it avoids, and chooses valueOf vs the String constructor appropriately; uses *Exact conversions.
Articulates the allocation/perf trade-off, thread-safety implication of immutability, and when long is the right call instead.
Frames it as a value-type design (immutability + interning of constants), discusses GC/throughput impact at scale and where to draw the BigInteger-vs-primitive boundary in APIs.
## The problem BigInteger solves Java's built-in integer types have **fixed width**. An `int` is 32 bits and holds values from about -2.1 billion to +2.1 billion; a `long` is 64 bits and holds from about -9.2 quintillion to +9.2 quintillion (precisely -2^63 to 2^63-1). When a calculation produces a value outside that range, it **overflows**: the bits wrap around silently, with no exception. For example `Long.MAX_VALUE + 1` becomes `Long.MIN_VALUE` (a large negative number). This is a frequent source of subtle bugs in factorials, financial running totals, ID generators, and cryptography. **`BigInteger`** (in package `java.math`) is a class that represents a signed integer of *arbitrary precision* — meaning it can be as large as your computer's memory allows, with no fixed bit width. Internally it stores the number as an array of 32-bit machine words plus a sign, and grows that array as needed. So it simply cannot overflow; it just allocates more memory. ## Immutability — what it means and why it matters An object is **immutable** when its state cannot change after construction. `BigInteger` is immutable exactly like `String`. Every arithmetic method — `add`, `subtract`, `multiply`, `divide`, `mod`, `pow` — leaves the object it is called on untouched and **returns a new `BigInteger`** holding the answer. The classic beginner mistake: ```java BigInteger b = BigInteger.valueOf(10); b.add(BigInteger.ONE); // result is THROWN AWAY System.out.println(b); // still 10 ``` You must reassign: `b = b.add(BigInteger.ONE);`. Why immutability is a deliberate design choice: - **Thread safety for free** — because nothing can mutate an instance, it can be shared across threads without locks. - **Safe sharing / caching** — the JDK can hand out shared cached constants (`BigInteger.ZERO`, `ONE`, `TWO`, `TEN`) without fear a caller will alter them. - **Value semantics** — two equal values behave interchangeably. The trade-off is **allocation pressure**: a tight loop doing millions of `add`s creates millions of short-lived objects, which is slower and produces garbage. For hot loops over values that fit in 64 bits, `long` is far faster. ## How you create one - `BigInteger.valueOf(long v)` — from a primitive; uses a small internal cache for values -16..16. - `new BigInteger("123456789012345678901234567890")` — parse a base-10 string. - `new BigInteger("ff", 16)` — parse in another radix (here hexadecimal). - Constants: `BigInteger.ZERO`, `ONE`, `TWO`, `TEN`. ## Getting values back out `intValue()` / `longValue()` truncate to the low bits (lossy if it doesn't fit). `intValueExact()` / `longValueExact()` (Java 8+) throw `ArithmeticException` if the value won't fit — prefer these to catch overflow on the way out. `toString()` gives the decimal text; `toString(radix)` gives another base. ## When to use it Use `BigInteger` when correctness for large or unbounded values matters: factorials and combinatorics, cryptographic key material, hashing, exact accumulation that could exceed `long`, or interview problems like "multiply two huge numbers given as strings." Use `long` when values are bounded and performance matters.
- Why does immutability make BigInteger thread-safe?Because no method can change an existing instance, multiple threads can read and share the same object without locks or visibility hazards — there is no mutable state to race on.
- How do you safely convert a BigInteger back to a long?Use longValueExact(), which throws ArithmeticException if the value is too big to fit, rather than longValue(), which silently truncates the low 64 bits.
saying these in an interview costs you the question
- Thinking b.add(x) mutates b in place
- Believing BigInteger has a maximum value like long
- Using new BigInteger(String) for small literals instead of valueOf
- Assuming == compares values (use equals/compareTo)