equals and hashCode via syntactic-sugar (AKA JDK-1.5 features)

Some classes we make may require a proper hashCode and equals method written. Especially if they will be placed in a Collection. Before, when a person who was too “lazy” (or as I like to call it, being a genius) to manually write their own hashCode and equals for each class, they’d possibly look to a library like Commons Lang (CL). CL not only does a great job of modularizing the equals and hashCode generation, but it also follows the guidelines laid in that great book Effective Java. Ok, this is good. However, that’s one more library you need to add to your code, and in my opinion, for a fairly small benefit. I’m not a major proponent of recreating the wheel. However, if it’s just a matter of a few lines of code, I say go for it.

Take for instance, a Person class. A Person will have a name, weight, heightInInches, dateOfBirth and bloodType (an enum). First, let’s look at an example of the equals and hashCode for the Class.


public class Person {
    String name;
    int weight;
    float heightInInches;
    Date dateOfBirth;
    BloodType bloodType; // An Enum of 8 items: O(+/-), A(+/-), B(+/-) and AB(+/-)

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        final Person person = (Person) o;

        if (Float.compare(person.heightInInches, heightInInches) != 0) return false;
        if (weight != person.weight) return false;
        if (bloodType != person.bloodType) return false;
        if (dateOfBirth != null ? !dateOfBirth.equals(person.dateOfBirth) : person.dateOfBirth != null) return false;
        if (name != null ? !name.equals(person.name) : person.name != null) return false;

        return true;
    }

    public int hashCode() {
        int result;
        result = (name != null ? name.hashCode() : 0);
        result = 37 * result + weight;
        result = 37 * result + heightInInches != +0.0f ? Float.floatToIntBits(heightInInches) : 0;
        result = 37 * result + (dateOfBirth != null ? dateOfBirth.hashCode() : 0);
        result = 37 * result + (bloodType != null ? bloodType.hashCode() : 0);
        return result;
    }
}

Now this is functional, but I only have 5 fields and this code already looks unwieldy… I might be going too far with unwieldy, but you know what I mean. Now with CL’s EqualsBuilder and HashCodeBuilder, the code quickly becomes more compact.


...
    public boolean equals(Object o) {
        Person otherPerson = (Person) o;
        return new EqualsBuilder()
                .append(name, otherPerson.name)
                .append(weight, otherPerson.weight)
                .append(heightInInches, otherPerson.heightInInches)
                .append(dateOfBirth, otherPerson.dateOfBirth)
                .append(bloodType, otherPerson.bloodType)
                .isEquals();
    }

    public int hashCode() {
        return new HashCodeBuilder(3, 37)
                .append(name)
                .append(weight)
                .append(heightInInches)
                .append(dateOfBirth)
                .append(bloodType)
                .toHashCode();
    }

Now I could make the equals() and hashCode() one-liners, but I am not interested in using the reflection-based methods on EqualsBuilder and HashCodeBuilder. Primarily because they are slower because well, they rely on reflection. Also, they cause issues on JVMs with certain security restrictions. So, using Java 1.5’s varargs and implicit autoboxing I came up with the equals/hashCode below.


...
    public boolean equals(Object obj) {
        Person otherPerson = (Person) obj;
        return CommonUtil.produceEquals(name, otherPerson.name,
                weight, otherPerson.weight,
                heightInInches, otherPerson.heightInInches,
                dateOfBirth, otherPerson.dateOfBirth,
                bloodType, otherPerson.bloodType);
    }

    public int hashCode() {
        return CommonUtil.produceHashCode(name, weight, heightInInches, dateOfBirth, bloodType);
    }

// Here are the methods in CommonUtil...
    public static int produceHashCode(final Object... itemsToHash) {
        int result = 0;
        if (isNonEmpty(itemsToHash)) {
            for (Object item : itemsToHash) {
                result = 37 * result + (item != null ? item.hashCode() : 0);
            }
        }
        return result;
    }

    public static boolean produceEquals(final Object... itemsToCompare) {
        if (isNonEmpty(itemsToCompare)) {
            if (0 == itemsToCompare.length % 2) {
                for (int index = 0; index < itemsToCompare.length; index += 2) {
                    final Object itemA = itemsToCompare[index];
                    final Object itemB = itemsToCompare[index + 1];
                    if (null == itemA ? null != itemB : !itemA.equals(itemB)) return false;
                }
            } else {
                throw new IllegalArgumentException("produceEquals() expects an itemsToCompare array of even length.");
            }
        }
        return true;
    }

I then ran test on the 3 variations and the default equals/hashCode from Object as a control. The test generated 100,000 People and placed them in a HashSet. The test runs 5 iterations per variation, and collects the avg times and heap deltas as benchmarks. The results below did not really surprise me. I knew the default native hashCode, and equals this == obj would be the fastest. However, they would not always be correct. If using an ORM, such as Hibernate, proper equals/hashCode implementions would almost be required (assuming you use collections). I expected the CL variation to take more time and memory than the default and per-variant, but I did not expect the custom variation to take just as much memory as CL. I assumed, since a new EqualsBuilder and HashCodeBuilder was built per call, the custom version would be a clear victor. Yeah, not so much. So, it simply comes down to using CL’s or my custom versions based on nothing other than whether or no not to add a new dependency. No major findings, but in this case, I’ll build my own.

Default Equals/HashCode
The avg time to generate and Add 100000 People to a Set: 2222ms (2.2 seconds)
The avg heap delta to generate and Add 100000 People to a Set: 1337563 bytes (1.28 MBs)

Per-variant specific Equals/HashCode
The avg time to generate and Add 100000 People to a Set: 2253ms (2.3 seconds)
The avg heap delta to generate and Add 100000 People to a Set: 1304939 bytes (1.24 MBs)

Commons-Lang Equals/HashCode
The avg time to generate and Add 100000 People to a Set: 2334ms (2.3 seconds)
The avg heap delta to generate and Add 100000 People to a Set: 5345507 bytes (5.10 MBs)

Custom Equals/HashCode (autoboxing)
The avg time to generate and Add 100000 People to a Set: 2331ms (2.3 seconds)
The avg heap delta to generate and Add 100000 People to a Set: 5226531 bytes (4.98 MBs)

Posted in Technical stuff | 10 Comments

Funny story…

Yesterday afternoon, my company sent out yet another reminder for everyone to dress up. Ya know, throw on a nice(r) shirt, if ya can wear a tie. We had prospective clients coming by, and we wanted to impress them. Fine. Needless to say, I deleted the email and went on about my life. A co-worker asks me if I was going to dress up. I said no, and scoffed at the idea. I went on to propose that they would not bring these clients by the development area.

So, the next day I get up and dress as usual: khakis, a plaid/square pattern shirt. The usual rubbish. I walk into the building, and guess who I see coming out of the elevator? The CEO. He is a good 30 steps from me. Of course, within those 30 steps, he gives my outfit a quick once-over. He was clearly not please. Considering the many emails over the past 2-3 weeks, I can understand his disappointment. Ok, so the CEO saw me, and will assume that I am A) a Jerk, B) a Bastard, or C) all of the above (AKA: a Jerk Bastard).

Ok, so i jump into the elevator thinking, that’s the end of that. He saw me, and wasn’t pleased. Well, it wasn’t. Later that day, maybe two hours later, the CEO walks over to the freaking develoment area. Now, I think, no problem; just don’t make eye contact. Umm, well that would be nice if he was just stopping over to visually scold me, or punch me. No, it was more like he was walking through w/ the clients, and for some outrageous reason, they stopped. to make matters worse, they were all on my side of the area, and I happened to be the only person sitting on that side at the time. So basically, the clients see 99.9% of the rest of the company wearing suit jackets and ties, and this one Jerk Bastard.

Not a great start to the day, but interesting nonetheless. I would like to say lesson learned… but, umm, yeah, I wont.

Posted in General | 6 Comments

U.N. — Peaceful Force?

The Israeli-Hezbollah conflict has produced nothing but destruction and death for both sides. It would be pretentious to attempt an explanation of the conflict: too many facets beyond the understanding of even the conflict participants. Here’s one positive thing: the U.N.’s latest action. Sending a large troop and support presence (about 15,000).

U.N. peacekeepers, are less soliders and more security officers. Security officers that secure in this case, a buffer zone along the Israeli-Lebanese border. Logically, by securing this buffer zone from coninuing violence, they are “keeping the peace”. This is good, in the sense that there will be a body between these warring parties.

However, this is not enough. In order for mistakes like the current Iraq war (which sadly, I initially supported: I thought C. Powell knew what he was talking about). We need a stronger international body. One that doesnt allow members like the Syria to join the security council. One that could say to the U.S., hey let’s work this out together, and find a reasonable soultion.

A major reason why the current administration ignored the U.N. before the invasion of Iraq was the U.N.’s lack of credibility and true power. Of course, the unilateral move made the U.N. seem even weaker. It will take some work on the parts of the G8 members. See that group alone could make the world a much better place, but most of them (including the U.S.) have so many opposing interests that taint all initiatives they put forth. Well, until the U.N or at least its larger members (G8 and China, I’m looking at you), we’ll just have to be content with unenforceable resolutions and “Peaceful Forces”.

Posted in Politics | 4 Comments

Hey, did you know…

The seven seas is a way of saying the entire world. At one point, the seven named seas were the known world (at least to users of the phrase). Check it out: http://www.whoi.edu/info/seven-seas.html

Posted in General | Leave a comment

JSR 303 – Bean Validation

JSR 303 seeks to define a meta-data model and API for JavaBean validation. “This API is seen as a general extension to the JavaBeans object model, and as such is expected to be used as a core component in other specifications, such as JSF, JPA, and Bean Binding.”

A standardized validation API would be a great addition to the JDK. I’ve used a couple including Commons Validator and XWork’s bundled validation framework. These frameworks make life easier. However, like most things, there are no standards.

XWork’s implementation provides the ability to traverse the inheritance tree. It also allows for validation based on the context (method call). It comes with a number of validators, including stringlength, required, date. Validation can take place at field or non-field level. Fairly easy to use; I’ve only had issues when developing validation along with type-conversion for non-trivial objects. However, that is specific to web-based situations.

If the JSR will be based on XWork’s implementation, my major concern is the definition of a context. The previous definition of an aliasname does not exist in non-XWork specific implementations. Aliasname’s were simply the method name of an Action. This worked since Action execution methods were no-arg by convention. This concept will have to provide validation on method calls with arguments. By the nature of annotations, this will not be an issue. However, I am curious to see the XML-based solution. Last of all, I look forward to the ability to conditionally exclude validation based on the current context.

Posted in Technical stuff | 4 Comments

Some people do know what do to with their money

Buffett calls wealth giveaway ‘logical’

Buffett will slowly give 85% of his wealth to charities, incluidng his daughter’s (the Susan A. Buffett’s foundation) and the Bill and Melida Gates’ foundation. Previously, Buffett planned on dividing and distributing his wealth personally, all $37 billion of it. I’m glad he changed his mind, the Bill and Melinda Gates foundation does some really great work. I’m sure the 4 other charities will do well by his money as well.

Posted in General | Leave a comment

Great post

How to be a Junior Developer Forever: part 1

I thought it was right on the money… If you want to grow, ya might want to read an API or man page once in a while.

Posted in Technical stuff | Leave a comment

Some Justice… it’s better than nothing

Finally, some justice for the people who hitched their financial futures to the Enron bandwagon.

Lay's Stats

Skilling's Stats

Now all we need to do is write a law for price gauging; then we can deal with these oil execs! 😉

Posted in General | Leave a comment

Honda Accord

Got this from a co-worker. Just another reason why the Honda Accord is the best car arround…

http://www.albinoblacksheep.com/flash/honda.php

Posted in General | 2 Comments

Yeah, that’s nice…

Umm, and sometimes you die of a heart attack. Please note, the CEO is obese, or as I like to call it, round-muscled. I’d like to believe that round-muscled people tend to be better CEO’s. Just look at Microsoft… exactly.

Posted in Funny | 4 Comments