I have an RMI server which exports objects which implement an interface, let's call it "TestInterface".
The client can then do various tasks, calling methods on this interface. I then have some other interface for a different type of object, for which one of the methods takes in a object of type "TestInterface". The method tries to use a hashmap where the "TestInterface" is the key (it stores a hashmap of all "TestInterface" objects on the server, the value being what I would like to retrieve), but fails to find it. I know this key exists within the hashmap, but due to the interface being passed to the client, and then back to the server later it seems to not find it.
I know it exists as I am able to print out the IDs of the objects which are keys in the hashmap, and the ID of the "TestInterface" passed in (they are the same). I then printed them both out (using the default toString), and I get this:
Proxy[TestInterface,RemoteObjectInvocationHandler[UnicastRef [liveRef: [endpoint:[127.0.0.1:1099](remote),objID:[-38c943f9:14be569cb95:-7ff9, 8183237023004091504]]]]]
Test[UnicastServerRef [liveRef: [endpoint:[127.0.0.1:1099](local),objID:[-38c943f9:14be569cb95:-7ff9, 8183237023004091504]]]]
I can see these are different, which is probably why I cannot look them up. However, they look to be pointing to the same object (I believe this is the case as the objectId is the same).
My question is, why can I not look up this "Key" in the hashmap, where the Key is a reference to the same object.
My second question is, is there an easy way to fix this?
This is occurring because the actual object which you are using as the key does not evaluate the same with hashCode() and equals(). Those are the methods the HashMap is using for the lookup. A common way to handle this is to create a wrapper class which will use the internal objId inside the hashCode() and equals(). This wrapper class is then used as the key to the map.
Something like:
public Wrapper {
private Keyclass key;
...
public Keyclass getWrapped() { return key; }
#Override
public int hashCode() {
return key.objID.hashCode();
}
#Override
public boolean equals(Object o) {
return key.objID.equals(((Wrapped)o).key.objID);
}
}
Related
I am using a HashSet to add unique Users to the hashset. These users are updated frequently from an online source and when it reads it in from there it gets a new object each time so all the Users get added again.
I have a UserID variable in my user class and would like to use this to add users to the HashSet and ensure uniqueness.
private HashSet<User> userHashSet = new HashSet<User>();
userHashSet.add(user);
//based on the users id it will either add or not
I want to do this without losing efficiency and preferably only one HashSet
You should implement the equals and hashCode method in your User class.
As an example:
#Override
public int hashCode() {
return getUserId().hashCode();
}
#Override
public boolean equals(Object obj) {
return obj instanceof User &&
getUserId().equals(((User) obj).getUserId());
}
You need to implement the hashCode and equals methods in your User class. So long as you implement those two methods correctly then you will get unique entries in your HashSet.
There are many articles about how to implement these correctly (e.g. What issues should be considered when overriding equals and hashCode in Java?), but the easiest way to do it is to get your IDE to auto-generate those methods, or use the Apache Commons helpers.
I know this question has already been asked on SO a couple of times, but I still haven't found a satisfying solution, and I'm unsure which way to go. The question is:
Why doesn't the Java library provide HashSet.get(Object o) and HashMap.getKey(Object o) methods that return the actual instance in the map providing an equal instance? Example:
// Retrieve a house with ID=10 that contains additional information like size,
// location and price.
houses.get(new House(10));
I think the best answer can be found here. So here's a mixture of answers that I'm aware of:
Why would you need the instance when you already have it? It doesn't make sense to try to get the same object you already have. The object has an identifier (which controls it's equality to other Foo types) plus any number of other fields that do not contribute to it's identity. I want to be able to get the object from the Set (which includes the extra fields) by constructing an 'equal' Foo object (text is taken from one of the comments). -> no answer
Iterate the Collection and search for the instance using equals(). This uses linear search and is extremely slow in big collections. -> bad answer
Use a HashMap instead of a HashSet I don't need a map and I think it's not adequate to return a map in a method like getHouses(). The getter should return a Set and not a Map.
Use TreeSet.ceiling - don't know
This hacky code below (Java 8 HashSet only) uses reflection and provides the missing functionality. I did not find something like this in other answers (no surprise). This could have been an acceptable solution if the target Java version is defined and future Java versions would finally provide such a method, now that we have default methods for interfaces. One could think of default E get(E o){stream().filter(e->e.equals(o)).findAny().orElse(null);}
// Alternative: Subclass HashSet/HashMap and provide a get()/getKey() methods
public static <T> T getFromSet(HashSet<T> set, T key) throws Exception {
Field mapField = set.getClass().getDeclaredField("map");
mapField.setAccessible(true);
HashMap<T, Object> map = (HashMap) mapField.get(set);
Method getNodeMethod = map.getClass().getDeclaredMethod("getNode",
int.class, Object.class);
getNodeMethod.setAccessible(true);
return (T) ((Map.Entry) getNodeMethod.invoke(map, key.hashCode(),
key)).getKey();
}
Here are the questions:
Is the best solution the use of HashMap<House, House> instead of HashSet<House>?
Is there another library out there that provides this functionality and supports concurrent access?
Do you know of a bug addressing this feature?
Similar questions on SO:
Why doesn't java.util.HashSet have a get(Object o) method?
Java: Retrieving an element from a HashSet
Why does the java.util.Set interface not provide a get(Object o) method?
The reason this behaviour hasn't been catered for is that creating a House instance with invalid data just to obtain one with valid data is really poor design.
Composition is the correct solution here:
/** immutable class containing all the fields defining identity */
public final class HouseIdentifier {
private final String id;
}
public class House {
private final HouseIdentifier id;
/** all the mutable, ephemeral properties of the house should go here */
private int size;
private Person owner;
}
If you design your class hierarchy like this, then all you need for your lookups is a simple and straightforward Map<HouseIdentifier, House>.
Map doesn't have a getKey(Object o) because it's not a bidirectional map. It only maps keys to values, not the other way around.
Set doesn't have get(Object o) because that's the job for a Map.
Mapping a House object to another House object is just bad design on your part. You want to get a House by an address or a number or similar, so you have one or more maps that give you those mappings (or more likely, a database). Your question makes sense only to you, because you're thinking "in the wrong way".
Your "wrong way of thinking" is evidenced by your statement
I don't need a map and I think it's not adequate to return a map in a
method like getHouses(). The getter should return a Set and not a Map.
I have never heard that a getter can't return a Map. Although I would probably name it getHouseMap(). You're creating a huge problem out of a trivial little issue. This is the job for a database anyways, so your dataset must be quite small.
I got a problem, where I cant get the value out of an HashMap but I am sure, the key i want to get the value out with, is in the map (but not the same instance).
So here is, what i have:
I got a HiberKey, and a HiberVal.
public class HiberKey {
private int id;
}
public class HiberVal {
private int id;
}
And while the programm is running, it does a transaction now and then, and at some point i got a Hashmap AND a single HiberKey single_key. The problem here is, that i wanted to execute map.get(single_key) i allways get a null value, but im shure, there is the same value in the map (not same reference, but also of an transaction from the hibernate-session).
The first thing i thought of was equals & hashcode, but those methodes are already implemented in HiberKey.
A solution would be to fill the map with key.id instead of hiberkey and call map.get(single_key.id), but thats not the idea of hibernate i think, and i got quite more complicated sctructures with Hashmaps in maps and so on, and if i fill all of them with integer, its not really handy and readable any more
Does anyone have a suggestion, why this could occour?
Thanks for any advice.
Hibernateversion: 3.5.3
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof BabelKey)) {
return false;
}
return ((BabelKey) obj).id == id;
}
#Override
public int hashCode() {
return String.valueOf(id).hashCode();
}
// UPDATE:
Ok, here is an example where i iterate the map with additional out.println debugging, and in the output you can see the key is in the map, but simply does not work.
output:
--------------------------
Following keys in Map:
Keys with no null value: 301 //Key is In Map
Keys with no null value: 304
Keys with no null value: 303
Keys with no null value: 307
Searched key: 301 //same key
Result: null //result is null
Next Try: null
// UPDATE2:
I compared the two key files, and equals returns true, and they also have the same hashcode. But somehow Hibernate uses Javaassist, so different instances wont work out with a HashMap. I think the only solution for that, is to save the id in the hashmap and compare the id of them.
In order to utilize an Object properly as a key to a HashMap, you must override the hashCode() and equals() methods properly for it to work. You must guarantee that those methods will give the same results with different instances of an object that contains the same data in order for it to work properly.
You must also verify that the object you are using as a key does not change in any way that would make the hashCode() method give different result prior to retrieving from the HashMap.
I recommend that the object that you are using as a key be immutable.
EDIT: I am adding a link that explains the problems and solutions to issues you may incur while using an Object as a key. There are a lot. I suggest that you walk through this article.
http://www.artima.com/lejava/articles/equality.html
I have JPA entities where some properties are annotated with #Transient.
Should I use these properties in equals/hashCode/toString methods?
My first thought is NO but I don't know why.
Tips?
Ideas?
Explanations?
The case of toString() is different, you can do whatever you want with toString() so I will only cover equals() (and hashCode()).
First, the rule: if you want to store an object in a List, Map or a Set then it is a requirement that equals and hashCode are implemented so they obey the standard contract as specified in the documentation.
Now, how to implement equals() and hashCode()? A "natural" idea would be to use the properties mapped as Id as part of the equals():
public class User {
...
public boolean equals(Object other) {
if (this==other) return true;
if (id==null) return false;
if ( !(other instanceof User) ) return false;
final User that = (User) other;
return this.id.equals( that.getId() );
}
public int hashCode() {
return id==null ? System.identityHashCode(this) : id.hashCode();
}
}
Unfortunately, this solution has a major problem: when using generated identifiers, the values are not assigned until an entity becomes persistent so if a transient entity is added to a Set before being saved, its hash code will change while it's in the Set and this breaks the contract of the Set.
The recommended approach is thus to use the attributes that are part of the business key i.e. a combination of attributes that is unique for each instance with the same database identity. For example, for the User class, this could be the username:
public class User {
...
public boolean equals(Object other) {
if (this==other) return true;
if ( !(other instanceof User) ) return false;
final User that = (User) other;
return this.username.equals( that.getUsername() );
}
public int hashCode() {
return username.hashCode();
}
}
The Hibernate Reference Documentation summarizes this as follow:
"Never use the database identifier to implement equality; use a business key, a combination of unique, usually immutable, attributes. The database identifier will change if a transient object is made persistent. If the transient instance (usually together with detached instances) is held in a Set, changing the hashcode breaks the contract of the Set. Attributes for business keys don't have to be as stable as database primary keys, you only have to guarantee stability as long as the objects are in the same Set." - 12.1.3. Considering object identity
"It is recommended that you implement equals() and hashCode() using Business key equality. Business key equality means that the equals() method compares only the properties that form the business key. It is a key that would identify our instance in the real world (a natural candidate key)" - 4.3. Implementing equals() and hashCode()
So, back to the initial question:
Use a business key if possible. #Transient attributes are very likely not part of such a key.
If not possible, use identifier properties but make sure to get the values assigned before to add an entity to a List, Map, Set.
See also
Equals and HashCode
Don't Let Hibernate Steal Your Identity
equals and hashcode in Hibernate
Understanding equals() and hashCode() (p. 396) in Java Persistence with Hibernate
The two typical usages of #Transient and transient that I'm aware of, are to use them either for stuff that can't be serialized/persisted (e.g. a remote resource handle) or computed properties which can be reconstructed from others.
For computed data, it makes no sense to use them in the equality relationship (equals/hashCode), because it would be redundant. The value is computed out of other value which are already used in the equality. It can however still makes sense to print them in toString (e.g. a base price and a ratio are used to compute the actual price).
For not serializable/persitable data, it depends. I can imagine a handle to a resource that is not serializable, but you can still compare the resource name that the handle represent. Same for toString, maybe printing the handle resource name is useful.
This was my 2 cent, but if you explain your particular usage of #Transient, someone can maybe give a better advice.
Exception maybe comes from letting it be transient and at the same time you provide writeObject() and readObject() where you process it.
How do you handle object equality for java objects managed by hibernate? In the 'hibernate in action' book they say that one should favor business keys over surrogate keys.
Most of the time, i do not have a business key. Think of addresses mapped to a person. The addresses are keeped in a Set and displayed in a Wicket RefreshingView (with a ReuseIfEquals strategy).
I could either use the surrogate id or use all fields in the equals() and hashCode() functions.
The problem is that those fields change during the lifetime ob the object. Either because the user entered some data or the id changes due to JPA merge() being called inside the OSIV (Open Session in View) filter.
My understanding of the equals() and hashCode() contract is that those should not change during the lifetime of an object.
What i have tried so far:
equals() based on hashCode() which uses the database id (or super.hashCode() if id is null). Problem: new addresses start with an null id but get an id when attached to a person and this person gets merged() (re-attached) in the osiv-filter.
lazy compute the hashcode when hashCode() is first called and make that hashcode #Transitional. Does not work, as merge() returns a new object and the hashcode does not get copied over.
What i would need is an ID that gets assigned during object creation I think. What would be my options here? I don't want to introduce some additional persistent property. Is there a way to explicitly tell JPA to assign an ID to an object?
Regards
Using the id of an entity is not a good idea because transient entities don't have an id yet (and you still want a transient entity to be potentially equal to a persistent one).
Using all properties (apart from the database identifier) is also not a good idea because all properties are just not part of the identity.
So, the preferred (and correct) way to implement equality is to use a business key, as explained in Java Persistence with Hibernate:
Implementing equality with a business key
To get to the solution that we recommend, you need to understand the notion of
a business key. A business key is a property, or some combination of properties, that
is unique for each instance with the same database identity. Essentially, it’s the natural key that you would use if you weren’t using a surrogate primary key instead.
Unlike a natural primary key, it isn’t an absolute requirement that the business
key never changes—as long as it changes rarely, that’s enough.
We argue that essentially every entity class should have some business key, even
if it includes all properties of the class (this would be appropriate for some
immutable classes). The business key is what the user thinks of as uniquely identifying a particular record, whereas the surrogate key is what the application and
database use.
Business key equality means that the equals() method compares only the properties that form the business key. This is a perfect solution that avoids all the problems described earlier. The only downside is that it requires extra thought to
identify the correct business key in the first place. This effort is required anyway;
it’s important to identify any unique keys if your database must ensure data integrity via constraint checking.
For the User class, username is a great candidate business key. It’s never null,
it’s unique with a database constraint, and it changes rarely, if ever:
public class User {
...
public boolean equals(Object other) {
if (this==other) return true;
if ( !(other instanceof User) ) return false;
final User that = (User) other;
return this.username.equals( that.getUsername() );
}
public int hashCode() {
return username.hashCode();
}
}
Maybe I missed something but for an Address, the business key would typically be made of the street number, the street, the city, the postal code, the country. I don't see any problem with that.
Just in case, Equals And HashCode is another interesting reading.
Maybe a transient property would do it?
That way you don't have to worry about the persistence. Like this:
#Transient
private Integer otherId;
I use to do it that way: equal and hashcode use the key when it has been set, otherwise equals uses the base implementation (aka ==). It should work too if hashcode() returns super.hashcode() instead of 0.
#Override
public int hashCode() {
if (code == null) {
return 0;
} else {
return code.hashCode();
}
}
#Override
public boolean equals(Object obj) {
if (obj instanceof PersistentObject && Hibernate.getClass(obj).equals(Hibernate.getClass(this))) {
PersistentObject po = (PersistentObject) obj;
if (code == null) {
return po.code == null && this == po;
} else {
return code.equals(po.getCode());
}
} else {
return super.equals(obj);
}
}
The question is how often are you likely to have multiple unsaved objects that might be duplicates that need to go into a set or map? For me, the answer is virtually never so I use surrogate keys and super.equals/hashcode for unsaved objects.
Business keys make sense in some cases, but they can cause problems. For example, what if two people live at the same address - if you want that to be one record in the database, then you have to manage it as a many-to-many and lose the ability to cascade delete it so when the last person living there is deleted, you have to do extra work to get rid of the address. But if you store the same addresss for each person then your business key has to include the person entity, which may mean a database hit inside your equals/hashcode methods.
Thanks for all your input. I decided to use surrogate keys and provide those right at object creation time. This way i stay clear of all that 'rarely' changing stuff and have something solid to base identity on. First tests look rather good.
thank you all for your time. Unfortunately, i can only accept one answer as solution i will take Pascals, as he provided me with good reading ;)
enjoy