I have a strange issue occuring in my application, I will quickly explain global architecture and then my problem in depth.
I use a service to populate a HashMap<DomainObject,Boolean> coming from my database (JPA driven) which is in turn returned to my view, via an EJB remote method call (using Apache Wicket). In this part, I add a new DomainObject to the map returned in order to store any new value from my end user.
The problem occurs when the user hit the "add" button in its browser, I try to retrieve the newly created item in my map, but it fails. By playing with the debugger I face the following things.
Assuming HashMap<DomainObject, Boolean> map and DomainObject do are the two variables interesting I have the following results in the debugger
map.keySet(); gives me an object corresponding to do (even the #whatever simili-reference is identical), hashcode() on both objects returns similar value and equals() between the two returns true
map.containsKey(do); returns false
map.get(do); returns null, weird because my key seems to be in the map.
Assuming my newly created item is the first key enumerated by keySet(), I do the following :
map.get(new ArrayList(map.keySet()).get(0)), and it returns null.
If it can help, by attaching breakpoints to my DomainObject.equals() and DomainObject.hashcode() methods I found that map.get() is only calling hashcode() and not equals().
The only workaround I found is to recreate a new map on top of the existing one new HashMap(map), in this new map, I have no problem at all looking up an object by its key.
I hope someone here can give my a pointer on what happens, thanks.
Environment used :
Sun Java 1.6.0_26 x64 under OS X 10.7.1
OpenJDK 1.6.0_18 x64 under Debian 6.0.2 (2.6.32)
Apache Wicket 1.4.17
Oracle Glassfish 3.1.1
JBoss Hibernate 3.6.5
DomainObject code :
public class AssetComponentDetailTemplate extends BaseEntite<Long> {
public enum DataType {
TXT,
DATE,
INT,
JOIN,
LIST,
COULEURS,
REFERENCE
}
public enum Tab {
IDENTITE,
LOCALISATION,
CYCLE_DE_VIE,
FINANCE,
RESEAU,
DETAIL
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private DataType dataType;
private Integer classNameId;
private Long orderId;
private Long nextAssetComponentDetailTemplateId;
private String unit;
#Enumerated(EnumType.STRING)
private Tab tab;
#Column(nullable = false)
private Long uniqueOrganizationId;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "idAssetComponentDetailTemplate", insertable = false, updatable = false)
private List<AssetComponentDetailJoin> assetComponentDetailJoins;
private Boolean mandatory = false;
public AssetComponentDetailTemplate() {
}
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public DataType getDataType() {
return dataType;
}
public void setDataType(final DataType dataType) {
this.dataType = dataType;
}
public Integer getClassNameId() {
return classNameId;
}
public void setClassNameId(final Integer classNameId) {
this.classNameId = classNameId;
}
public Long getUniqueOrganizationId() {
return uniqueOrganizationId;
}
public void setUniqueOrganizationId(final Long uniqueOrganizationId) {
this.uniqueOrganizationId = uniqueOrganizationId;
}
public Long getNextAssetComponentDetailTemplateId() {
return nextAssetComponentDetailTemplateId;
}
public void setNextAssetComponentDetailTemplateId(final Long nextAssetComponentDetailTemplateId) {
this.nextAssetComponentDetailTemplateId = nextAssetComponentDetailTemplateId;
}
public String getUnit() {
return unit;
}
public void setUnit(final String unit) {
this.unit = unit;
}
public Tab getTab() {
return tab;
}
public void setTab(final Tab tab) {
this.tab = tab;
}
public Long getOrder() {
return orderId;
}
public void setOrder(final Long order) {
this.orderId = order;
}
public Boolean isMandatory() {
return mandatory;
}
#Override
public String toString() {
return name;
}
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final AssetComponentDetailTemplate that = (AssetComponentDetailTemplate) o;
if (classNameId != null ? !classNameId.equals(that.classNameId) : that.classNameId != null) {
return false;
}
if (dataType != that.dataType) {
return false;
}
if (id != null ? !id.equals(that.id) : that.id != null) {
return false;
}
if (name != null ? !name.equals(that.name) : that.name != null) {
return false;
}
if (nextAssetComponentDetailTemplateId != null ?
!nextAssetComponentDetailTemplateId.equals(that.nextAssetComponentDetailTemplateId) :
that.nextAssetComponentDetailTemplateId != null) {
return false;
}
if (orderId != null ? !orderId.equals(that.orderId) : that.orderId != null) {
return false;
}
if (tab != that.tab) {
return false;
}
if (uniqueOrganizationId != null ? !uniqueOrganizationId.equals(that.uniqueOrganizationId) :
that.uniqueOrganizationId != null) {
return false;
}
if (unit != null ? !unit.equals(that.unit) : that.unit != null) {
return false;
}
return true;
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (dataType != null ? dataType.hashCode() : 0);
result = 31 * result + (classNameId != null ? classNameId.hashCode() : 0);
result = 31 * result + (orderId != null ? orderId.hashCode() : 0);
result = 31 * result +
(nextAssetComponentDetailTemplateId != null ? nextAssetComponentDetailTemplateId.hashCode() : 0);
result = 31 * result + (unit != null ? unit.hashCode() : 0);
result = 31 * result + (tab != null ? tab.hashCode() : 0);
result = 31 * result + (uniqueOrganizationId != null ? uniqueOrganizationId.hashCode() : 0);
return result;
}
[This basically expands on Jesper's answer but the details may help you]
Since recreating the map using new HashMap(map) is able to find the element I am suspecting that the hashCode() of the DomainObject changed after adding it to the Map.
For example if your DomainObject looks the following
class DomainObject {
public String name;
long hashCode() { return name.hashCode(); }
boolean equals(Object other) { /* compare name in the two */'
}
Then
Map<DomainObject, Boolean> m = new HashMap<DomainObject, Boolean>();
DomainObject do = new DomainObject();
do.name = "ABC";
m.put(do, true); // do goes in the map with hashCode of ABC
do.name = "DEF";
m.get(do);
The last statement above will return null. Because the do object you have inside the map is under the bucket of "ABC".hashCode(); there is nothing in the "DEF".hashCode() bucket.
The hashCode of the Objects in map should not change once added to map. The best way to ensure it is that the fields on which hashCode depends must be immutable.
Is your DomainObject class immutable? Does it have properly implemented hashCode and equals methods?
Note that you will get into trouble if your DomainObject class is not immutable and you change the state of the object while it is in the map in a way that would change the result of calling hashCode or equals.
hashCode must be implemented in such a way that it returns the same value for two objects whenever equals returns true when comparing these objects. See the API documentation of java.lang.Object.hashCode() for detailed information.
Here is your clue:
hashcode() on both objects returns similar value
For the objects to be considered equal, their hash codes shouldn't just be similar, they must be identical.
If two objects have different hash codes, then as far as the container is concerned the objects are different. There's no need to even call equals().
From the Javadoc:
The general contract of hashCode is:
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.
If I were you, I'd take a close look at DomainObject.hashcode() and DomainObject.equals() to see what's causing the contract to be broken.
map.get(do) returning null could be easily explained by assuming that the Boolean value for that key might be null but map.containsKey(do) returning false would require do's hashCode to be different at the time of calling containsKey(do) to it's hashCode at the time of retrieving it from the keySet.
To see what's happening, you could (temporarily) use a more verbose implementation of HashMap...
Maybe something like this:
public class VerboseHashMap<K, V> implements Map<K, V> {
private transient final static Logger logger = Logger.getLogger(VerboseHashMap.class);
private HashMap<K, V> internalMap = new HashMap<K, V>();
public boolean containsKey(Object o) {
logger.debug("Object HashCode: " + o.hashCode());
logger.debug("Map contents:");
for (Entry<K, V> entry : internalMap.entrySet()) {
logger.debug(entry.getKey().hashCode() + " - " + entry.getValue().toString());
}
return internalMap.containsKey(o);
}
public V get(Object key) {
logger.debug("Object HashCode: " + key.hashCode());
logger.debug("Map contents:");
for (Entry<K, V> entry : internalMap.entrySet()) {
logger.debug(entry.getKey().hashCode() + " - " + entry.getValue().toString());
}
return internalMap.get(key);
}
}
You'd need to map all the other requirements of the Map interface to your internalMap as well.
Note: This code is not intended for production, nor is it in any way performance oriented, nice or unsmelly....
2nd note (after seeing your code): To use your domain-object as a key for your hashMap, you should only use the immutable parts of your object for hashCode and equals (in this case the id-value). Else lazy-loading further values would change the hashCode...
In Response to your comment:
public class Demo extends TestCase {
public void testMap() {
Map<DomainObject, String> map = new HashMap<DomainObject, String>();
DomainObject sb = new DomainObject();
map.put(sb, "Some value");
System.out.println(map.containsKey(sb));
sb.value = "Some Text";
System.out.println(map.containsKey(sb));
}
private static class DomainObject {
public String value = null;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DomainObject other = (DomainObject) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
}
}
prints
true
false
The HashCode for the key is computed at the time of putting it into the map.
Related
This question already has answers here:
Implementing equals and hashCode for objects with circular references in Java
(4 answers)
Closed 3 years ago.
I'm trying to calculate hashcode of one class, but I got stackoverflow. How can I do this correctly? I genered it by IntelliJ idea, but still. Got stackoverflow, I know the reason (probably) but I really want to calculate proper hashcode..
public class Main {
public static void main(String[] args) {
TestA testA = new TestA();
TestB testB = new TestB();
testA.id = 1;
testA.name = "test";
testA.testB = testB;
testB.testA = testA;
testB.id = 1;
testB.name = "test";
System.out.println(testA.hashCode());
}
}
class TestB {
int id;
String name;
TestA testA;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TestB)) return false;
TestB testB = (TestB) o;
if (id != testB.id) return false;
if (name != null ? !name.equals(testB.name) : testB.name != null) return false;
return testA != null ? testA.equals(testB.testA) : testB.testA == null;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (testA != null ? testA.hashCode() : 0);
return result;
}
}
class TestA {
int id;
String name;
TestB testB;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TestA)) return false;
TestA testA = (TestA) o;
if (id != testA.id) return false;
if (name != null ? !name.equals(testA.name) : testA.name != null) return false;
return testB != null ? testB.equals(testA.testB) : testA.testB == null;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (testB != null ? testB.hashCode() : 0);
return result;
}
}
I included main function too. You can easily open this..
What you are looking for is a way to walk the object tree without entering into an infinite loop. This can be achieved by storing the visited objects in a thread-local Set and stopping when entering a hashcode while this is in that set.
And you can't just willy-nilly use a HashSet to store the 'visited' objects, because it internally calls your hashcode so the problem is just shifted elsewhere and you still get a stack overflow. Luckily there's a container that uses identity instead of equality, however it's the Map variant, not the Set. Ideally you want IdentityHashSet, but it doesn't exist, however the still useful IdentityHashMap exists. Just use the keys as the actual contents and use dummy values.
public class Main {
public static void main(String[] args) {
TestA testA = new TestA();
TestB testB = new TestB();
testA.id = 1;
testA.name = "test";
testA.testB = testB;
testB.testA = testA;
testB.id = 1;
testB.name = "test";
System.out.println(testA.hashCode());
}
}
class TestB {
int id;
String name;
TestA testA;
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof TestB))
return false;
TestB testB = (TestB)o;
if (id != testB.id)
return false;
if (name != null ? !name.equals(testB.name) : testB.name != null)
return false;
return testA != null ? testA.equals(testB.testA) : testB.testA == null;
}
private static final ThreadLocal<Set<Object>> VISITED = ThreadLocal.withInitial(() -> new HashSet(10));
#Override
public int hashCode() {
Set<Object> visited = VISITED.get();
if (visited.contains(this))
return 0;
visited.add(this);
try {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (testA != null ? testA.hashCode() : 0);
return result;
} finally {
visited.remove(this);
}
}
}
class TestA {
int id;
String name;
TestB testB;
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof TestA))
return false;
TestA testA = (TestA)o;
if (id != testA.id)
return false;
if (name != null ? !name.equals(testA.name) : testA.name != null)
return false;
return testB != null ? testB.equals(testA.testB) : testA.testB == null;
}
private static final ThreadLocal<Map<Object, Object>> VISITED =
ThreadLocal.withInitial(() -> new IdentityHashMap<>(10));
#Override
public int hashCode() {
Map<Object, Object> visited = VISITED.get();
if (visited.containsKey(this))
return 0;
visited.put(this, this);
try {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (testB != null ? testB.hashCode() : 0);
return result;
} finally {
visited.remove(this);
}
}
}
Note: The two VISITED variables can be a single variable, but since your classes don't have a common superclass (other than Object) I had to make two of them.
Caveat: When the tree contains multiple times the same instance of a class, the hashcode of that instance will be calculated multiple times. This is because everytime that instance is done visiting, it's removed from the list. This is because you don't want hard references to these instances to remain in the thread-local Map, preventing garbage collection.
I have declared a class implementing comparable interface and compareTo method comparing two employees using employee id. Created class objects inserted into array list. Now When I use Collections.sort(arrayList Object) it is working fine.I have a confusion in how the comparison differs between comparable and comparator interface.I want to know how comparison happens between a employee id string which purely consists of numbers and other string employee id which has few characters and numbers using both the inerface.
class Velraj implements Comparable<Velraj>{
String id;
String work;
public Velraj(String id, String work){
this.id = id;
this.work = work;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getWork() {
return work;
}
public void setWork(String work) {
this.work = work;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((work == null) ? 0 : work.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Velraj other = (Velraj) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (work == null) {
if (other.work != null)
return false;
} else if (!work.equals(other.work))
return false;
return true;
}
public int compareTo(Velraj o) {
// TODO Auto-generated method stub
return this.getWork().compareTo(o.getWork());
}
}
public class AppMain {
public static void main(String[] args) {
Velraj v1 = new Velraj("16450","Security Specialist");
Velraj v2 = new Velraj("245591","Consultant");
Velraj v3 = new Velraj("RNJIV3664","Java Architecct");
ArrayList<Velraj> a = new ArrayList<Velraj>();
a.add(v1);
a.add(v2);
a.add(v3);
Collections.sort(a);
/*Collections.sort(a, new Comparator<Velraj>(){
public int compare(Velraj o1, Velraj o2) {
// TODO Auto-generated method stub
return o1.getId().compareTo(o2.getId());
}
});*/
for(Velraj v: a){
System.out.println(v.getId() +"--> " +v.getWork());
}
}
}
Output using comparable:
245591--> Consultant
RNJIV3664--> Java Architecct
16450--> System Security Specialist
Output using comparator:
16450--> System Security Specialist
245591--> Consultant
RNJIV3664--> Java Architecct
Comparison of Strings uses lexicographic order, also known as alphabetical order. That is, order by the first character, and if the first character is equal look at the second character and so on.
The individual characters are ordered according to their character code. Basically digits < upper case letters < lower case letters.
The reason you are getting different results when using Comparable and Comparator is because you are comparing on different properties in both cases!
In the overridden compareTo method of your class, you have specified that objects should be sorted based on the work property ofVelraj class. But in compare method in your main function, you have specified that objects be sorted on id field of Velraj. If you use the same property in both cases, then you'd have gotten identical result on sort.
I am trying to override the mentioned methods for my HashSet:
Set<MyObject> myObjectSet = new HashSet<MyObject>();
MyObject:
public class MyObject implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
int number;
Map<String,String> myMap;
public MyObject(String name, int number, Map<String,String> myMap) {
this.name = name;
this.number = number;
this.myMap = myMap;
}
[...]
}
How do I override the hashcode(), equals() and compareTo() method?
Currently I have the following:
public int hashCode () {
return id.hashCode();
}
// override the equals method.
public boolean equals(MyObject s) {
return id.equals(s.id);
}
// override compareTo
public int compareTo(MyObject s) {
return id.compareTo(s.id);
}
I read that comparing by id is not enough this is object is a persistent entity for the DB (see here).
The name and number aren't unique across all objects of this type.
So how should I override it?
Do I also need to compare the hashMap inside it?
I am confused. The only unique thing about the object is the the map myMap which gets populated later in the lifecycle.
How do I check for its equality?
Based on all the responses I have changed the methods to the following
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyComplexObj myComplexObj = (MyComplexObj) o;
return myMap != null ? myMap.equals(myComplexObj.myMap) : myComplexObj.myMap == null;
}
#Override
public int hashCode() {
return myMap != null ? myMap.hashCode() : 0;
}
public int compareTo(MyComplexObj o) {
return myMap.compareTo(o.getMyMap()));
}
This fails at the compareTo method, "this method is undefined for the type Map
The basic question here is "How can you determine if two objects are equal to each other?"
This is a simple question for simple objects. However, it becomes increasingly difficult with even slightly more complex objects.
As stated in the original question:
The only unique thing about the object is the the map myMap which gets populated later in the lifecycle.
Given two instances of the type MyObject, the member variables myMap must be compared with each other. This map is of type Map<String, String>. A few questions immediately come to mind:
How do the keys & values define equality?
(does a key=value pair need to be compared as a unit?)
(or should only the values be compared to each other?)
How does the order of the keys in the map affect equality?
(should keys in the list be sorted, so that A-B-C is equivalent to B-C-A?)
(or does 1-2-3 mean something different than 3-2-1?)
Does upper/lower case make any different to the equality of the values?
Will these objects ever be stored in some kind of Java HashSet or Java TreeSet?
(do you need to store the same object several times in the same collection?)
(or should objects with equal hashcodes only be stored once?)
Will these objects ever require sorting as part of a list or Java Collection?
How should the comparison function arrange non-equal objects in a list?
(how should key order determine if an object will come earlier or later in a list?)
(how should values determine order, especially if several values are different?)
Answers to each of these questions will vary between applications. In order to keep this applicable to a general audience, the following assumptions are being made:
To maintain a deterministic comparison, keys will be sorted
Values will be considered to be case-sensitive
Keys and values are inseparable, and will be compared as a unit
The Map will be flattened into a single String, so results can be compared easily
The beauty of using equals(), hashCode(), and compareTo() is that once hashCode() is implemented properly, the other functions can be defined based on hashCode().
Considering all of that, we have the following implementation:
#Override
public boolean equals(final Object o)
{
if (o instanceof MyObject)
{
return (0 == this.compareTo(((MyObject) o)));
}
return false;
}
#Override
public int hashCode()
{
return getKeyValuePairs(this.myMap).hashCode();
}
// Return a negative integer, zero, or a positive integer
// if this object is less than, equal to, or greater than the other object
public int compareTo(final MyObject o)
{
return this.hashCode() - o.hashCode();
}
// The Map is flattened into a single String for comparison
private static String getKeyValuePairs(final Map<String, String> m)
{
final StringBuilder kvPairs = new StringBuilder();
final String kvSeparator = "=";
final String liSeparator = "^";
if (null != m)
{
final List<String> keys = new ArrayList<>(m.keySet());
Collections.sort(keys);
for (final String key : keys)
{
final String value = m.get(key);
kvPairs.append(liSeparator);
kvPairs.append(key);
kvPairs.append(kvSeparator);
kvPairs.append(null == value ? "" : value);
}
}
return 0 == kvPairs.length() ? "" : kvPairs.substring(liSeparator.length());
}
All the critical work is being done inside of hashCode(). For sorting, the compareTo() function only needs to return a negative/zero/positive number -- a simple hashCode() diff. And the equals() function only needs to return true/false -- a simple check that compareTo() equals zero.
For further reading, there is a famous dialogue by Lewis Carroll on the foundations of logic, which touches on the basic question of equality:
https://en.wikipedia.org/wiki/What_the_Tortoise_Said_to_Achilles
And, in regard to even simple grammatical constructs, there is a fine example of two "equal" sentences at the start of chapter 6, "Pig and Pepper", from Alice in Wonderland:
The Fish-Footman began by producing from under his arm a great letter, and this he handed over to the other, saying, in a solemn tone, "For the Duchess. An invitation from the Queen to play croquet." The Frog-Footman repeated, in the same solemn tone, "From the Queen. An invitation for the Duchess to play croquet." Then they both bowed low and their curls got entangled together.
compareTo() is relevant to sorting. It has no relevance to a HashSet or HashMap.
A properly working equals() and hashCode() are vital for members of hash-based collections. Read their specifications in the Javadoc for Object.
Possibly the definitive recommendations for implementing these are in Joshua Bloch's Effective Java. I recommend reading the relevant chapter -- it's easily Google-able. There's no point in trying to paraphrase it all here.
One thing that may have escaped your notice, is that your field myMap has a working equals() and hashCode() of its own, so you don't have to do anything special with it. If you can guarantee that none of the fields are null, a reasonable hashCode() would be (following Bloch's system):
public int hashCode() {
int result = 44; // arbitrarily chosen
result = 31 * result + (int) (id ^ (id >>> 32));
result = 31 * result + name.hashCode();
result = 31 * result + number;
result = 31 * result + myMap.hashCode();
return result;
}
(You'll need more code if any of these could be null)
Pretty much all IDEs will automatically generate both equals() and hashcode(), using all the fields in the class. They'll use something very similar to Bloch's recommendations. Hunt around the UI. You'll find it.
Another alternative is to use Apache ReflectionUtils, which allows you to simply use:
#Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
#Override
public boolean equals(final Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
This works out which fields to use at runtime, and applies Bloch's methods.
This is what intellij default option gives
import java.util.Map;
public class MyObject {
String name;
int number;
Map<String,String> myMap;
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyObject myObject = (MyObject) o;
if (number != myObject.number) return false;
if (name != null ? !name.equals(myObject.name) : myObject.name != null) return false;
return myMap != null ? myMap.equals(myObject.myMap) : myObject.myMap == null;
}
#Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + number;
result = 31 * result + (myMap != null ? myMap.hashCode() : 0);
return result;
}
}
But, since you said
The only unique thing about the object is the the map myMap which gets
populated later in the lifecycle.
I would just keep myMap and skip both name and number (But this begs the question, why would you include a redundant data- name and number in all the elements of your collection?)
Then it becomes
import java.util.Map;
public class MyObject {
String name;
int number;
Map<String,String> myMap;
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyObject myObject = (MyObject) o;
return myMap != null ? myMap.equals(myObject.myMap) : myObject.myMap == null;
}
#Override
public int hashCode() {
return myMap != null ? myMap.hashCode() : 0;
}
}
Keep in mind that, there are other ways too for the equals and hashcode methods. For example, Here are the options that intelliJ gives for code generation
To Answer Further question about CompareTo
Unlike Equals and Hashcode, here is no contract exist between compareTo and any other behaviors. You don't really need to do anything with compareTo until you want to make use of it for say, sorting. To read more about CompareTo Why should a Java class implement comparable?
If you want to make myMap implements comparable, and any other methods that you want, create decorator that implement comparable interface and delegate all other methods to enclosing myMap instance.
public class ComparableMap implements Map<String, String>, Comparable<Map<String, String>> {
private final Map<String, String> map;
public ComparableMap(Map<String, String> map) {
this.map = map;
}
#Override
public int compareTo(Map<String, String> o) {
int result = 0;
//your implementation based on values on map on you consider one map bigger, less or as same as another
return result;
}
#Override
public boolean equals(Object obj) {
return map.equals(obj);
}
#Override
public int hashCode() {
return map.hashCode();
}
// map implementation methods
#Override
public int size() {
return map.size();
}
#Override
public boolean isEmpty() {
return map.isEmpty();
}
#Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
#Override
public String get(Object key) {
return map.get(key);
}
#Override
public String put(String key, String value) {
return map.put(key, value);
}
#Override
public String remove(Object key) {
return map.remove(key);
}
#Override
public void putAll(Map<? extends String, ? extends String> m) {
map.putAll(m);
}
#Override
public void clear() {
map.clear();
}
#Override
public Set<String> keySet() {
return map.keySet();
}
#Override
public Collection<String> values() {
return map.values();
}
#Override
public Set<Entry<String, String>> entrySet() {
return map.entrySet();
}
}
You may use this map in anywhere where you use myMap
public class MyObject implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
int number;
ComparableMap myMap;
public MyObject(String name, int number, Map<String, String> myMap) {
this.name = name;
this.number = number;
this.myMap = new ComparablemyMap(myMap);
}
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyComplexObj myComplexObj = (MyComplexObj) o;
return myMap != null ? myMap.equals(myComplexObj.myMap) : myComplexObj.myMap == null;
}
#Override
public int hashCode() {
return myMap != null ? myMap.hashCode() : 0;
}
public int compareTo(MyComplexObj o) {
return myMap.compareTo(o.getMyMap())); //now it works
}
}
I have a HashMap for storing objects:
private Map<T, U> fields = Collections.synchronizedMap(new HashMap<T, U>());
but, when trying to check existence of a key, containsKey method returns false.
equals and hashCode methods are implemented, but the key is not found.
When debugging a piece of code:
return fields.containsKey(bean) && fields.get(bean).isChecked();
I have:
bean.hashCode() = 1979946475
fields.keySet().iterator().next().hashCode() = 1979946475
bean.equals(fields.keySet().iterator().next())= true
fields.keySet().iterator().next().equals(bean) = true
but
fields.containsKey(bean) = false
What could cause such strange behavioure?
public class Address extends DtoImpl<Long, Long> implements Serializable{
<fields>
<getters and setters>
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + StringUtils.trimToEmpty(street).hashCode();
result = prime * result + StringUtils.trimToEmpty(town).hashCode();
result = prime * result + StringUtils.trimToEmpty(code).hashCode();
result = prime * result + ((country == null) ? 0 : country.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Address other = (Address) obj;
if (!StringUtils.trimToEmpty(street).equals(StringUtils.trimToEmpty(other.getStreet())))
return false;
if (!StringUtils.trimToEmpty(town).equals(StringUtils.trimToEmpty(other.getTown())))
return false;
if (!StringUtils.trimToEmpty(code).equals(StringUtils.trimToEmpty(other.getCode())))
return false;
if (country == null) {
if (other.country != null)
return false;
} else if (!country.equals(other.country))
return false;
return true;
}
}
You shall not modify the key after having inserted it in the map.
Edit : I found the extract of javadoc in Map :
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
Example with a simple wrapper class:
public static class MyWrapper {
private int i;
public MyWrapper(int i) {
this.i = i;
}
public void setI(int i) {
this.i = i;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return i == ((MyWrapper) o).i;
}
#Override
public int hashCode() {
return i;
}
}
and the test :
public static void main(String[] args) throws Exception {
Map<MyWrapper, String> map = new HashMap<MyWrapper, String>();
MyWrapper wrapper = new MyWrapper(1);
map.put(wrapper, "hello");
System.out.println(map.containsKey(wrapper));
wrapper.setI(2);
System.out.println(map.containsKey(wrapper));
}
Output :
true
false
Note : If you dont override hashcode() then you will get true only
As Arnaud Denoyelle points out, modifying a key can have this effect. The reason is that containsKey cares about the key's bucket in the hash map, while the iterator doesn't. If the first key in your map --disregarding buckets -- just happens to be the one you want, then you can get the behavior you're seeing. If there's only one entry in the map, this is of course guaranteed.
Imagine a simple, two-bucket map:
[0: empty] [1: yourKeyValue]
The iterator goes like this:
iterate over all of the elements in bucket 0: there are none
iterate over all the elements in bucket 1: just the one yourKeyValue
The containsKey method, however, goes like this:
keyToFind has a hashCode() == 0, so let me look in bucket 0 (and only there). Oh, it's empty -- return false.
In fact, even if the key stays in the same bucket, you'll still have this problem! If you look at the implementation of HashMap, you'll see that each key-value pair is stored along with the key's hash code. When the map wants to check the stored key against an incoming one, it uses both this hashCode and the key's equals:
((k = e.key) == key || (key != null && key.equals(k))))
This is a nice optimization, since it means that keys with different hashCodes that happen to collide into the same bucket will be seen as non-equal very cheaply (just an int comparison). But it also means that changing the key -- which will not change the stored e.key field -- will break the map.
Debugging the java source code I realized that the method containsKey checks two things on the searched key against every element in the key set:
hashCode and equals; and it does it in that order.
It means that if obj1.hashCode() != obj2.hashCode(), it returns false (without evaluating obj1.equals(obj2). But, if obj1.hashCode() == obj2.hashCode(), then it returns obj1.equals(obj2)
You have to be sure that both methods -may be you have to override them- evaluate to true for your defined criteria.
Here is SSCCE for your issue bellow. It works like a charm and it couldn't be else, because your hashCode and equals methods seem to be autogenerated by IDE and they look fine.
So, the keyword is when debugging. Debug itself can harm your data. For example somewhere in debug window you set expression which changes your fields object or bean object. After that your other expressions will give you unexpected result.
Try to add all this checks inside your method from where you got return statement and print out their results.
import org.apache.commons.lang.StringUtils;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class Q21600344 {
public static void main(String[] args) {
MapClass<Address, Checkable> mapClass = new MapClass<>();
mapClass.put(new Address("a", "b", "c", "d"), new Checkable() {
#Override
public boolean isChecked() {
return true;
}
});
System.out.println(mapClass.isChecked(new Address("a", "b", "c", "d")));
}
}
interface Checkable {
boolean isChecked();
}
class MapClass<T, U extends Checkable> {
private Map<T, U> fields = Collections.synchronizedMap(new HashMap<T, U>());
public boolean isChecked(T bean) {
return fields.containsKey(bean) && fields.get(bean).isChecked();
}
public void put(T t, U u) {
fields.put(t, u);
}
}
class Address implements Serializable {
private String street;
private String town;
private String code;
private String country;
Address(String street, String town, String code, String country) {
this.street = street;
this.town = town;
this.code = code;
this.country = country;
}
String getStreet() {
return street;
}
String getTown() {
return town;
}
String getCode() {
return code;
}
String getCountry() {
return country;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + StringUtils.trimToEmpty(street).hashCode();
result = prime * result + StringUtils.trimToEmpty(town).hashCode();
result = prime * result + StringUtils.trimToEmpty(code).hashCode();
result = prime * result + ((country == null) ? 0 : country.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Address other = (Address) obj;
if (!StringUtils.trimToEmpty(street).equals(StringUtils.trimToEmpty(other.getStreet())))
return false;
if (!StringUtils.trimToEmpty(town).equals(StringUtils.trimToEmpty(other.getTown())))
return false;
if (!StringUtils.trimToEmpty(code).equals(StringUtils.trimToEmpty(other.getCode())))
return false;
if (country == null) {
if (other.country != null)
return false;
} else if (!country.equals(other.country))
return false;
return true;
}
}
I have a HashMap that stores an object I created as a key and maps to an ArrayList of similar objects.
However, I am calling the get method, and using jGrasp's debugger I can clearly see that the key I am using in get() exists and indeed maps to an array but the only value I can get is a null value.
Here is where I am getting the null value.
public List<Entry> query(Record query) {
List<Entry> candList;
Entry key = new Entry(makeKey(query));
candList = map.get(key);
return candList;
}
Here is where I am populating the HashMap from a main store.
for(int i = 0; i < main.size(); i++) {
if(main.get(i).isActive()) {
values.clear();
tmp = new Entry(main.get(i).record());
key = new Entry(Record.make(tmp.entity(),tmp.relation(),wild));
if(!map.containsKey(key)) {
for(int v = 0; v < main.size(); v++) {
value = main.get(v);
if(key.entity().equals(value.entity()) && key.relation().equals(value.relation())) {
values.add(value);
}
}
map.put(key,new ArrayList(values));
}
}
}
Entry is a wrapper class that defaults to the equals() method of its inner object, here.
public boolean equals(Object o){
if(o == null){
return false;
}
else if(o instanceof Record){
Record r = (Record) o;
return this.entity.equals(r.entity) && this.relation.equals(r.relation) && this.property.equals(r.property);
}
else return false;
}
I also have a hashcode written for the object here.
int h = 0;
public int hashCode() {
int hash = h;
if(h != 0)
return hash;
String len = entity.concat(relation.concat(property));
for(int i = 0; i < len.length(); i++)
hash = hash * 31 +(int)len.charAt(i);
return hash;
}
For a little clarification, the Entry object holds an object of type Record that contains three immutable Strings, hence where the hashCode equation comes from.
For further clarification someone asked to see the entire Entry class.
private static class Entry {
private static boolean active;
private Record rec;
public Entry(Record r){
this.rec = r;
this.active = true;
}
public String entity() {
return rec.entity;
}
public String relation() {
return rec.relation;
}
public String property() {
return rec.property;
}
public Record record(){
return this.rec;
}
public boolean isActive(){
return this.active;
}
public void deactivate(){
this.active = false;
}
public void activate(){
this.active = true;
}
public boolean equals(Entry e) {
return this.rec.equals(e.record());
}
public int hashCode() {
return this.rec.hashCode();
}
public String toString() {
return rec.toString();
}
}
There are some collisions occurring in my HashMap but I know that's not supposed to be too much of an issue. Any ideas?
public boolean equals(Object o){
if(o == null){
return false;
}
else if(o instanceof Record){
Record r = (Record) o;
return this.entity.equals(r.entity) && this.relation.equals(r.relation) && this.property.equals(r.property);
}
else return false;
}
your Entry equals method may have some problem,what's the definition of relation?
the relation must be overwrite equals() and hashCode()
It's great to put all your code here,what's your main's definition?
and In your code there are many places contains maybe null pointer bug
your hashcode function might have a problem when setting the int to 0 (int h = 0) ... a good explanation can be found in Josh Bloch's Effectiv Java book (item 8).
Here is an example:
#Override
public int hashCode() {
int result = 17;
// this line should change depending on your fields
// let say you have a string property that is not null
result = 31 * result + property.hashCode();
return result;
}
... you can also use a library like Guava
#Override
public int hashCode() {
return Objects.hashCode(this.property1, this.property2);
}