Reputation: 1143
I have two sets both containing the same object types. I would like to be able to access the following:
My question relates to how best to compare the two sets to acquire the desired views. The class in question has numerous id properties which can be used to uniquely identify that entity. However, there are also numerous properties in the class that describe the current status of the object. The two sets can contain objects that match according to the ids, but which are in a different state (and as such, not all properties are equal between the two objects).
So - how do I best implement my solution. To implement an equals() method for the class which does not take into account the status properties and only looks at the id properties would not seem to be very true to the name 'equals' and could prove to be confusing later on. Is there some way I can provide a method through which the comparisons are done for the set methods?
Also, I would like to be able to access the 3 views described above without modifying the original sets.
All help is much appreciated!
Upvotes: 3
Views: 3574
Reputation: 37950
(Edit: My first suggestion has been removed because of an unfortunate implementation detail in TreeSet
, as pointed out by Martin Konecny. Some collection classes (e.g. )TreeSet
) allow you to supply a Comparator
that is to be used to compare elements, so you might want to use one of those classes - at least, if there is some natural way of ordering your objects.
If not (i.e. if it would be difficult to implement CompareTo()
, while it would be simpler to implement HashCode()
and Equals()
), you could create a wrapper class which implements those two functions by looking at the relevant fields from the objects they wrap, and create a regular HashSet
of these wrapper objects.
Upvotes: 4
Reputation: 15259
This is a requirement for which the Standard C++ Library fares better with its set
type, which accepts a comparator for this purpose. In the Java library, your need is modeled better by a Map
— one mapping from your candidate key to either the rest of the status-related fields, or to the complete object that happens to also contain the candidate key. (Note that the C++ set
type is mandated to be some sort of balanced tree, usually implemented as a red-black tree, which means it's equivalent to Java's TreeSet
, which does accept a custom Comparator
.) It's ugly to duplicate the data, but it's also ugly to try to work around it, as you've already found.
If you have control over the type in question and can split it up into separate candidate key and status parts, you can eliminate the duplication. If you can't go that far, consider combining the candidate key fields into a single object held within your larger, complete object; that way, the Map
key type will be the same as that candidate key type, and the only storage overhead will be the map keys' object references. The candidate key data would not be duplicated.
Note that most set types are implemented as maps under the covers; they map from the would-be set element type to something like a Boolean flag. Apparently there's too much code that would be duplicated in wholly disjoint set and map types. Once you realize that, backing up from using a set in an awkward way to using a map no longer seems to impose the storage overhead you thought it would.
It's a somewhat depressing realization, having chosen the mathematically correct idealized data structure, only to find it's a false choice down a layer or two, but even in your case your problem sounds better suited to a map representation than a set. Think of it as an index.
Upvotes: 1
Reputation: 110054
Short version: implement equals
based on the entity's key, not state.
Slightly longer version: What the equals
method should check depends on the type of object. For something that's considered a "value" object (say, an Integer
or String
or an Address
), equality is typically based on all fields being the same. For an object with a set of fields that uniquely identify it (its primary key), equality is typically based on the fields of the primary key only. Equality doesn't necessarily need to (and often shouldn't) take in to consideration the state of an object. It needs to determine whether two objects are representations of the same thing. Also, for objects that are used in a Set
or as keys in a Map
, the fields that are used to determine equality should generally not be mutable, since changing them could cause a Set
/Map
to stop working as expected.
Once you've implemented equals
like this, you can use Guava to view the differences between the two sets:
Set<Foo> notInSet2 = Sets.difference(set1, set2);
Set<Foo> notInSet1 = Sets.difference(set2, set1);
Both difference sets will be live views of the original sets, so changes to the original sets will automatically be reflected in them.
Upvotes: 3