primality testing
Java programming
optimization techniques
algorithm efficiency
computational mathematics

What would be the fastest method to test for primality in Java?

Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms

Introduction

The fastest way to test primality in Java depends on the size of the number and whether you need a deterministic answer. For ordinary int or long values, optimized trial division is often enough. For very large numbers, BigInteger.isProbablePrime() is usually the practical answer.

Pick the Algorithm Based on Number Size

There is no single best primality test for every use case:

  • small int and long: optimized trial division is simple and fast
  • large arbitrary-precision values: probabilistic tests such as Miller-Rabin are better
  • cryptographic workflows: BigInteger.isProbablePrime() is usually what you want

If someone asks for the "fastest" method without specifying number size, the honest answer is: use the simplest deterministic method for primitive integers, and use BigInteger.isProbablePrime() for big numbers.

Optimized Trial Division for long

For primitive numbers, the first improvements are:

  • reject numbers less than 2
  • handle 2 and 3 directly
  • reject even numbers
  • test divisors only up to the square root
  • skip multiples of 2 and 3

That leads to the well-known 6k +/- 1 pattern:

java
1public static boolean isPrime(long n) {
2    if (n < 2) return false;
3    if (n == 2 || n == 3) return true;
4    if (n % 2 == 0 || n % 3 == 0) return false;
5
6    for (long i = 5; i * i <= n; i += 6) {
7        if (n % i == 0 || n % (i + 2) == 0) {
8            return false;
9        }
10    }
11    return true;
12}

This is a strong default for many tasks involving int or moderate long values.

Why the 6k +/- 1 Trick Helps

After eliminating divisibility by 2 and 3, any remaining prime greater than 3 must be of the form 6k - 1 or 6k + 1. That does not mean every such number is prime. It means all other candidates can be skipped safely.

So instead of checking every odd number:

java
for (long i = 3; i * i <= n; i += 2) {
    if (n % i == 0) return false;
}

you check fewer divisors. The asymptotic complexity is the same, but the constant factor is better.

Be Careful with Overflow in the Loop Condition

The condition i * i <= n is common and efficient, but it can overflow for very large long values. A safer version is:

java
1for (long i = 5; i <= n / i; i += 6) {
2    if (n % i == 0 || n % (i + 2) == 0) {
3        return false;
4    }
5}

That avoids multiplication overflow while preserving the same logic.

For Very Large Numbers, Use BigInteger

If you are testing numbers larger than long, Java's BigInteger class already provides a high-quality primality check:

java
1import java.math.BigInteger;
2
3BigInteger value = new BigInteger("32416190071");
4boolean probablyPrime = value.isProbablePrime(20);
5System.out.println(probablyPrime);

The argument to isProbablePrime() is the certainty. A higher certainty reduces the probability of a false positive. In practice, the method is fast and reliable enough for most real-world uses, including many cryptographic workflows.

Why BigInteger.isProbablePrime() Is Usually the Practical Winner

If you are dealing with arbitrary-precision values, writing your own primality test is usually the wrong tradeoff unless the task is educational. The JDK implementation is optimized and more appropriate than applying naive trial division to large values.

For example, this is the right style:

java
public static boolean isProbablyPrime(String value) {
    return new BigInteger(value).isProbablePrime(30);
}

Not this:

java
// Do not do this for huge numbers.
// Trial division becomes far too slow.

Deterministic Versus Probabilistic

For primitive values, the long method above is deterministic. For BigInteger.isProbablePrime(), the result is probabilistic, but the error probability is extremely small when certainty is chosen sensibly.

That is an important distinction:

  • deterministic means guaranteed correct
  • probabilistic means "prime" is reported with high confidence

For many applications, especially outside formal proof contexts, probabilistic is entirely sufficient.

What About Sieve Algorithms

If you need to test many numbers in a range, a sieve can beat repeated standalone primality checks. For example, generating all primes up to a limit is better done with the Sieve of Eratosthenes than by calling isPrime() for every candidate.

But that solves a different problem. If the task is checking one number at a time, a sieve is usually not the right answer.

A Small Benchmark Harness

If performance matters in your application, measure it on your input sizes:

java
1public static void main(String[] args) {
2    long start = System.nanoTime();
3    boolean result = isPrime(2_147_483_647L);
4    long elapsed = System.nanoTime() - start;
5
6    System.out.println(result);
7    System.out.println("Elapsed ns: " + elapsed);
8}

Benchmarks matter because the "fastest" algorithm on paper may not be the best fit for your exact number sizes and workload shape.

Common Pitfalls

  • Using naive trial division for very large values when BigInteger.isProbablePrime() is the right tool.
  • Forgetting early checks for numbers less than 2, even numbers, and divisibility by 3.
  • Writing i * i <= n without considering overflow for large long values.
  • Confusing the problem of testing one number with the problem of generating many primes in a range.
  • Assuming probabilistic primality checks are unacceptable in cases where they are actually the standard practical choice.

Summary

  • For int and many long values, optimized trial division with the 6k +/- 1 pattern is a strong deterministic method.
  • For arbitrary-precision values, BigInteger.isProbablePrime() is usually the fastest practical option.
  • The best method depends on input size and whether you need deterministic or probabilistic results.
  • Benchmark on realistic inputs instead of relying on generic claims about speed.
  • Choose a sieve only when the task is generating many primes, not testing one number at a time.

Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms