Checking the key in a Map - java

Can anyone suggest me why the if condition is not working in the below code as the record has key as SiteId.
while (!pdsxOutRecords.isEmpty()) {
PdsxRecord record = pdsxOutRecords.remove(0);
// The below if condition is not working
if(record.getAttrs().containsKey("SiteId")) {
System.out.println("Testing");
}
}
And PdsxRecord class is like this
public class PdsxRecord
{
private String m_key;
private Map<PdsxAttrKey, PdsxAttrValue> m_mapAttrs;
}
// constructor
public PdsxRecord(String key, Map<PdsxAttrKey, PdsxAttrValue> mapAttrs)
{
m_key = key;
m_mapAttrs = mapAttrs;
}
public String getKey()
{
return m_key;
}
public Map<PdsxAttrKey, PdsxAttrValue> getAttrs()
{
return m_mapAttrs;
}
Below thing gets printed by using record.getAttrs()
{Gem.2036=null, Gem.2037=null, Gem.2038=com.ebay.pdsx.common.PdsxAttrValue#6b306b30, Gem.2039=null, Gem.10230=null, Gem.10117=null, Gem.10119=null, Gem.10240=null, UID=com.ebay.pdsx.common.PdsxAttrValue#1e501e50, Gem.10001=null, Gem.10002=com.ebay.pdsx.common.PdsxAttrValue#5d095d09, Gem.10003=null, Gem.10246=null, Gem.10247=null, Gem.60001=null, Gem.10007=null, Gem.10009=null, GEM_ROUTING.PartnerLastModifiedDate=null, Gem.70006=null, CGUID=com.ebay.pdsx.common.PdsxAttrValue#1e361e36, Gem.10173=null, Gem.10097=null, Gem.10131=null, Gem.10010=null, Gem.10132=null, Gem.10177=null, Gem.10178=null, Gem.10179=null, Gem.10015=null, TimeStamp=com.ebay.pdsx.common.PdsxAttrValue#1e571e57, Gem.10016=com.ebay.pdsx.common.PdsxAttrValue#645e645e, Gem.10018=null, Gem.10019=null, Gem.2025=null, SiteId=com.ebay.pdsx.common.PdsxAttrValue#1e3f1e3f, GEM_ROUTING.Partner1LastLoggedInUserId=null, GEM_ROUTING.Partner3LastLoggedInUserId=null, Gem.10181=null, Gem.10182=null, Gem.10183=null, Gem.10185=null, Gem.10187=null, Gem.10101=null, Gem.10189=null, Gem.10102=null, Gem.10026=null, PGuid=com.ebay.pdsx.common.PdsxAttrValue#1e461e46, Gem.2032=null, SGuid=null, Gem.2033=null, Gem.2034=null, Gem.2035=null}
This is the below PdsxRecord class
public class PdsxRecord
{
private String m_key;
private Map<PdsxAttrKey, PdsxAttrValue> m_mapAttrs;
// use the other constructor!
protected PdsxRecord()
{
}
// constructor
public PdsxRecord(String key, Map<PdsxAttrKey, PdsxAttrValue> mapAttrs)
{
m_key = key;
m_mapAttrs = mapAttrs;
}
/**
* get Key
*
* #return
*/
public String getKey()
{
return m_key;
}
/**
* get attributes as a map of key=value
*
* #return
*/
public Map<PdsxAttrKey, PdsxAttrValue> getAttrs()
{
return m_mapAttrs;
}
/**
* String -- for debugging and simple persistence
*/
public String toString()
{
UnsynchronizedStringBuffer buf = new UnsynchronizedStringBuffer();
buf.append("key=" + getKey() + "\n");
if (getAttrs() == null || getAttrs().size() == 0) {
return buf.toString();
}
for (Map.Entry<PdsxAttrKey, PdsxAttrValue> entry : getAttrs().entrySet()) {
String key = (entry.getKey()==null ? "null" : entry.getKey().getKey());
String value = ((entry.getValue() == null ||
entry.getValue().getValue() == null) ?
"null" : entry.getValue().getValue().toString());
buf.append(" " + key + "=" + value +"\n");
}
return buf.toString();
}
}
Updated:- Class for PdsxAttrKey
public class PdsxAttrKey
{
private String m_key;
protected PdsxAttrKey()
{
}
public PdsxAttrKey(String key)
{
m_key = key;
}
public String getKey()
{
return m_key;
}
/**
* Override the default to allow comparing with Strings
*/
public boolean equals(Object o)
{
if (o == null) {
return false;
}
if (o instanceof String) {
return o.equals(m_key);
}
if (o instanceof PdsxAttrKey) {
return m_key.equals(((PdsxAttrKey)o).getKey());
}
return false;
}
/**
* hash code implementation
*/
public int hashCode()
{
return (m_key == null ? 0 : m_key.hashCode());
}
public String toString()
{
return getKey();
}
}

Maybe because you Map is consisting of PdsxAttrKey as a key, and you are checking if there's a key which is a String with value "SiteId".
Here's some code that might be useful if you do not want to change your Map definition from Map<PdsxAttrKey, PdsxAttrValue> to something like Map<String, PdsxAttrValue>:
while (!pdsxOutRecords.isEmpty()) {
PdsxRecord record = pdsxOutRecords.remove(0);
if(record.getAttrs().containsKey(new PdsxAttrKey("SiteId"))) {
System.out.println("Testing");
}
}
Note that this assumes that you can pass a String to the PdsxAttrKey constructor, and that the class can be instantiated. Oh and of course that you have equals() and hashcode() in the class, which pretty much only check the String value for the of the PdsxAttrKey. You might ask yourself if this is really worth the hassle. That's why I originally suggested that your change your Map definition to use Strings as keys, but of course I am not sure if this is a viable solution in your case.

In record.getAttrs() you're returning: Map<PdsxAttrKey, PdsxAttrValue>. Then you checking if there is a key (type: PdsxAttrKey) of type String, value "SiteId".
You should check if map contains PdsxAttrKey (implementing equals and hashcode methods in PdsxAttrKey) or extracting keys from PdsxAttrKey and comparing them with "SiteId".
If you choose to iterate try this:
for(PdsxAttrKey key : record.getAttrs().keySet()) {
if("SiteId".equals(key.getYourKeyStringValue())) {
//found
break;
}
}
Otherwise you should implement equals and hashcode (remember - both) in PdsxAttrKey and invoke contains:
PdsxAttrKey lookupKey = new PdsxAttrKey("SiteId"); //with consideration of `equals` method
if(record.getAttrs().containsKey(lookupKey)) {
...
}

As Xeon says, if you want to compare object with string value, you must override equals and hascode method in the class which is used as key, in this case, PdsxAttrKey. As an example:
public class PdsxAttrKey {
public String name;
public PdsxAttrKey(String name) {
this.name = name;
}
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public boolean equals(Object obj) {
if(obj == null) {
return false;
} else if (obj instanceof PdsxAttrKey) {
return this.name.equals(((PdsxAttrKey)obj).name);
}
return false;
}
}
Or If there is no real need to have an object as key, then you can redefine the map declaration as follows and use strings as key.
private Map<String, PdsxAttrValue> m_mapAttrs;

Related

CacheLoader loading the same keys in multiple times

I am getting duplicate keys in my cacheIterator.
I'm calling a web service using SOAP to rate policies for an insurance company. I am attempting to use a Cachebuilder / loader to store the DTO's as a key and the response from the service as a value. From what I've researched, the .get and .getUnchecked methods will get a value from the cache and if it's not there, it will load that value into the cache.
here is some code:
public class CacheLoaderImpl
{
private static CacheLoaderImpl instance = null;
private static LoadingCache<PolicyDTO, RatingServiceObjectsResponse> responses;
protected CacheLoaderImpl()
{
responses = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<PolicyDTO, RatingServiceObjectsResponse>() {
public RatingServiceObjectsResponse load(PolicyDTO key)
throws Exception
{
return getResponse(key);
}
});
}
public static CacheLoaderImpl getIntance()
{
if(instance == null)
{
instance = new CacheLoaderImpl();
}
return instance;
}
public LoadingCache<PolicyDTO, RatingServiceObjectsResponse> getResponses()
{
return responses;
}
public RatingServiceObjectsResponse getResponse(PolicyDTO key) throws ExecutionException
{
RatingServiceObjectsResponse response = new RatingServiceObjectsResponse();
try
{
response = new CGIRatabaseServiceImpl().getCoverages(key);
}
catch (RemoteException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
return response;
}
}
And this is where I call the get method:
RatingServiceObjectsResponse response = CacheLoaderImpl.getIntance().getResponses().get(policy.toCoveragesCallDTO());
I was under the assumption that maybe it was comparing memory addresses which would be different so I overwrote the toString method to convert the DTO object to JSON. Upon inspecting the cache I can see that the keys are exactly the same with a compare tool. Yet, they're still being stored and calling the service every single time. I tried overwriting the equals method on PolicyDTO but it is never hit when I debug.
How can I make the cacheloader only load values of different keys and pull existing values out as it is originally intended?
I think I just don't have a solid idea how the cacheLoader actually works. I appreciate any help or suggestions.
PolicyDTO class:
public class PolicyDTO extends AbstractDto implements IPolicyDTO
{
private ArrayList<ILineOfBusinessDTO> lobDTOs = new ArrayList<ILineOfBusinessDTO>();
private String pcInd;
private String ratingEffectiveDate;
private String companyName;
public String getPcInd()
{
return pcInd;
}
public void setPcInd(String pcInd)
{
this.pcInd = pcInd;
}
public String getRatingEffectiveDate()
{
return ratingEffectiveDate;
}
public void setRatingEffectiveDate(AdvancedDate ratingEffectiveDate)
{
if(ratingEffectiveDate != null)
{
this.ratingEffectiveDate = ratingEffectiveDate.toFormattedStringMMDDYYYY();
}
else
{
this.ratingEffectiveDate = new AdvancedDate().toFormattedStringMMDDYYYY();
}
}
public String getCompanyName()
{
return companyName;
}
public void setCompanyName(String companyName)
{
this.companyName = companyName;
}
public DtoType getType()
{
return hasGetCoveragesCoverageDTO() ? DtoType.GET_COVERAGE_POLICY : DtoType.RATE_POLICY;
}
public boolean hasGetCoveragesCoverageDTO()
{
if(lobDTOs != null)
{
for(ILineOfBusinessDTO lineDTO : lobDTOs)
{
if(lineDTO.hasGetCoveragesCoverageDTO())
{
return true;
}
}
}
return false;
}
#Override
public void addLob(ILineOfBusinessDTO lob) {
lobDTOs.add(lob);
}
#Override
public Iterator<ILineOfBusinessDTO> getLobIterator() {
return lobDTOs.iterator();
}
public ICoverageDTO findCoverage(String coverageID)
{
ICoverageDTO coverageDTO = null;
for(ILineOfBusinessDTO lineDTO : lobDTOs)
{
coverageDTO = lineDTO.findCoverage(coverageID);
if(coverageDTO != null)
{
return coverageDTO;
}
}
return null;
}
#Override
public String toString()
{
return JSONConversionUtility.convertPolicyDTO(this);
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result
+ ((companyName == null) ? 0 : companyName.hashCode());
result = prime * result + ((lobDTOs == null) ? 0 : lobDTOs.hashCode());
result = prime * result + ((pcInd == null) ? 0 : pcInd.hashCode());
result = prime
* result
+ ((ratingEffectiveDate == null) ? 0 : ratingEffectiveDate
.hashCode());
return result;
}
#Override
public boolean equals(Object object)
{
if(object instanceof PolicyDTO)
{
return object.toString().equals(this.toString());
}
return false;
}
}
Your PolicyDTO class has hashCode inconsistent with equals - it violates the following rule:
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.
Cache uses hashCode (much like HashMap class does), so when it sees two keys with different hashcodes, it assumes they are not equal.

Java HashMap containsKey returns false for existing object

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;
}
}

HashMap with ArrayLists values returning Null on Get() call

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);
}

Getting null when I call hashTable.get(key)

I have Pair class in my project and I am using hashtable in my application.
After constructing my hashtable, I test that the Pair objects are created and store correctly in the hashtable by printing the content of the the hash, and immediately I am trying to get one of the values using get(key) method, and it always gives me null.
This is my whole Class, Mapping which has a private object of type hashtable
package metastore;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import org.apache.hadoop.hive.ql.parse.ASTNode;
import preprocessingQuery.Pair;
public class Mapping {
private Hashtable<Pair, Pair> hashTable ;
public Mapping(){
hashTable= new Hashtable<Pair, Pair>();
}
public Hashtable<Pair, Pair> getHashTable() {
return hashTable;
}
public void setHashTable(Hashtable<Pair, Pair> hashTable) {
this.hashTable = hashTable;
}
public Pair getMapping( Pair originalPair) {
Pair mappedPair=(hashTable.get(originalPair));
return mappedPair;
}
public ArrayList<Mapping> getPairs(ASTNode an){
ArrayList<Mapping> pairs=new ArrayList<Mapping>();
return pairs;
}
public void print() {
Enumeration<Pair> contentOfHT;
contentOfHT = hashTable.keys();
while(contentOfHT.hasMoreElements()) {
Object str = contentOfHT.nextElement();
System.out.println(str + "\tis mapped to " +
hashTable.get(str));
}
}
public void loadMappingTable() {
String originalTable;
String originalCol;
String mappedTable;
String mappedCol;
Pair originalPair;
Pair mappedPair;
BufferedReader in = null;
try {
in = new BufferedReader(
new FileReader(
"D:\\Documents and Settings\\QUAdmin.STAFF\\Desktop\\mapping.txt"));
String line ;
while ((line = in.readLine()) != null) {
StringTokenizer stok = new StringTokenizer(line, "\t");
originalTable= stok.nextToken();
originalCol= stok.nextToken();
mappedTable= stok.nextToken();
mappedCol= stok.nextToken();
originalPair=new Pair(originalTable,originalCol);
mappedPair=new Pair(mappedTable,mappedCol);
hashTable.put(originalPair, mappedPair);
}
} catch (Exception ex) {
// catch all exceptions as one. This is bad form imho
ex.printStackTrace();
} finally {
try {
if (in != null)
in.close();
} catch (IOException ex) {
}
}
}
public static void main(String[] args)
{
Mapping map=new Mapping();
map.loadMappingTable();
System.out.println("Size: "+ map.getHashTable().size());
System.out.println("The content of the hash table");
map.print();
System.out.println("Testing the mapping");
Pair originalPair=new Pair("table1","table1_name");
System.out.println(map.getMapping(originalPair));
System.out.println(map.getHashTable().get(originalPair));
System.out.println(map.getHashTable());
}
}//end of Mapping Class
And this is the output
Size: 3
The content of the hash table
[table=table1, col=table1_age] is mapped to [table=table1_SNT, col=table1_SNT_age]
[table=table1, col=table1_name] is mapped to [table=table1_SNT, col=table1_SNT_name]
[table=table1, col=table1_id] is mapped to [table=table1_SNT, col=table1_SNT_id]
Testing the mapping
null
null
{[table=table1, col=table1_age]=[table=table1_SNT, col=table1_SNT_age], [table=table1, col=table1_name]=[table=table1_SNT, col=table1_SNT_name], [table=table1, col=table1_id]=[table=table1_SNT, col=table1_SNT_id]}
Thanks
I'd need to see your implementation of Pair. My guess is that you are not correctly implementing equals and hashcode.
[Edit]
Given your implementation of Pair (taken from the comments)
package preprocessingQuery;
public class Pair {
private String table;
private String col;
public Pair(String table, String col) {
super();
this.table = table;
this.col = col;
}
public String getTable() {
return table;
}
public void setTable(String table) {
this.table = table;
}
public String getCol() {
return col;
}
public void setCol(String col) {
this.col = col;
}
#Override public String toString() {
return "[table=" + table + ", col=" + col + "]";
}
}
You are indeed missing equals and hashcode.
Some background: The default implementation of Object.equals and Object.hashCode are based on the memory address of the object (Object reference). From that perspective, all your Pairs are all different as they are different objects.
For any collection implementation to work properly, you need to override the default implementation of equals and hashCode of the objects to be stored in the collection.
For your Pair class, it should look something like this:
#Override
public boolean equals(Object other) {
if (this == other) {
return true; // shortcut for referential equality
}
if (other == null) {
return false; // by definition, 'this' object is not null
}
if (!(other instanceof Pair)) {
return false;
}
Pair otherPair = (Pair) other; // Cast to the known type
// check equality of the members
if (this.table == null) {
if (otherPair.table != null) {
return false;
}
} else if (!this.table.equals(otherPair.table)) {
return false;
}
if (this.col == null) {
if (otherPair.col != null) {
return false;
}
} else if (!this.col.equals(otherPair.col)) {
return false;
}
return true;
}
HashCode follows suite. You should understand and follow the general contract of Hashcode.
#Override
public int hashCode() {
int hash = this.table==null?0:table.hashCode();
hash += 41 * this.col==null?0:col.hashCode();
return hash;
}
This is due to the fact that you did not override the equals and hashCode methods in the class Pair, or at least they are not properly overriden.
When you invoke 'get' on a hashtable, the hashtable will first invoke the hashCode method to find the entry in its table. If hashCode is not properly overridden, then the hashtable will not find your entry.
Secondly, when hashtable has found the entry, it will test that the key of the entry is equals to the one you provided (in case of hashCode clash).
you can override those methods like this:
public int hashCode {
return table.hashCode()+tableName.hashCode();
}
public boolean equals(Object o) {
if (o==this)
return true;
if (o instanceof Pair) {
Pair p = (Pair) o;
return this.table.equals(p.table) && this.tableName.equals(p.tableName);
}
return false;
}
Finally, when you iterate over a Hashtable (and more generally, over a Map), you should not invoke the keys and the do a get(key) but instead, you should iterate directly over the Entries
for(Entry<K,V> e: map.entrySet()) {
System.err.println(e.getKey+" is mapped to "+e.getValue());
}
It is much more efficient because this will not invoke the hashCode and equals methods (as explained above) which can be costly operations.
Redefine equals and hashcode in the class Pair.

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