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+:

ClassWraps
OptionalIntint
OptionalLonglong
OptionalDoubledouble
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

MethodDescription
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 int or use overloading; making callers wrap values in OptionalInt just to pass them is awkward
  • As an instance field — use null or a sentinel value for nullable fields; OptionalInt fields 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.