The sieve is optimal for what you try to do. In your code, the isPrime method is not efficient. An easy optimisation is to start your loop from 2 (1 is redundant) and finish your loop on \$\sqrt{n}\$ (this is well known in number theory). Also there is no need to use a list, simply return false when you detect a non prime in the loop. So the whole thing can take a few lines:
public static boolean isPrime(long ln) {
long end = (long)Math.sqrt(ln) + 1;
for (int i = 2; i < end; ++i) {
if (n % i == 0) return false;
}
return true;
}