Keep Concerns Separated
Your code structure conflates several things:
- Generating the Fibonacci sequence
- Checking if a number is prime or not
- Printing the numbers of interest
It's best to use a structure that keeps these concerns separated. You can then arrange them in a multitude of ways to accomplish different tasks.
For example, suppose you want to find the squares of Fibonacci primes, or you want to place the primes you find in a database so you can retrieve them later. Also, others have suggested algorithmic changes. All these changes will be easier to implement if your code is modular. But, more importantly, it allows the different parts to be reused.
A Functional Approach
In this particular case, I think you would benefit from a paradigm shift from object-oriented programming to functional programming. There are varying definitions of functional programming. For our purposes here, we'll simply say that functional programming deals with the abstraction of composing functions (in contrast to the object-oriented approach, which deals with the abstraction of composing objects).
Note that this is purely a shift in our way of thinking about the problem. You can express any "object oriented" program in terms of functions and vice versa -- loosely speaking, objects and functions are isomorphic.
Here's how we can decompose this problem using functional principles:
- Generate an "infinite" stream of Fibonacci numbers
- Filter the stream of Fibonacci numbers to include only primes
- Print numbers from the filtered stream
You could implement all this yourself (c.f., the NumberCheck
interface in Tim's answer and Java 8's Predicate
interface, used below). However, we don't have to re-invent the wheel. There are multiple libraries for functional programming in Java, and Java 8 has added functional APIs to the standard. Here's one way we to map this to the functional programming abstractions provided in Java 8:
- Implement the Fibonacci sequence as a
Stream
.
filter
the Fibonacci stream with a prime check Predicate
, to generate a new stream with only Fibonacci primes
- Print each number in the stream of Fibonacci primes
Dealing with Large Numbers
The numbers of the Fibonacci sequence grow exponentially. It will not take long before long
values are insufficient to compute Fibonacci numbers. To compute larger numbers, you will need to use an arbitrary-precision number type such as BigInteger
.
Algorithmic Improvements
There are several ways in which you can perform the computation more efficiently:
- Your prime checker requires \$O(\sqrt{n})\$ time, where \$n\$ is the number being checked. In the overall algorithm, though, \$n\$ grows exponentially. So checking the \$k\$th fibonacci number takes \$O(2^n)\$ time. Using a sieve, as suggested by vnp, will help reduce the constant factor, but will not help with the asymptotic complexity. Ultimately, you will have to resort to a probabilistic prime check as suggested by Danaj.
As both vnp and Danaj point out, the \$k\$th Fibonacci number cannot be prime if \$k\$ is not also prime (excluding \$k = 4\$). Thus, we can structure our program as follows (modulo the case when \$k = 4\$ -- I leave that as an exercise for the reader):
- Generate a
Stream
of natural numbers
- Filter the natural numbers, producing a
Stream
of primes
map
each prime \$k\$ to the \$k\$th Fibonacci number
- Filter this stream once more for primes, producing a
Stream
of Fibonacci primes
- Print the Fibonacci primes
Note how we only need to change the way we generate Fibonacci numbers to compute the \$k\$th Fibonacci number directly instead of generating a Stream
of the Fibonacci sequence. The rest is just making a few tweaks to the way we compose our pipeline of functions. Regardless of paradigm, this is the sort of modularity sought by good engineering principles.
- Since we are now calculating the \$k\$th Fibonacci number directly, we can use an \$O(\log n)\$ algorithm to calculate the Fibonacci numbers. See, e.g., matrix form and SICP Ex. 1.19.