Você está na página 1de 7

J D C

T E C H

T I P S
TIPS, TECHNIQUES, AND SAMPLE CODE

WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips,


September 25, 2001. This issue covers:
* Generating Integer Random Numbers
* Displaying Element-Level Tool Tips for Swing
Components
These tips were developed using Java(tm) 2 SDK, Standard Edition,
v 1.3.
This issue of the JDC Tech Tips is written by John Zukowski,
president of JZ Ventures, Inc. (http://www.jzventures.com).
You can view this issue of the Tech Tips on the Web at
http://java.sun.com/jdc/JDCTechTips/2001/tt0925.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GENERATING INTEGER RANDOM NUMBERS
There are many ways to generate random numbers with the base
libraries found in the Java 2 SDK, Standard Edition. If you
haven't kept up to date with changes to the libraries, you might
be using an inefficient mechanism or, possibly worse, getting
results that are not uniformly distributed. Here's a look at a
good way to generate integer random numbers, and then a look at
what's not so good about some other ways.
The java.util.Random class has been available for generating
random numbers since the initial release of the system libraries.
Starting with the 1.2 release of the Java 2 SDK, Standard
Edition, the Random class has a nextInt() method that accepts an
integer argument:
public int nextInt(int n)
Given some value n, nextInt(n) returns a value greater than or
equal to zero but less then that value: 0 <= nextInt(n) < n.
All you have to do is create a Random object first, then call
nextInt(n) to return the next random int value.
Here's a demonstration. The following code snippet generates
a large set of random numbers and prints out the average:
int count = 1000000;
int range = Integer.MAX_VALUE / 3 * 2;
double sum = 0;
Random rand = new Random();
for (int i=0; i<count; i++) {
sum += rand.nextInt(range);
}
System.out.println(sum/count);
After looping a million times, the average value should be

approximately at the midpoint of the selected range.


So far, that isn't too complicated, but what's special about
using nextInt(n)? Why is using nextInt(n) better than (1) using
the older nextInt() method, with no range, then (2) using
Math.abs() to get the absolute value, and then (3) using the
mod (%) operator to get the value into the right range? The
latter approach is demonstrated in the following code snippet.
While there are a few extra operations in this approach, isn't
it just as good as the first approach?
sum = 0;
for (int i=0; i<count; i++) {
sum += Math.abs(rand.nextInt()) % range;
}
System.out.println(sum/count);
There are actually three problems with the latter approach.
First, nextInt() is equally likely to return a value between
Integer.MIN_VALUE and Integer.MAX_VALUE. If you take the absolute
value of Integer.MIN_VALUE, the result is not a positive number.
In fact, the result of Math.abs(Integer.MIN_VALUE) is
Integer.MIN_VALUE. So, on rare occasions, you'll get back a
negative number. Given the rarity of the event, 1 out of
2^31 times, the likelihood of the event happening during testing,
and being repeatable, is highly unlikely.
Second, when you mod (%) the results of nextInt(), you
effectively reduce the randomness of the results. The low order
bits of random numbers can repeat more regularly than the entire
number. This is a known issue with pseudo-random number
generators, and so it's another reason not to use mod (%).
Finally, and possibly worst of all, the results are not evenly
distributed. If you execute the loops in the two approaches, the
first loop will produce results above 715 million, with a
midpoint of the range at 715,827,882. That is within an
acceptable tolerance for randomness. Surprisingly, the results of
the second loop are consistently less than 600,000,000.
How can the second loop be so far off base? Essentially, the
problem is that the mapping is unfair. When you use the mod (%)
operator, you are taking values that are too large and squeezing
them into the low end. This favors the lesser values. Compare
this to rolling a single die with three other friends. Because
there are only four of you and six possible values, the mapping
for the first four values is easy. Die value 1 is mapped to
person 1, value 2 to person 2, and so on. Now what about the
larger values, 5 and 6? If you take the same approach as the
mod (%) operator, you map the large values in such a way that
any time a 5 is rolled, person 1 wins, and any time a 6 is rolled,
person 2 wins. Is this fair? Well, that's what happens when you
mod (%) the results of nextInt().
Using the nextInt(range) method solves all three of these problems.
That leaves the random() method of the Math class as another
possible way to generate random numbers. Using that method can't
be that bad, can it? You just have to multiply the result by the

range:
sum = 0;
for (int i=0; i<count; i++) {
sum += (int)(Math.random() * range);
}
System.out.println(sum/count);
Well, using this method at least doesn't have any of the problems
of nextInt(). You can't get a negative number back, you are not
using the mod (%) operator so you don't run into the low-order
byte range problem. And the range is uniform.
What is the problem, though, is that Math.random() uses floating
point arithmetic, and both versions of nextInt() work with only
integer operations. Math.random() can be up to four times slower
because of its use of floating point operations. Throw in the
cast, and the operation is even slower.
Because it avoids the problems inherent in using the other
approaches, using the nextInt(range) method of Random is a better
way to generate integer random numbers.
Here's a complete program you can use to test the different
approaches discussed in this tip.
import java.util.*;
import java.text.*;
public class RandomTest {
public static void main(String args[]) {
NumberFormat nf = NumberFormat.getInstance();
int count = 1000000;
int range = Integer.MAX_VALUE / 3 * 2;
System.out.println("Midpoint: " +
nf.format(range/2));
double sum = 0;
Random rand = new Random();
for (int i=0; i<count; i++) {
sum += rand.nextInt(range);
}
System.out.println("Good
: " +
nf.format(sum/count));
sum = 0;
for (int i=0; i<count; i++) {
sum += Math.abs(rand.nextInt()) % range;
}
System.out.println("Bad
: " +
nf.format(sum/count));
sum = 0;
for (int i=0; i<count; i++) {
sum += (int)(Math.random() * range);
}
System.out.println("Longer : " +
nf.format(sum/count));
}
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DISPLAYING ELEMENT-LEVEL TOOL TIPS FOR SWING COMPONENTS

Tool tips are those little popup windows that appear when you
rest your mouse pointer over a control such as a button or list.
All Swing components support tool tips. To establish a tool tip
for a control, call setToolTipText() for the component. When a
user rests the mouse pointer over the control, the tool tip
automatically appears. Typically, you want one text string
for an entire component. But there can be times when you want
the text to be more flexible. For instance, in the case of a
JList, JTree, or JTable component, you might want to display tool
tip text based on which list entry, tree node, or table cell the
mouse pointer is over. How can you customize the text?
There are two ways you can create custom tool tips for these Swing
components. You can customize the control and override the
public String getToolTipText(MouseEvent event) method, or you can
customize the renderer for the control and let the renderer deal
with the tool tips.
If you decide to override the getToolTipText() method, you have
to understand that the passed-in MouseEvent contains the mouse
coordinates over the control. It is your responsibility to
determine what element the cursor is on. Then, you have to look
up the appropriate tool tip text for the element and return that
text. You must then manually register the component with the
ToolTipManager. If you called the setToolTipText() method for the
control, a call to the registerComponent() method of
ToolTipManager would have been done for you, but because you
don't need to call setToolTipText(), you must do the registration
yourself.
Here's an example that demonstrates this first approach. The
example uses the keys for the system properties (such as
java.runtime.name) as the list entries. The example uses the
property values (such as Java 2, Runtime Environment, Standard
Edition) as the tool tip text for those entries.
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;

public class ListTest extends JList {


DefaultListModel model;
Properties tipProps;
public ListTest(Properties props) {
model = new DefaultListModel();
setModel(model);
ToolTipManager.sharedInstance().registerComponent(
this);
tipProps = props;
Enumeration enum = props.propertyNames();
while (enum.hasMoreElements()) {
model.addElement(enum.nextElement());
}
}
public String getToolTipText(MouseEvent event) {

Point p = event.getPoint();
int location = locationToIndex(p);
String key = (String)model.getElementAt(
location);
String tip = tipProps.getProperty(key);
return tip;
}
public static void main (String args[]) {
JFrame frame = new JFrame("Custom Tips");
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
Properties props = System.getProperties();
ListTest list = new ListTest(props);
JScrollPane scrollPane = new JScrollPane(list);
frame.getContentPane().add(scrollPane);
frame.setSize(300, 300);
frame.show();
}
}
If you decide instead to customize the cell renderer, you have to
do a little more work at first, but you don't have to determine
what element is under the mouse at run time. If you find that the
tool tips are hardly used, though, this extra work might not be
necessary. That's because getting cell renderers happens
frequently, and the tool tip setup happens every time the cell is
rendered. It might be better to use the first mechanism.
Essentially, what it involves is calling the setToolTipText()
method for the cell renderer.
Here's an example that demonstrates the cell renderer approach.
The example is taken from the book "John Zukowski's Definitive
Guide to Swing for Java 2, Second Edition" published by Apress
(http://www.apress.com/catalog/book/189311578X/).
First the renderer: this code essentially reuses a
DefaultTreeCellRenderer as the renderer, setting its tip text.
import
import
import
import

javax.swing.*;
javax.swing.tree.*;
java.awt.*;
java.util.*;

public class ToolTipTreeCellRenderer


implements TreeCellRenderer {
DefaultTreeCellRenderer renderer =
new DefaultTreeCellRenderer();
Dictionary tipTable;
public ToolTipTreeCellRenderer (
Dictionary tipTable) {
this.tipTable = tipTable;
}
public Component getTreeCellRendererComponent(
JTree tree, Object value, boolean selected,
boolean expanded, boolean leaf, int row,
boolean hasFocus) {
renderer.getTreeCellRendererComponent(
tree, value, selected, expanded, leaf, row,

hasFocus);
if (value != null) {
Object tipKey;
if (
value instanceof DefaultMutableTreeNode) {
tipKey =
((DefaultMutableTreeNode)value).getUserObject();
} else {
tipKey = tree.convertValueToText(value,
selected, expanded, leaf, row, hasFocus);
}
Object tip = tipTable.get(tipKey);
if (tip != null) {
renderer.setToolTipText(tip.toString());
} else {
renderer.setToolTipText(null);
}
}
return renderer;
}
}
The test program then just registers the tree with the
ToolTipManager and registers the renderer. There is no need to
subclass JTree. Like the JList example, the system properties are
used for the component elements. In this case, the tree is only
one level deep.
import
import
import
import

javax.swing.*;
javax.swing.tree.*;
java.awt.*;
java.util.*;

public class TreeTips {


public static void main(String args[]) {
JFrame frame = new JFrame("Custom Tree Tips");
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
Properties props = System.getProperties();
JTree tree = new JTree(props);
ToolTipManager.sharedInstance().registerComponent(
tree);
TreeCellRenderer renderer =
new ToolTipTreeCellRenderer(props);
tree.setCellRenderer(renderer);
JScrollPane scrollPane = new JScrollPane(tree);
frame.getContentPane().add(
scrollPane, BorderLayout.CENTER);
frame.setSize(300, 150);
frame.setVisible(true);
}
}
. . . . . . . . . . . . . . . . . . . . . . .
IMPORTANT: Please read our Terms of Use and Privacy policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
* FEEDBACK

Comments? Send your feedback on the JDC Tech Tips to:


jdc-webmaster@sun.com
* SUBSCRIBE/UNSUBSCRIBE
- To subscribe, go to the subscriptions page,
(http://developer.java.sun.com/subscription/), choose
the newsletters you want to subscribe to and click "Update".
- To unsubscribe, go to the subscriptions page,
(http://developer.java.sun.com/subscription/), uncheck the
appropriate checkbox, and click "Update".
- To use our one-click unsubscribe facility, see the link at
the end of this email:
- ARCHIVES
You'll find the JDC Tech Tips archives at:
http://java.sun.com/jdc/TechTips/index.html
- COPYRIGHT
Copyright 2001 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.
This document is protected by copyright. For more information, see:
http://java.sun.com/jdc/copyright.html
JDC Tech Tips
September 25, 2001
Sun, Sun Microsystems, Java, and Java Developer Connection are
trademarks or registered trademarks of Sun Microsystems, Inc. in
the United States and other countries.

Você também pode gostar