|
hashCodes Uncovered by Corey McGlone In this edition of the JavaRanch Journal SCJP Tipline, author Corey McGlone investigates hashCodes, expanding on an April blog entry. What is a hashCode?First of all, what the heck are hashcodes for? Well, oddly enough, they're used heavily in Hashtables. Hashtables, along with the other classes that extend the Map interface, are discussed in this Journal article For the purposes of this article, I'm going to assume you know what a Hashtable is and how useful it can be. Needless to say, Hashtables can help you keep a large number of objects organized and allow you to access them very quickly. Of course, to do so, a Hashtable relies on the power of the hashCode method. In essence, when you invoke the get(Object o) method of a Hashtable, the Hashtable will use the hashCode method of the object you passed to it in order to access an object (or list of objects). As long as the hashCode method is working properly, everything works just fine. If it doesn't, however, you can have some rather serious problems. So, what makes a valid hashCode? Well, here's what is said about hashCodes in the API Specification for Object: The general contract of hashCode is: Study: A Proper hashCodeLet's start by looking at a good program. In this case, we've defined a new object, MyObject, which defines its own equals and hashCode methods. In general, it is good practice to define your own hashCode method any time you override equals (more about this later). Here it is:
Executing the above code leaves you with this output:
As you can see, we easily retrieved the objects we had originally put into the Hashtable and it took practically no time at all. How does our hashCode method do? Does it pass all 3 of the criteria laid out earlier? Let's look at each of the criteria one at a a time. 1. Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application. Does our hashCode meet that criteria? Does our hashCode continually return the same value (assuming that our variable, a, hasn't changed)? Certainly, it does - it returns the value of a. Okay, next criteria. 2. If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result. How about this one? Does our hashCode method still work here? Sure, it does. If two object have the same value for a, they will be equal (by the equals method). In such a situation, they would also return the same hashCode value. Our hashCode method works here. Okay, on to the final criteria. 3. It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables. Well, this isn't really a requirement at all - it's more of a suggestion, if anything. It is best if the hashCodes for unequal objects are different, but it's not required. We'll look at this a little more in a few minutes. So, there you have it - we've successfully overridden the hashCode method. So, how do you know when you should do such a thing? Well, in general, it's considered good practice to override hashCode any time you override equals. The reason for this is due to the default behavior of the equals and hashCode methods. When Should you Override hashCode()?In Java, the default equals method (as defined in Object) compares the two objects to see if they are, in fact, the same object. That implementation does not take into account any data that might be contained by the object. For example, had we not overridden the equals method in our class, MyObject, we'd see that the following code:
...would produce the output "false." The reason for this is that, even though the two objects contain the same data, they are different objects - two separate objects on the heap. Fortunately, we overrode the equals method and, given the MyObject class as defined originally, we'd get the output "true" from this example. However, what about the hashCode? Well, the default hashCode method works in a similar fashion to the default equals method. It converts the internal address of the object into an int and uses that as the hashCode. Well, that won't work so well here. We just defined a way in which two distinct objects (which will, necessarily, have distinct memory addresses) to be considered "equal." The default hashCode implementation, however, will return different hashCodes for the two objects. That violates the second rule defined above - any objects that are considered equal (by their equals method) must generate the same hashCode value. Therefore, whenever you override the equals method, you should also override the hashCode method. Study: Faulty hashCodesWhat happens if we override the hashCode method poorly? Let's violate one of the rules, shall we? Let's change our hashCode method to look like this:
This one actually violates a couple rules. Not only does it not guarantee that two objects that are equal have the same hashCode, it doesn't even guarantee that the same object will keep the same hashCode from one invocation to the next. Any idea what havoc this might wreak on a poor, unsuspecting Hashtable? If I execute the main method again, as I did before, I get this output:
Eek! Look at all of the objects I'm missing! Without a properly functioning hashCode function, our Hashtable can't do its job. Objects are being put into the Hashtable but we can't properly get at them because our hashCode is random. This is certainly not the way to go. If you were to run this, you might even get different output than I got! Even if the hashCode that is returned is always the same for a given object, we must ensure that the hashCodes that are returned for two objects that are equal are identical (Rule #2). Let's modify our MyObject class so that we hold true to Rule #1 but not to Rule #2. Below is the modified parts of our class:
Executing this code gives us some more disturbing results, although they may not appear that way at first. Here's my output:
So what's wrong with that, you ask? Well, what should the put method do? If you first put an object into a Hashtable using a specific key and then put a new value into the Hashtable using a key that is equal to that one, the original value should be replaced with this new one. That's not what's happening here. Instead, our Hashtable is treating our keys as if they're all unequal. Eek! This is the same result you could expect if you were to override equals without overriding hashCode. Here's the output we should get, assuming we have a good hashCode method:
Inefficient hashCodesOkay, one more thing to go over. What happens if we have a valid hashCode, but the values aren't very distinct. In this case, I'm going to hold to requirements 1 and 2, but I'm going to ignore requirement 3 entirely. Let's modify MyObject.hashCode() and our main method to look like this:
Note that this is a valid hashCode method. It always returns the same value for a given object, assuming that nothing used in the equals method changes (not that it cares anything about that). It also returns the same value for two objects that are "equal." What it doesn't do is return a different value for objects that are not equal. It always returns the same value. This is a valid hashCode but, as this example will show, an inefficient one. Executing this 5 times using this new hashCode method, I get this output:
That gives me an average time of 7140.6 - roughly 7 seconds. By executing my original hashCode method and execute the same main method, I got this output:
That's an average of about 16 millseconds - a far cry from the 7 seconds we saw earlier! We're seeing a dramatic increase in the amount of time required to retrieve objects from our Hashtable using the poor hashCode method. The reason for this is that, with every key object having the same hashCode, our Hashtable has no choice but to index every value under the same hashCode. In such a case, we've got everything in one long list and our Hashtable does us no good at all - we might as well have stored the objects in an ArrayList. SummaryHopefully you see the importance of creating a valid hashCode for any objects you're using. Whenever creating your own hashCode method, it's important to remember the 3 rules. I've listed them here in a summarized format; refer to the API Spec for full details.
With that knowledge, you should have a firm grasp on how to override hashCodes and make the most out of the Hashtable (and other Map classes) in Java. Remember to tune in to The SCJP Tipline for more updates. Return to Top |
||||||||||||||||||||||||||||||||||||||||||||||||||
LegoTM meets JavaTM : RoboJDE vs. LeJOSby Jessica Sant IntroductionI recently took a Robot Building Lab as part of my Master's degree program. The class was a mix of graduate and undergraduate Computer Science students. The majority of the course was taught using the HandyBoard controller programmed with a C derivative called IC4. Being the Java geek that I am, I was thrilled when my professor mentioned there was a Java product out there that you could use to program the HandyBoard. Enter RoboJDE. All the graduate students had an additional requirement to program an RCX robot using leJOS. This article is based on my experience of the two platforms. This is by no means a definitive comparison of the two platforms. Every language has its purpose, and this may have not been the ideal application of Java due to the extremely limited memory on the platforms (~12k for the JVM, program, and the execution space), but trying to code java small was a very interesting (albeit sometimes frustrating) exercise. Plus, I always get a kick out of writing a program that "does something cool". And getting a robot to do stuff like following a flash light, avoiding obstacles it encounters, and determining where it is along a wall based on what it sees with its sonar definitely qualify as "cool". HighlightsRoboJDE and leJOS both have strong aspects to their platform. Some highlights include: leJOS's built in RotationNavigator that performs both forward and inverse kinematics (basically knowing where it is on a 1' x 1' grid and the ability to go to a particular point on that grid); and RoboJDE's sensor interaction that better encapsulates the behavior of different sensors. Because both platforms are Java-based it is possible to create stand-alone programs in order to test the needed functionality off-line and without the use of the actual robot. Each of these aspects proved quite helpful in the respective labs.Each platform has their issues... the main problem that sticks out for leJOS is its lack of Garbage Collection. The extremely limited memory is hard enough to deal with, and not having the ability to free the memory after its been allocated makes the situation more challenging for significant programs. RoboJDE has a very efficient built-in garbage collector. The main issue with RoboJDE is that its only available on the Windows platform, leJOS is available on both Windows and Linux. Hardware comparison
I/O ports The Lego Mindstorm RCX board has a limited number of input and output ports (three each). However the ability to overlap sensors allows the programmer to use more sensors than there are ports available. Overlapping sensors has the side effect that the readings from the sensors can be received but it is often impossible to differentiate between which overlapped sensor is sending the signal. The HandyBoard had seven analog input ports, seven digital input ports and two digital input / output ports. The limited number of digital I/O ports became a problem during the 2nd half of the course because we needed to use two encoders (used with kinematics to determine where the robot is) and a sonar sensor. Each of these sensors required the use of an individual digital input/output port. The HandyBoard does not have the ability to overlap sensors, and so we were forced to use only one encoder along with the sonar sensor. *Note: as a work around we could have written native code to allow one of the encoders to run on a pure input port, but this proved too difficult given the time constraints and my pathetic understanding of the Motorola M68HC11 assembly language. LCD The RCX board has a severely limited display. It allows only a total of 5 characters to be shown on the board whereas the HandyBoard can display a total of 32 characters (2 lines, 16 characters in length). The ability for the HandyBoard to display a more significant output significantly aids in the process of debugging. Robot Interface The Handyboard interfaces with the PC through the serial port, which connects to a standard phone line, which connects to the robot. So, if you'd like to get debug information sent back to the PC during the robot's run, it must remain "tethered" via the phone cord. The RCX on the other hand uses a USB tower to send signals via IR (I believe the tower can also be connected to the PC via a serial port, but we didn't have the appropriate hardware in our kits). Whether it was due to the IR speed or the RCX loading software's speed, the RCX took a significantly longer amount of time to download programs to the robot than the HandyBoard software. The nice advantage about the built-in IR capabilities of the RCX robots is that it doesn't need to be tethered in order to send debug information back to the PC. Software comparisonThe ToolsRoboJDE is a commercial product that compiles code for the Handyboard. It has a more elegant user interface and cleaner out-of-the-box experience than the UI used with leJOS. RoboJDE runs only on windows, but the installation is much cleaner, requiring only the execution of a simple setup executable. The UI itself produces a significant amount of statistics about the user's program during compilation (program size, number of static variables, number of classes used, etc) and execution (free bytes available, free blocks available, % CPU used, % memory used). This information proved quite useful while coding and debugging the HandyBoard. leJOS is an open-source product and thus has several available interfaces that can be used to compile and download programs. Bricxcc required several products to be installed as well as multiple environment variables to be set, and I was unable to configure it successfully. Instead, I used RCXDownload, which requires only that the JDK and the USB Tower driver be installed. It provides none of the helpful statistics provided by RoboJDE; it is simply a way to compile and download the leJOS programs. The API Both RoboJDE and leJOS are Java-based platforms, but they differ in the amount of the standard Java API they implement and how they interact with the Robot.
Robot Interaction One of the significant comparisons that need to be made between RoboJDE and leJOS is how easily the two interact with and control the robot. RoboJDE provides specialized classes to handle a select set of sensors. These classes encapsulate all the functionality of the particular sensor. For example, the conversion of a sonar reading into a distance (see Figure 1) is handled by the RangeFinder class. If necessary, this class can also be extended to support different brands of Range Finders other than the DevantechSRF04. Instead of providing specialized classes that handle different sensors, leJOS provides a generic Sensor class which, when initialized with different "Type and Modes", will behave differently. These types can range anywhere from TOUCH to TEMP or LIGHT. I find that RoboJDE's method of interacting with sensors is more intuitive and easier to implement than the one used by leJOS.
Both RoboJDE and leJOS have a similar method of interacting with the Motors (see Figure 2). With both API's, you set the power levels of the motors their directions. Two very useful features that leJOS possesses that are lacking in RoboJDE are the methods Motor.X.isMoving() and Motor.X.flt(). These methods tell the program if the motor is currently being moved, and tells the motor to stop applying power to the motor, respectively. The .flt() command is different than stop in that it removes power, rather than bringing the motor to an abrupt stop.
Kinematics LeJOS makes kinematics almost a trivial task. LeJOS provides a class called RotationNavigator that uses two encoders to calculate the distance the robot has traveled, and in what direction. The RotationNavigator performs the desired move by using commands such as RotationNavigator.gotoPoint() (inverse kinematics). It also can calculate where the robot is currently located by using commands such as RotationNavigator.getX() and .getY() (hence forward kinematics). When using RoboJDE, all of the functionality provided by a class such as RotationNavigator needed to be designed and coded. Granted, having to code this myself gave me a renewed appreciation for high school geometry as well as a better understanding of how the kinematics calculations were actually performed. Bump detection Due to the limited number of input ports available on the RCX, the two bump sensors were stacked on a single input port and it was not possible to determine if the bump occurred on the left or right side of the robot. So, when avoiding a bump the robot always attempted to avoid the obstacle by turning to the right. This caused a problem sometimes because the robot would take several tries to completely avoid an obstacle, which would in turn cause the robot to hit the obstacle more, and possibly collect more error in the calculation of its position.
Debugging Debugging the robot's program for logical errors is a much easier task in the HandyBoard due in large part to the RoboJDE UI as well as the large LCD on the HandyBoard. Error messages, variable values and other helpful debugging statements can easily be sent to the host machine (and simultaneously displayed on the LCD) by simply calling System.out.println(). As mentioned before, the RoboJDE interface that tracks the amount of memory and CPU used as well as program size etc is incredibly helpful when debugging. This type of helpful debugging is much more involved with the Mindstorm. There is no simple way to print to the host, and the limited size of the LCD prevents much useful information from being displayed. We found that "debugging" is often reduced to adding different sounds to be played at various points in the program and trying to interpret these sounds to mean that the program has successfully reached a particular area in the code. *In all fairness to the RCX and leJOS, because it has an IR connection to the PC, you can send message through the IR in order to debug problems -- but I didn't realize this at the time I was programming it. Garbage Collection One significant advantage that RoboJDE has over leJOS is its automatic Garbage Collection. When an object or variable is no longer used it is removed and the memory is freed in order to be used by other variables. In leJOS the programmer must be diligent and not produce more objects than necessary because there is no way to reclaim memory once it is used. The lack of a Garbage Collector is a huge detriment due to the extremely small amount of memory on the RCX. RoboJDE is also very efficient when it compiles your program. Any methods or classes not used will not be compiled and built into byte code for the HandyBoard. This also aids in significantly reducing the size of your program. Other Stuff One significant advantage that leJOS has over RoboJDE is that it has a Persistent memory area that can be used to store calibration values from one program execution to the next. This functionality would have proven useful during the first half of the course when we were constantly calibrating and recalibrating the light and reflection sensors. If we could have calibrated the robot once and stored these values in a Persistent memory area, we could have saved time from one run to the next. Simulating the Robot Due to Java's object oriented nature, it was easy to encapsulate much of the functionality of the labs as individual classes that were not dependent on the Robot code itself. This quality became very useful when writing and testing some of the major pieces of the labs. For example, in one of the final labs a map of the world needed to be built and stored on the HandyBoard based on the Sensor inputs. We were able to hard-code a simulated set of sonar readings into a main class. These values were then manipulated and stored so that they could later be used to find an optimal path to the goal. The ability to do all of this off-line with the use of real debugging utilities such as those built into IntelliJ IDEA significantly reduced the number of logical errors present in the program once it was actually used by the Handyboard. What I thinkIn the end, I found that using and debugging RoboJDE was easier than leJOS. The UI provided by RoboJDE made it easier to identify memory problems as well as logical errors, and as I mentioned before, I found the API a bit more intuitive to use as well.All that being said, for the average robot geek (and particularly for the recreational robot geek), money is almost always an issue. LeJOS is a free open source product, and the Mindstorm itself costs about $200. The HandyBoard costs about $300, and RoboJDE is about $100 (there is a free Lite version available as well). So, for the casual geek, RoboJDE may not be the most cost-efficient path, but I believe for the academic arena, its certainly a strong choice. Links
Return to Top |
||||||||||||||||||||||||||||||||||||||||||||||||||
Whizlabs SCMAD Exam Simulator (CX-310-110) Reviewby Valentin Crettaz
When some big corporation's educational services announces the release of a new certification exam, you can be certain that the Whizlabs team won't be that far away. After many successful simulator releases in 2003 and before (SCBCD, SCJP 1.4, etc), Whizlabs now wants to make you MAD (that is, a Mobile Application Developer
Basically, the user interface hasn't change much compared to what I have experienced with other simulators from Whizlabs' product line. While some display bugs have been eliminated, a few ones are still on the edge eargerly waiting to be terminated. Furthermore, when looking back at Lasse Koskela's review of their SCJP 1.4 simulator last December, it appears that Whizlabs has done much of its homework:
Moreover, I have to admit that I have specially liked the way Drag-n-Drop questions have been implemented. They are even better than in the real exam since after you have answered a question you can reopen the Drag-n-Drop box without having to answer the question again. This is, in my opinion, one of the big limitations in Sun's certification exam software. Actually, as most of you, every time I take a Sun certification exam, I have to write down my answer to a Drag-n-Drop question before closing the dialog box, which is a little annoying. Regarding the content itself, you will find a total of 490 questions evenly distributed across 5 mock exams (68 questions each) and 1 quiz (150 questions). You can even create your own mock exams with the built-in test customizer that allows you to select any questions in the bank to help you focus on a specific area of your choice. Besides, all questions come with very exhaustive explanations and links to sections of the relevant specifications. Once you finish an exam you are offered the opportunity to review some answers you were unsure about or to directly go to the exam report and see if you have passed or failed. On the reports you will find a good deal of very comprehensive information. For each question, you will be able to see the objective to which the question relates, the level of difficulty, the type of the question (i.e., multiple choice, DnD, fill-in, etc) and whether you answered it correctly or not. Wait, that's not all!! The help menu will direct you to a complete user guide and some help content on how to use the tool itself. As always, Whizlabs has also bundled a Tips'N'Trick cheat sheet and quick revision notes for you to read just before taking the exam. On the downside, it's just unfortunate that you can't print out these helpful notes from the tool and take them with you to the test center to do some last minute brush up... Maybe, they'll consider adding that to future versions of their tools. Who knows... A cool new feature has been added to the simulator: If you ever have a doubt about a question or an explanation or if you want to report some problem you experienced, you can do it by clicking on the "Discuss it" button and you will be forwarded to a dedicated SCMAD forum on Whizlabs website. Once you have logged in, you will just have to write your message. Of course, a live Internet connection is required in order to take advantage of this feature. Finally, I think that this new exam simulator is well worth the money spent ($79.95 for new customers and $63.95 for existing ones) for the reasons enumerated below:
Don't hesitate to download the free trial edition to get a feeling of the exam simulator before buying it. I definitely recommend this outstanding resource to any respectful J2ME developer willing to tackle the SCMAD exam.
Resources
Return to Top |
||||||||||||||||||||||||||||||||||||||||||||||||||
The Coffee House |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
Solveig Haugland by Dirk Schreckmann Not yet posted. Check back soon. |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
Mosey on in and pull up a stool. The JavaRanch Big Moose Saloon is the place to get your Java questions answered. Our bartenders keep the peace, and folks are pretty friendly anyways, so don't be shy! Question: Use OOP to design database tables schema?Over in the OO, Patterns, UML and Refactoring forum, Edward Chen opened a bottle of worms with this thread starter:
Now, mosey on o'er, see what folks are saying and chime in with some thoughts of your own. Join this discussion in The Big Moose Saloon! Return to Top |
||||||||||||||||||||||||||||||||||||||||||||||||||
Discuss this book review in The Big Moose Saloon! Return to Top |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
Following are the scheduled book promotions coming in July and August. Be sure to check our book promotions page often for changes and new promotions.
Return to Top |
||||||||||||||||||||||||||||||||||||||||||||||||||