Suppose you
want to generate random integers between zero and some upper bound. Faced with
this common task, many programmers would write a little method that looks
something like this:
private static final Random rnd = new Random();
// Common but deeply flawed!
static int random(int n) {
return Math.abs(rnd.nextInt()) % n;
}
This method
may look good, but it has three flaws. The first is that if n is a small power of two, the sequence of
random numbers it generates will repeat itself after a fairly short period. The
second flaw is that if n is not a power of two, some
numbers will, on average, be returned more frequently than others. If n is large, this effect can be quite
pronounced. This is graphically demonstrated by the following program, which
generates a million random numbers in a carefully chosen range and then prints
out how many of the numbers fell in the lower half of the
range:
public static void main(String[] args) {
int n = 2 * (Integer.MAX_VALUE / 3);
int low = 0;
for (int i = 0; i < 1000000; i++)
if (random(n) < n/2)
low++;
System.out.println(low);
}
If the random method worked properly, the program would
print a number close to half a million, but if you run it, you’ll find that it
prints a number close to 666,666. Two-thirds of the numbers generated by the random method fall in the lower half of its range!
The third flaw
in the random method is that
it can, on rare occasions, fail catastrophically, returning a number outside
the specified range. This is so because the method attempts to map the value
returned by rnd.nextInt() to a
non-negative int by calling Math.abs. If nextInt()
returns
Integer.MIN_VALUE, Math.abs will also return Integer.MIN_VALUE, and the remainder operator (%) will return a negative number, assuming n is not a power of two. This will almost
certainly cause your program to fail, and the failure may be difficult to
reproduce.
To write a
version of the random method that
corrects these three flaws, you’d have to know a fair amount about pseudorandom
number generators, number theory, and two’s complement arithmetic. Luckily, you
don’t have to do this—it’s been done for you. It’s called Random.nextInt(int), and it has been a part of the Java platform
since release 1.2.
By using a
standard library, you take advantage of the knowledge of the experts who wrote
it and the experience of those who used it before you.
A second
advantage of using the libraries is that you don’t have to waste your time
writing ad hoc solutions to problems that are only marginally related to your
work.
A third
advantage of using standard libraries is that their performance tends to
improve over time, with no effort on your part.
A final
advantage of using the standard libraries is that you place your code in the
mainstream. Such code is more easily readable, maintainable, and reusable by
the multitude of developers.
Numerous
features are added to the libraries in every major release, and it pays to keep
abreast of these
additions. Each time there is a major release of the Java platform, Sun
publishes a Web page describing its new features. These pages are well worth
reading [Java5-feat, Java6-feat]. The libraries are too big to study all the
documentation [JavaSE6], but every programmer should be familiar with the
contents of java.lang, java.util, and, to a lesser extent, java.io. Knowledge of other libraries can be
acquired on an as-needed basis.
To summarize,
don’t reinvent the wheel. If you need to do something that seems like it should
be reasonably common, there may already be a class in the libraries that does
what you want. If there is, use it; if you don’t know, check. Generally
speaking, library code is likely to be better than code that you’d write
yourself and is likely to improve over time. This is no reflection on your
abilities as a programmer. Economies of scale dictate that library code
receives far more attention than most developers could afford to devote to the
same functionality.
Reference: Effective Java 2nd Edition by Joshua Bloch