Question

This feels like a really basic question, but it's pestering me. I want to get a diff between two sets, but it seems as if I'm incapable of doing so due to the set potentially containing objects with different implementations of Hash and Equals.

To give a simplified example of my problem lets say I have an IFoo interface which is implemented by two classes, call them BasicFoo and ExtendedFoo. They both describe the same physical object, but ExtendedFoo is enriched with some extra information BasicFoo lacks. Essentially BasicFoo describes what physical object my caller is interested in, which I'll eventually use to look up the appropriate ExtendedFoo object in my model. A BasicFoo could be thought of as being equal to an ExtendedFoo if they describe the same physical object.

I have a method that would do something sort of like this:

public void removeFooInModel(HashSet<IFoo> fooColection){

   HashSet<Foo> fooInModel=Model.getFoo();
   fooCollection.removeAll(fooInModel);
   return;
}

My problem is that I don't think it will always work. if the user passes in a fooCollection that actually contains BasicFoo objects, and my Model.getFoo() method returns a Set that actually contains ExtendedFoo then their equal and hashCode methods will be different (ExtendedFoo has far more state to compare for equality). Despite my having a very clear understanding of how to map equality and hash between the two objects It seems like there is no way to force this knowledge to be utilized by my Set object.

What I'm wondering is if there is any way to get the convenience of a Sets methods, like removeAll an Contains, when I may wish to mix and match implementations?

One way to do this would be to make my ExtendedFoo and BasicFoo HashCode methods identical, only hashing on state that is available in the IFoo interface, and make their equal methods compare any IFoo object of a different class by only comparing IFoo getter methods. However, if I someone later writes a FooBar object that extends IFoo how can I gaurentee that they also write their hashcode method identical to mine so my method still works with their classes?

I can work around my own issue easily enough by not using a Set in the one place this is an issue. However, what would others consider the 'proper' solution if I needed the efficency gains offered by a HashSet?

Was it helpful?

Solution

Unfortunately, you cannot convince the hash set to compare basic and extended objects for equality as if they both were basic objects. However, you could build a wrapper object that holds a basic or an extended object, and uses the comparison method of the basic object to compare the two.

Here is a sketch of a possible implementation:

class FooWrapper {
    private final BasicFoo obj;
    public FooWrapper(BasicFoo obj) {
        this.obj = obj;
    }
    public BasicFoo getWrapped() {
        return obj;
    }
    public int hashCode() {
        // Compute the hash code the way the BasicFoo computes it
    }
    public boolean equals(Object other) {
        // Compare the objects the way the BasicFoo does
    }
}

Now you can take a mixed collection of BasicFoo and ExtendedFoo, wrap them in the FooWrapper, and put them in a hash set. You can perform operations on the sets, and then harvest the results by unwrapping the individual BasicFoo objects from the set of wrappers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top