How to calculate hashCode of this class [duplicate] - java

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.

Related

Equals method does not return true for same object

#IdClass=(value = TripleKey.class)
class Triple {
String subject;
String predicate;
String object;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
Triple triple = (Triple) o;
if (!subject.equals(triple.subject)) return false;
return predicate.equals(triple.predicate);
}
#Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + subject.hashCode();
result = 31 * result + predicate.hashCode();
return result;
}
}
my objects are:
{
"subject": "http://www.someurl.com/thing/resources/<owner>#SenMLJSON",
"predicate": "probes_in",
"object":"http://sweet.jpl.nasa.gov/2.3/matrSediment.owl#clay"
}
and
{
"subject": "http://www.someurl.com/thing/resources/<owner>#SenMLJSON",
"predicate": "probes_in",
"object":"http://sweet.jpl.nasa.gov/2.3/matrSediment.owl#sand"
}
When I try the following I still have duplicates :
public static List<Triple> mergeTripleLists(List<Triple> oldList, List<Triple> newList) {
Set<Triple> tripleSet = new HashSet<>(oldList);
tripleSet.removeAll(newList);
tripleSet.addAll(newList);
return new ArrayList<>(tripleSet);
}
The problem is in:
if (!super.equals(o)) return false;
If should work after removing it.
The problem is the call to the equals method of the super class which uses object reference to test equality, so remove the line with
!super.equals(o)
You also need to remove the call to the hashCode method of the super class.

How do I implement equals for generic types?

Suppose I have a generic container type like this:
public final class Container<T> {
public final T t;
public Container(final T t) {
this.t = t;
}
}
I want to implement equals such that this passes:
final Container<Object> a = new Container<>("Hello");
final Container<String> b = new Container<>("Hello");
assertNotEquals(a, b);
The instances a and b should be different because their type parameter T is different.
However, due to erasure, this is tricky to do. This implementation, for example, is incorrect:
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof Container<?>) {
final Container<?> other = (Container<?>)obj;
return Objects.equals(this.t, other.t);
}
return false;
}
I expect that I will need to store some kind of token for T.
How do I implement equals for generic types?
This does not answer the question.
you can modify a little the Container class and add this field:
public final Class<T> ct;
with that and the equals override then
System.out.println(a.equals(b));
will return false because the equals method will check Class<String> vs Class<Object>
class Container<T> {
public final T t;
public final Class<T> ct;
public Container(final T t, Class<T> ct) {
this.t = t;
this.ct = ct;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((ct == null) ? 0 : ct.hashCode());
result = (prime * result) + ((t == null) ? 0 : t.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;
Container other = (Container) obj;
if (ct == null) {
if (other.ct != null)
return false;
} else if (!ct.equals(other.ct))
return false;
if (t == null) {
if (other.t != null)
return false;
} else if (!t.equals(other.t))
return false;
return true;
}
}

Why does my HashMap.get returns null value even after putting proper key,value?

I am trying to retrieve some values from a Hash Map, before returning the value I am checking if the key is present in the map or not, and this check always fails which results in null value. I have overridden hash Code and equals method as well. Could someone tell me what I am doing wrong here?
class fields:
private static final List<String> DZ=new ArrayList<String>();
private static final Map<Participant,List<String>> subDz=new HashMap<Participant,List<String>>();
Method where I am putting into the map:
public static synchronized void handleSubs(String[] subData,String dz){
int[] lowdims=new int[subData.length];
int[] highdims=new int[subData.length];
try {
for (int i=1;i<subData.length;i++){
if (!subData[i].equals("") && !subData[i].equals("\n")){
if (i%2==0){
highdims[i]=Integer.parseInt(subData[i].trim());
}
else {
lowdims[i]=Integer.parseInt(subData[i].trim());
}
}
}
if (!DZ.isEmpty()){
DZ.clear();
}
DZ.add(dz);
allSubDZs.add(dz);
int[] newlow=removeZeroes(lowdims);
int[] newhigh=removeZeroes(highdims);
allSubs.add(new Participant(newlow,newhigh));
subDz.put(new Participant(newlow,newhigh),DZ );
}
Method where I am retrieving the values:
public static List<String> getSubDz(Participant sub){
if (subDz.containsKey(sub)){
return subDz.get(sub);
}
else{
logger.info("Subscription DZ not available");
return null;
}
}
The if check in the getSubDz always fails, even though I put the key in it.
hashCode and equals methods:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((DZ == null) ? 0 : DZ.hashCode());
return result;
}
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final SubscriptionHandler other=(SubscriptionHandler)obj;
if (DZ == null) {
if (other.DZ != null) {
return false;
}
} else if (!DZ.equals(other.DZ)) {
return false;
}
return true;
You need equals and hashcode on the key class. This would be the class Participant in your case.

Java: Better way to compare sets of 2 numbers than concatenation

Here's the situation: I want to test 2 objects for uniqueness based on 2 different ID's. Example:
// Note I'm using JSON notation to keep things simple; the actual code
// is with Java Objects
// OBJECT A
{
main_id: 0,
id_a: 123,
id_b: 456
}
// OBJECT B
{
main_id: 1,
id_a: 123,
id_b: 456
}
// OBJECT C
{
main_id: 2,
id_a: 123,
id_b: 789
}
In the Example, Objects A and B are the same because id_a and id_b are the same, and Object C is different.
To determine this in the code, I'm planning on converting both ID's to a string and concatenating them together with a separator char in the middle (e.g., "{id_a},{id_b}"), then adding them to a Set<String> to test for uniqueness.
My question is, is there a better way? (By better, I mean more efficient and/or less kludgy)
If you want to use HashSet, you can override hashCode and equals to exclusively look at those two members.
Hash code: (31 is just a prime popularly used for hashing in Java)
return 31*id_a + id_b;
Equals: (to which you'll obviously need to add instanceof checks and type conversion)
return id_a == other.id_a && id_b == other.id_b;
If you don't want to bind these functions to the class because it's used differently elsewhere, but you still want to use HashSet, you could consider:
Creating an intermediate class to be stored in the set, which will contain your class as a member and implement the above methods appropriately.
Use your string approach
Use HashSet<Point> - Point is not ideal for non-coordinate purposes as the members are simply named x and y, but I do find it useful to have such a class available, at least for non-production code.
Alternatively, if you want to use TreeSet, you could have your class implement Comparable (overriding compareTo) or provide a Comparator for the TreeSet, both of which would compare primarily on the one id, and secondarily on the other.
The basic idea would look something like this:
if (objectA.id_a != objectB.id_a)
return Integer.compare(objectA.id_a, objectB.id_a);
return Integer.compare(objectA.id_b, objectB.id_b);
Not sure this is any more efficient or less kludgy. You could keep the original hashcode/equals using the main id (as per your comment) and then create a wrapper that has a hashcode/equals for the composite ida, idb. Maybe over the top for what you need though.
CompositeIdEntity.java
public interface CompositeIdEntity {
long getIdA();
long getIdB();
}
Entity.java
public class Entity implements CompositeIdEntity {
private final long mainId;
private final long idA;
private final long idB;
public Entity(long mainId, long idA, long idB) {
this.mainId = mainId;
this.idA = idA;
this.idB = idB;
}
#Override
public long getIdA() {
return idA;
}
#Override
public long getIdB() {
return idB;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (mainId ^ (mainId >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Entity other = (Entity) obj;
if (mainId != other.mainId)
return false;
return true;
}
#Override
public String toString() {
return "Entity [mainId=" + mainId + ", idA=" + idA + ", idB=" + idB
+ "]";
}
}
CompositeIdWrapper.java
public class CompositeIdWrapper {
private final CompositeIdEntity compositeIdEntity;
public CompositeIdWrapper(CompositeIdEntity compositeIdEntity) {
this.compositeIdEntity = compositeIdEntity;
}
public CompositeIdEntity getCompositeIdEntity() {
return compositeIdEntity;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ (int) (compositeIdEntity.getIdA() ^ (compositeIdEntity
.getIdA() >>> 32));
result = prime * result
+ (int) (compositeIdEntity.getIdB() ^ (compositeIdEntity
.getIdB() >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CompositeIdWrapper other = (CompositeIdWrapper) obj;
if (compositeIdEntity.getIdA() != other.compositeIdEntity.getIdA())
return false;
if (compositeIdEntity.getIdB() != other.compositeIdEntity.getIdB())
return false;
return true;
}
}
Test.java
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Entity en1 = new Entity(0, 123, 456);
Entity en2 = new Entity(1, 123, 456);
Entity en3 = new Entity(2, 123, 789);
Entity en4 = new Entity(2, 123, 456);
Entity en5 = new Entity(1, 123, 789);
// Set based on main id
Set<Entity> mainIdSet = new HashSet<>();
mainIdSet.add(en1);
mainIdSet.add(en2);
mainIdSet.add(en3);
mainIdSet.add(en4);
mainIdSet.add(en5);
System.out.println("Main id set:");
for (Entity entity : mainIdSet) {
System.out.println(entity);
}
// Set based on ida, idb
Set<CompositeIdWrapper> compositeIdSet = new HashSet<>();
compositeIdSet.add(new CompositeIdWrapper(en1));
compositeIdSet.add(new CompositeIdWrapper(en2));
compositeIdSet.add(new CompositeIdWrapper(en3));
compositeIdSet.add(new CompositeIdWrapper(en4));
compositeIdSet.add(new CompositeIdWrapper(en5));
System.out.println("Composite id set:");
for (CompositeIdWrapper wrapped : compositeIdSet) {
System.out.println(wrapped.getCompositeIdEntity());
}
}
}
Output
Main id set:
Entity [mainId=1, idA=123, idB=456]
Entity [mainId=2, idA=123, idB=789]
Entity [mainId=0, idA=123, idB=456]
Composite id set:
Entity [mainId=0, idA=123, idB=456]
Entity [mainId=2, idA=123, idB=789]
See this, Here I override the equals() and hashcode() to ensure uniqueness on "name" field of a Person object
public class SetObjectEquals {
Person p1 = new Person("harley");
Person p2 = new Person("harley");
public void method1() {
Set<Person> set = new HashSet<Person>();
set.add(p1);
set.add(p2);
System.out.println(set);
}
public static void main(String[] args) {
SetObjectEquals obj = new SetObjectEquals();
obj.method1();
}
}
class Person {
String name;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.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;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
Person(String name) {
this.name = name;
}
}

Java HashMap not finding key, but it should

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.

Categories