The following code is not giving me the result I'm expecting:
public static void main (String[] args) {
Set<Pair> objPair = new LinkedHashSet<Pair>();
objPair.add(new Pair(1, 0));
System.out.println("Does the pair (1, 0) exists already? "+objPair.contains(new Pair(1, 0)));
}
private static class Pair {
private int source;
private int target;
public Pair(int source, int target) {
this.source = source;
this.target = target;
}
}
The result will be:
Does the pair (1, 0) exists already? false
I can't understand why it's not working.
Or maybe I'm using the "contains" method wrong (or for the wrong reasons).
There is also another issue,
if I add the same value twice, it will be accepted, even being a set
objPair.add(new Pair(1, 0));
objPair.add(new Pair(1, 0));
It won't accept/recognize the class Pair I've created?
Thanks in Advance.
You need to override your hashCode and equals methods in your Pair class. LinkedHashSet (and other Java objects that use hash codes) will use them to locate and find your Pair objects.
Without your own hashCode() implementation, Java considers two Pair objects equal only if they are the exact same object and new, by definition, always creates a 'new' object. In your case, you want Pair objects to be consider equal if they have the same values for source and target -- to do this, you need to tell Java how it should test Pair objects for equality. (and to make hash maps work the way you expect, you also need to generate a hash code that is consistent with equals -- loosely speaking, that means equal objects must generate the same hashCode, and unequal objects should generate different hash codes.
Most IDEs will generate decent hashcode() and equals() methods for you. Mine generated this:
#Override
public int hashCode() {
int hash = 3;
hash = 47 * hash + this.source;
hash = 47 * hash + this.target;
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Pair other = (Pair) obj;
if (this.source != other.source) {
return false;
}
if (this.target != other.target) {
return false;
}
return true;
}
Related
I was asked this in interview. using Google Guava or MultiMap is not an option.
I have a class
public class Alpha
{
String company;
int local;
String title;
}
I have many instances of this class (in order of millions). I need to process them and at the end find the unique ones and their duplicates.
e.g.
instance --> instance1, instance5, instance7 (instance1 has instance5 and instance7 as duplicates)
instance2 --> instance2 (no duplicates for instance 2)
My code works fine
declare datastructure
HashMap<Alpha,ArrayList<Alpha>> hashmap = new HashMap<Alpha,ArrayList<Alpha>>();
Add instances
for (Alpha x : arr)
{
ArrayList<Alpha> list = hashmap.get(x); ///<<<<---- doubt about this. comment#1
if (list == null)
{
list = new ArrayList<Alpha>();
hashmap.put(x, list);
}
list.add(x);
}
Print instances and their duplicates.
for (Alpha x : hashmap.keySet())
{
ArrayList<Alpha> list = hashmap.get(x); //<<< doubt about this. comment#2
System.out.println(x + "<---->");
for(Alpha y : list)
{
System.out.print(y);
}
System.out.println();
}
Question: My code works, but why? when I do hashmap.get(x); (comment#1 in code). it is possible that two different instances might have same hashcode. In that case, I will add 2 different objects to the same List.
When I retrieve, I should get a List which has 2 different instances. (comment#2) and when I iterate over the list, I should see at least one instance which is not duplicate of the key but still exists in the list. I don't. Why?. I tried returning constant value from my hashCode function, it works fine.
If you want to see my implementation of equals and hashCode,let me know.
Bonus question: Any way to optimize it?
Edit:
#Override
public boolean equals(Object obj) {
if (obj==null || obj.getClass()!=this.getClass())
return false;
if (obj==this)
return true;
Alpha guest = (Alpha)obj;
return guest.getLocal()==this.getLocal()
&& guest.getCompany() == this.getCompany()
&& guest.getTitle() == this.getTitle();
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (title==null?0:title.hashCode());
result = prime * result + local;
result = prime * result + (company==null?0:company.hashCode());
return result;
}
it is possible that two different instances might have same hashcode
Yes, but hashCode method is used to identify the index to store the element. Two or more keys could have the same hashCode but that's why they are also evaluated using equals.
From Map#containsKey javadoc:
Returns true if this map contains a mapping for the specified key. More formally, returns true if and only if this map contains a mapping for a key k such that (key==null ? k==null : key.equals(k)). (There can be at most one such mapping.)
Some enhancements to your current code:
Code oriented to interfaces. Use Map and instantiate it by HashMap. Similar to List and ArrayList.
Compare Strings and Objects in general using equals method. == compares references, equals compares the data stored in the Object depending the implementation of this method. So, change the code in Alpha#equals:
public boolean equals(Object obj) {
if (obj==null || obj.getClass()!=this.getClass())
return false;
if (obj==this)
return true;
Alpha guest = (Alpha)obj;
return guest.getLocal().equals(this.getLocal())
&& guest.getCompany().equals(this.getCompany())
&& guest.getTitle().equals(this.getTitle());
}
When navigating through all the elements of a map in pairs, use Map#entrySet instead, you can save the time used by Map#get (since it is supposed to be O(1) you won't save that much but it is better):
for (Map.Entry<Alpha, List<Alpha>> entry : hashmap.keySet()) {
List<Alpha> list = entry.getValuee();
System.out.println(entry.getKey() + "<---->");
for(Alpha y : list) {
System.out.print(y);
}
System.out.println();
}
Use equals along with hashCode to solve the collision state.
Steps:
First compare on the basis of title in hashCode()
If the title is same then look into equals() based on company name to resolve the collision state.
Sample code
class Alpha {
String company;
int local;
String title;
public Alpha(String company, int local, String title) {
this.company = company;
this.local = local;
this.title = title;
}
#Override
public int hashCode() {
return title.hashCode();
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Alpha) {
return this.company.equals(((Alpha) obj).company);
}
return false;
}
}
...
Map<Alpha, ArrayList<Alpha>> hashmap = new HashMap<Alpha, ArrayList<Alpha>>();
hashmap.put(new Alpha("a", 1, "t1"), new ArrayList<Alpha>());
hashmap.put(new Alpha("b", 2, "t1"), new ArrayList<Alpha>());
hashmap.put(new Alpha("a", 3, "t1"), new ArrayList<Alpha>());
System.out.println("Size : "+hashmap.size());
Output
Size : 2
This question already has answers here:
Why do I need to override the equals and hashCode methods in Java?
(31 answers)
Closed 9 years ago.
I have a hashmap which key is an object of my inner class "Key".
My problem is that when I use get(key) it never gives anything back. Since get works with equals I have overwritten equals in my Key class, so it should work for the get method, but apparently it does not.
Any suggestions?
CODE:
public class Infrastruktur
{
private Zuechter online;
private HashMap<Key,Zuechter> zuechter;
Infrastruktur()
{
zuechter = new HashMap<Key,Zuechter>();
}
}
public void login(String name, String passwort)
{
Key hashMapKey = new Key(name, passwort);
if(this.zuechter.get(hashMapKey) != null)
this.online = this.zuechter.get(hashMapKey);
}
public void register(String name, String passwort)
{
if(name != null && passwort != null)
{
this.zuechter.put(new Key(name,passwort),new Zuechter());
login(name, passwort);
}
}
public void logOut()
{
this.online = null;
}
public Zuechter getOnline() {
return this.online;
}
private class Key
{
String name;
String passwort;
Key(String name, String passwort)
{
this.name = name;
this.passwort = passwort;
}
#Override
public boolean equals(Object o)
{
if (o == null) return false;
if (o == this) return true;
if (!(o instanceof Key)) return false;
Key key = (Key)o;
if(this.name.equals(key.name) && this.passwort.equals(key.passwort)) return true;
return false;
}
}
/* Testing */
public static void main(String[] args)
{
Infrastruktur inf = new Infrastruktur();
inf.register("Jakob", "passwort");
inf.logOut();
inf.login("Jakob", "passwort");
System.out.println(inf.getOnline().test());
}
}
If I run the class this is the output I get:
not found
not found
Exception in thread "main" java.lang.NullPointerException
at Infrastruktur.main(Infrastruktur.java:105)
You should also implement hashCode() for your Key class. An example implementation could be:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + name.hashCode();
result = prime * result + passwort.hashCode();
return result;
}
Use Eclipse to generate the hashCode method of your class. In any Map scenario, Java hashes the key value to allow 0(1) read access.
It simply hashes to jump to a reference if found. All Java IDEs have a Generate hashCode and equals option. A simple example, with null checks omitted.
#Override
public int hashCode() {
int hash = 3;
hash = 7 * hash + this.name.hashCode();
hash = 7 * hash + this.passwort.hashCode();
return hash;
}
You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object.hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.
from Effective Java, by Joshua Bloch
tl;dr either generate hashCode() manually,
#Override
public int hashCode() {
int hash = 31;
hash = 29 * hash + Objects.hashCode(name);
hash = 29 * hash + Objects.hashCode(passwort);
return hash;
}
use IDE hashCode generation, or just use generic (albeit slower)
#Override
public int hashCode() {
return Objects.hash( name, passwort );
}
... you can even write a generic hashCode() for any class using reflection (very slow, but good as placeholder)
btw, omitting null checks in hashCode() for mutable or immutable objects with null as a valid field value is one of the easiest ways to introduce bugs into code - that's exactly why either explicit check or Objects.hashCode() is needed.
I want to compare database dump to xml and *.sql. In debagge toRemove and toAdd only differ in dimension. toRemove has size 3, toAdd has size 4. But after running the code, removeAll, toRemove has size 3 and toAdd has size 4. What's wrong?
final DBHashSet fromdb = new DBHashSet(strURL, strUser, strPassword);
final DBHashSet fromxml = new DBHashSet(namefile);
Set<DBRecord> toRemove = new HashSet<DBRecord>(fromdb);
toRemove.removeAll(fromxml);
Set<DBRecord> toAdd = new HashSet<DBRecord>(fromxml);
toAdd.removeAll(fromdb);
Update:
public class DBRecord {
public String depcode;
public String depjob;
public String description;
public DBRecord(String newdepcode, String newdepjobe, String newdesc) {
this.depcode = newdepcode;
this.depjob = newdepjobe;
this.description = newdesc;
}
public String getKey() {
return depcode + depjob;
}
public boolean IsEqualsKey(DBRecord rec) {
return (this.getKey().equals(rec.getKey()));
}
public boolean equals(Object o) {
if (o == this)
return true;
if (o == null)
return false;
if (!(getClass() == o.getClass()))
return false;
else {
DBRecord rec = (DBRecord) o;
if ((rec.depcode.equals(this.depcode)) && (rec.depjob.equals(this.depjob)))
return true;
else
return false;
}
}
}
In order to properly use HashSet (and HashMap, for that matter), you must implement a hashCode() as per the following contract:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
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.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
The code you've supplied for DBRecord does not overide it, hence the problem.
You'd probably want to override it in the following way, or something similar:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + depcode.hashCode();
result = prime * result + depjob.hashCode());
return result;
}
If I have a map and an object as map key, are the default hash and equals methods enough?
class EventInfo{
private String name;
private Map<String, Integer> info
}
Then I want to create a map:
Map<EventInfo, String> map = new HashMap<EventInfo, String>();
Do I have to explicitly implement hashCode() and equals()? Thanks.
Yes, you do. HashMaps work by computing the hash code of the key and using that as a base point. If the hashCode function isn't overriden (by you), then it will use the memory address, and equals will be the same as ==.
If you're in Eclipse, it'll generate them for you. Click Source menu → Generate hashCode() and equals().
If you don't have Eclipse, here's some that should work. (I generated these in Eclipse, as described above.)
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((info == null) ? 0 : info.hashCode());
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 (!(obj instanceof EventInfo)) {
return false;
}
EventInfo other = (EventInfo) obj;
if (info == null) {
if (other.info != null) {
return false;
}
} else if (!info.equals(other.info)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
Yes, you need them else you won't be able to compare two EventInfo (and your map won't work).
Strictly speaking, no. The default implementations of hashCode() and equals() will produce results that ought to work. See http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode()
My understanding is that the default implementation of hashCode() works by taking the object's address in memory and converting to integer, and the default implementation of equals() returns true only if the two objects are actually the same object.
In practice, you could (and should) probably improve on both of those implementations. For example, both methods should ignore object members that aren't important. In addition, equals() might want to recursively compare references in the object.
In your particular case, you might define equals() as true if the two objects refer to the same string or the two strings are equal and the two maps are the same or they are equal. I think WChargin gave you pretty good implementations.
Depends on what you want to happen. If two different EventInfo instances with the same name and info should result in two different keys, then you don't need to implement equals and hashCode.
So
EventInfo info1 = new EventInfo();
info1.setName("myname");
info1.setInfo(null);
EventInfo info2 = new EventInfo();
info2.setName("myname");
info2.setInfo(null);
info1.equals(info2) would return false and info1.hashCode() would return a different value to info2.hashCode().
Therefore, when you are adding them to your map:
map.put(info1, "test1");
map.put(info2, "test2");
you would have two different entries.
Now, that may be desired behaviour. For example, if your EventInfo is collecting different events, two distinct events with the same data may well want to be desired to be two different entries.
The equals and hashCode contracts is also applicable in a Set.
So for example, if your event info contains mouse clicks, it may well be desired that you would want to end up with:
Set<EventInfo> collectedEvents = new HashSet<EventInfo>();
collectedEvents.add(info1);
collectedEvents.add(info2);
2 collected events instead of just 1...
Hope I'm making sense here...
EDIT:
If however, the above set and map should only contain a single entry, then you could use apache commons EqualsBuilder and HashCodeBuilder to simplify the implementation of equals and hashCode:
#Override
public boolean equals(Object obj) {
if (obj instanceof EventInfo) {
EventInfo other = (EventInfo) obj;
EqualsBuilder builder = new EqualsBuilder();
builder.append(name, other.name);
builder.append(info, other.info);
return builder.isEquals();
}
return false;
}
#Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
builder.append(name);
builder.append(info);
return builder.toHashCode();
}
EDIT2:
It could also be appropriate if two EventInfo instances are considered the same, if they have the same name, for example if the name is some unique identifier (I know it's a bit far fetched with your specific object, but I'm generalising here...)
I have a hashmap:
Map<LotWaferBean, File> hm = new HashMap<LotWaferBean, File>();
LotWaferBean lw = new LotWaferBean();
... //populate lw
if (!hm.containsKey((LotWaferBean) lw)) {
hm.put(lw, triggerFiles[l]);
}
The code for LotWaferBean:
#Override
public boolean equals(Object o) {
if (!(o instanceof LotWaferBean)) {
return false;
}
if (((LotWaferBean) o).getLotId().equals(lotId)
&& ((LotWaferBean) o).getWaferNo() == waferNo) {
return true;
}
return false;
}
In my IDE I put breakpoints in equals() but it is never executed. Why?
Try putting a breakpoint in hashCode().
If the hashCode() of two objects in a map return the same number, then equals will be called to determine if they're really equal.
JVM checks the hashcode bucket of that object's hashcode, if there are more objects with the same hashcode, then only, the equals() method will be executed. And, the developer should follow correct contract between the hashCode() and equals() methods.
Only if 2 hashCodes equal, equals() will be called during loop keys.
Only if 2 hashCodes equal, equals() will be called during loop keys.
this is the correct answer... or almost. Precisely, if 2 hash codes collide (being the same ensures they are bound to collide under proper hashmap impl), only then equality check is performed.
BTW, your equal method is most likely incorrect. In case LotWaferBean is overridden, your equals method will accept the subclass instance, but will your subclass also do?
It better should read:
#Override
public boolean equals(Object o) {
if (o == null || o.getClass() != getClass()) { // << this is important
return false;
}
final LotWaferBean other = (LotWaferBean)o;
return other.getLotId().equals(lotId)
&& other.getWaferNo() == waferNo);
}
As Abimaran Kugathasan noted, the HashMap implementation uses hash-buckets to efficiently look up keys, and only uses equals() to compare the keys in the matching hash-bucket against the given key. It's worth noting that keys are assigned to hash-buckets when they are added to a HashMap. If you alter keys in a HashMap after adding them, in a way that would change their hash code, then they won't be in the proper hash-bucket; and trying to use a matching key to access the map will find the proper hash-bucket, but it won't contain the altered key.
class aMutableType {
private int value;
public aMutableType(int originalValue) {
this.value = originalValue;
}
public int getValue() {
return this.value;
}
public void setValue(int newValue) {
this.value = newValue;
}
#Override
public boolean equals(Object o) {
// ... all the normal tests ...
return this.value == ((aMutableType) o).value;
}
#Override
public int hashCode() {
return Integer.hashCode(this.value);
}
}
...
Map<aMutableType, Integer> aMap = new HashMap<>();
aMap.put(new aMutableType(5), 3); // puts key in bucket for hash(5)
for (aMutableType key : new HashSet<>(aMap.keySet()))
key.setValue(key.getValue()+1); // key 5 => 6
if (aMap.containsKey(new aMutableType(6))
doSomething(); // won't get here, even though
// there's a key == 6 in the Map,
// because that key is in the hash-bucket for 5
This can result in some pretty odd-looking behavior. You can set a breakpoint just before theMap.containsKey(theKey), and see that the value of theKey matches a key in theMap, and yet the key's equals() won't be called, and containsKey() will return false.
As noted here https://stackoverflow.com/a/21601013 , there's actually a warning the JavaDoc for Map regarding the use of mutable types for keys. Non-hash Map types won't have this particular problem, but could have other problems when keys are altered in-place.