OptionalInt is a container that may or may not hold an int value. It's Java's way of explicitly representing the possibility of "no result" without using null or throwing exceptions — avoiding NullPointerException at the source.
It's part of a family of optional types in Java 8+:
| Class | Wraps |
|---|---|
OptionalInt | int |
OptionalLong | long |
OptionalDouble | double |
Optional<T> | Any object type |
The primitive-specific versions (OptionalInt, OptionalLong, OptionalDouble) exist for performance — they avoid boxing to wrapper types like Integer.
Core Methods
| Method | Description |
|---|---|
isPresent() | Returns true if a value is present |
getAsInt() | Returns the value; throws NoSuchElementException if empty |
orElse(int other) | Returns the value if present, otherwise other |
orElseGet(IntSupplier) | Returns the value if present, otherwise calls the supplier |
ifPresent(IntConsumer) | Runs an action if a value is present |
Basic Example
import java.util.Arrays;
import java.util.OptionalInt;
public class OptionalIntExample {
public static void main(String[] args) {
int[] numbers = {9, 10, 11, 12, 15, 25};
// reduce() returns OptionalInt because the array could be empty
OptionalInt first = Arrays.stream(numbers)
.reduce((left, right) -> left);
if (first.isPresent()) {
System.out.println("First element: " + first.getAsInt()); // 9
}
}
}
Using orElse() and orElseGet()
orElse() is cleaner than an if/else block for providing a default:
int[] numbers = {9, 10, 11, 12};
int[] empty = {};
OptionalInt result = Arrays.stream(numbers).filter(n -> n > 20).findFirst();
System.out.println(result.orElse(-1)); // -1 (no element > 20)
OptionalInt maxVal = Arrays.stream(empty).max();
System.out.println(maxVal.orElse(0)); // 0 (stream was empty)
OptionalInt from Stream Operations
Many IntStream terminal operations return OptionalInt because the stream may be empty:
int[] data = {3, 7, 2, 9, 4};
OptionalInt max = Arrays.stream(data).max();
OptionalInt min = Arrays.stream(data).min();
OptionalInt any = Arrays.stream(data).filter(n -> n > 5).findAny();
max.ifPresent(v -> System.out.println("Max: " + v)); // Max: 9
min.ifPresent(v -> System.out.println("Min: " + v)); // Min: 2
any.ifPresent(v -> System.out.println("Found: " + v)); // Found: 7
OptionalInt vs Optional<Integer>
You might wonder why not just use Optional<Integer>. The difference is boxing:
// OptionalInt — no boxing, primitive int stored directly
OptionalInt a = OptionalInt.of(42);
// Optional<Integer> — boxes int to Integer object
Optional<Integer> b = Optional.of(42);
For stream operations on int arrays and IntStream, use OptionalInt. For collections of Integer objects, use Optional<Integer>. Prefer OptionalInt whenever you're working with primitives to avoid unnecessary heap allocation.
isEmpty() — Java 11+
Java 11 added isEmpty() as the logical complement of isPresent():
OptionalInt result = Arrays.stream(new int[]{}).max();
if (result.isEmpty()) {
System.out.println("No values in stream"); // prints this
}
This is purely a readability improvement. result.isEmpty() is equivalent to !result.isPresent(). Use whichever reads more naturally in context — conditions like "if we got nothing, log a warning" read better with isEmpty().
stream() — Java 9+
OptionalInt.stream() returns an IntStream containing one value if present, or an empty stream if absent. This is useful for flatMapping:
int[] arrays = {3, 1, 4};
// Each OptionalInt gets converted to a 0-or-1-element stream
IntStream combined = Arrays.stream(arrays)
.filter(n -> n > 2)
.findFirst()
.stream(); // either stream of [3] or empty stream
combined.forEach(System.out::println); // 3
Common Pitfall: Calling getAsInt() Without Checking
The most frequent mistake with OptionalInt is calling getAsInt() unconditionally:
// WRONG — throws NoSuchElementException if stream is empty
int max = Arrays.stream(new int[]{}).max().getAsInt();
// CORRECT — always provide a fallback
int max = Arrays.stream(new int[]{}).max().orElse(Integer.MIN_VALUE);
// ALSO CORRECT — check first
OptionalInt maxOpt = Arrays.stream(new int[]{}).max();
if (maxOpt.isPresent()) {
System.out.println(maxOpt.getAsInt());
}
Only call getAsInt() when you have a guarantee the stream is non-empty, or after an isPresent() check.
When NOT to Use OptionalInt
While OptionalInt is useful as a method return type, avoid using it in these situations:
- As a method parameter — callers should pass an
intor use overloading; making callers wrap values inOptionalIntjust to pass them is awkward - As an instance field — use
nullor a sentinel value for nullable fields;OptionalIntfields add memory overhead with little benefit - Inside collections — a
List<OptionalInt>is almost always a design mistake; filter out missing values before collecting instead
OptionalInt is designed specifically for method return values where "no result" is a normal, expected outcome — like stream terminal operations on a potentially empty stream.
Summary
OptionalInt makes "no value" an explicit part of your API rather than something a caller has to guess at. Use orElse() for a concise default, isPresent() / isEmpty() for conditional logic, and avoid calling getAsInt() without a guard. Reserve it for method return values — not fields or parameters.