Datatype improvements
Of all the available Collection
implementations, you chose to use LinkedList
for your ll
variable (by the way, candidatePrimes
would be a better name). As you only use the add(Object)
and the remove(Object)
method, any Collection
will do. So, you have the freedom to select one of (to name a few popular classes)
ArrayList
LinkedList
HashSet
TreeSet
Your program makes heavy use of the remove()
method. While the act of removing an entry from a linked list is cheap, finding the place in the list where this is to happen is a costly operation (proportional to the current length of the list).
If you profile your application, the tool will most probably point out the remove()
calls to be the bottleneck.
A Collection
better suited to your usage pattern would be a HashSet
, as it can remove an element in mostly constant time.
You might want to experiment with other Collection
implementations as well, but I guess HashSet
will turn out to be the winner.
Your basic decision was to maintain a collection of the candidate primes, shrinking during the process. The more classical approach is to have a fixed-size boolean[]
array where true
at index n
means that n
is a (candidate) prime number. That makes the Sieve process faster as indexed array access is a very fast operation (faster than any remove()
can ever be), but makes collecting the results more complicated and probably slower.
Algorithmic improvements
You should adjust your loops.
You can stop the inner loop earlier. As you're only interested in numbers up to 10000, there's no need to remove higher numbers from your collection. Your current code will iterate both i
and j
up to 4999, leading up to a product of 24990001. Instead, you can leave the inner loop as soon as the product exceeds 10000:
for (int j = 2; i*j <= 10000; j++)
As Andrei showed in his answer, the inner loop also need not start with 2. It's enough to have it start with i, as cases with e.g. i=17
and j=3
have already been covered with i=3
and j=17
. So, it can read
for (int j = i; i*j <= 10000; j++)
The outer loop needs to count only up to the square root of the maximum number. Why? Inside your nested loops, you mark compound numbers as non-primes, with one factor being i
, and the other being j
. If e.g. i=987
and j=97
, you'll mark the same number 96739
as non-prime that has already been covered with i=97
and j=987
. The square root is just the point where the second factor gets smaller than the first one.
You only need to enter the inner loop if i
is a prime (if i
is currently contained in your collection). Why? Take e.g. i=15
. It's the product of 3 and 5. So, any multiple of 15 is also a multiple of 3 (and of 5), and all multiples of 3 have already been eliminated in the i=3
iteration. So, checking the multiples of 15 is a waste of time.
As we already know, multiples of 2 aren't primes, and you can easily put that knowledge into your program by a more elaborate initialization of your collection. Replace
for(int i = 2; i< 10000; i++)
{
ll.add(i);
}
with
ll.add(2);
for(int i = 3; i< 10000; i+=2)
{
ll.add(i);
}
explicitly adding the known prime 2 and all odd numbers to the initial collection. This way, your initial collection has only half the size.
Accordingly, you can adjust the outer and the inner loop to just iterate over the odd numbers (as all the even non-primes have already been covered in the initialization).
Regarding
int y = i*i;
if(y<ls)
ll.remove(new Integer(y));
You can completely remove this code section. The resulting y
numbers have already been covered in the nested loops, whenever j=i
.
If you apply all these changes, you have to replace the collection-size-based limit ls
with one that directly represents the maximum number you're interested in.
Program structure
You're doing everything inside main()
. It's better to have calculations and input/output in separate places. You should introduce a method
private static Collection<Integer> findPrimes(int maxNumber) { ... }
where you put the Sieve algorithm, and call that from main()
where you do the results output and the timing.
And, please rename your class from LinkedListEx
to something like EratosthenesPrimes
. Don't name classes after implementation details, but after their purpose.