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.
Related
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.
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);
}
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;
i have some problems with some of my methods for my MultiSet class.
This is a tester, and MultiSet class should get the output: "Succes!" if it works correctly.
This is the tester:
public class MultiSetTest {
public static void main(String[] args) {
MultiSet<String> a = new MultiSet<String>();
MultiSet<String> b = new MultiSet<String>();
a.add("Foo");
a.add("Bar");
a.add("Foo");
System.out.println("a:" + a); // test toString
b.add("Bar");
b.add("Foo");
b.add("Bar");
b.add("Foo");
System.out.println("b:" + b);
assert !a.equals(b) : "Failed test 1!"; // test equals
assert b.remove("Bar") : "Failed test 2!"; // test remove
assert a.equals(b) : "Failed test 3!";
for(String s : a) { // test iterator
assert b.remove(s) : "Failed test 4!";
}
assert b.size() == 0 : "Failed test 5!";
Set<String> baseSet = new HashSet<String>(a);
assert baseSet.size()==2 : "Failed test 6!";
b = new MultiSet<String>(a);
assert a.equals(b) : "Failed test 7!";
try {
assert false;
System.out.println("Please enable assertions!");
}
catch(AssertionError e) {
System.out.println("Success!");
}
}
}
And my Multiset class:
public class MultiSet<E> extends AbstractCollection<E>
{
private int size = 0;
private Map<E, Integer> values = new HashMap<E, Integer>();
public MultiSet()
{
}
public MultiSet(Collection<E> c)
{
addAll(c);
}
public boolean add()
{
return false;
}
public boolean remove()
{
return false;
}
public Iterator<E> iterator()
{
return new Iterator<E>()
{
private Iterator<E> iterator = values.keySet().iterator();
private int remaining = 0;
private E current = null;
public boolean hasNext()
{
return remaining > 0 || iterator.hasNext();
}
public E next()
{
if (remaining == 0)
{
current = iterator.next();
remaining = values.get(current);
}
remaining--;
return current;
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
public boolean equals(Object object)
{
if (this == object) return true;
if (this == null) return false;
if (this.getClass() != object.getClass()) return false;
MultiSet<E> o = (MultiSet<E>) object;
return o.values.equals(values);
}
public int hashCode()
{
return values.hashCode()*163 + new Integer(size).hashCode()*389;
}
public String toString()
{
String res = "";
for (E e : values.keySet());
//res = ???;
return getClass().getName() + res;
}
public int size()
{
return size;
}
}
Maybe if you could help me on the way with either add or remove, then i can probably work the other one out.
Also, my equals doesn't appear to work correctly,
and I'm unsure about how to work out "res" at String toString. Don't mind my return statement, i'll throw in some brackets etc. later to make it look good.
Thank you for your help.
// Chris
Why not on of use well-tested Google Guavas's Multisets instead of reinventing the wheel? You can choose one of many implementations:
ConcurrentHashMultiset,
EnumMultiset,
ForwardingMultiset,
HashMultiset,
ImmutableMultiset,
LinkedHashMultiset,
TreeMultiset
what should cover your use case or - if you really want - implement Multiset interface by yourself, looking into sources of default implemntations.
EDIT:
Your implementation breaks Collecion interface contract - you cannot return false for add(E e). Read Collection docs:
boolean add(E e)
Parameters:
e - element whose presence in this collection is to be ensured
Returns:
true if this collection changed as a result of the call
Throws:
UnsupportedOperationException - if the add operation is not supported by this collection
If you want to use read-only Multiset use either ImmutableMultiset (more specifically ImmutableMultiset.copyOf(Iterable)) or implement Multiset
's interface add(E e) method throwing UnsupportedOperationException.