Using Objects as keys in Java HashMap - java

package test;
import java.util.HashMap;
public class test {
public static void main(String args[]) {
HashMap<ID, String> h = new HashMap<ID, String> ();
String b;
ID id1 = new ID(100);
ID id2 = new ID(200);
ID id3 = new ID(200);
h.put(id1, "apple");
h.put(id2, "pear");
**System.out.println(h.containsKey(id3));**
}
}
class ID {
Integer code;
public ID(Integer id) {
this.code = id;
}
#Override
public int hashCode()
{
return code.hashCode();
}
#Override
public boolean equals(Object o)
{
return this.code.equals(o);
}
}
the output of this program is "false". I can't understand of this result. I implemented hashCode and equals which are overrided. I don't have an idea how to solve. How to use objects as keys in java HashMap?

Your equals implementation is wrong :
public boolean equals(Object o)
{
return this.code.equals(o); // this will never return true,
// since you are comparing an Integer instance
// to an ID instance
}
You should compare this code to the other code:
public boolean equals(Object o)
{
if (!(o instanceof ID))
return false;
ID oid = (ID) o;
return this.code.equals(oid.code);
}

Wrong Implementation of equals method. According to your implementation it executes the Integer class equals method. We should handle the logic without execute library classes equals methods.
#Override
public boolean equals(Object o) {
if (o == null || ((o instanceof ID) && o.hashCode() != this.hashCode())) {
return false;
}
return true;
}

Related

Comparing objects within a set

Right now I have this Java code
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class Dummy {
private String value;
public Dummy(final String value) {
this.value = value;
}
public boolean equals(final Object that) {
return that instanceof Dummy && Objects.equals(value, ((Dummy) that).value);
}
public int hashcode() {
return Objects.hash(value);
}
public static void main(final String... args) {
final Set<Dummy> dummies = new HashSet<>();
dummies.add(new Dummy("toto"));
System.out.println(dummies.contains(new Dummy("toto")));
}
}
The output is "false", and I'm supposed to change it to "true" by changing only one character, but I have absolutely no idea how to do that... Any ideas? Thanks. :)
hashcode() is not a Object's method but hashCode() is.
public int hashcode() {
return Objects.hash(value);
}
should be
public int hashCode() {
return Objects.hash(value);
}
In my case I had lots of objects who all need a equals and hash method. I have used lombok to reduce the work and code.
#Data
#AllArgsConstructor
#EqualsAndHashCode
public class JsonPictureStuff {
private String type;
private String url;
private String width;
private String height;
}
The annotations instruct lombok to add the code for you.
your set is not working because you are implementing equals and hashcode in a not proper way...
specially because you are not even considering the string field
you are avoiding the annotation Override that will hint you about wrongly named methods like hashcode and hashCode
you can do something like
#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;
Dummy other = (Dummy ) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
in ide like eclipse you can do right click and auto generate those methods so you can save the implementation

Equals method in sub classes

Here is the main class.
public class Person {
private String name;
public Person() {
name = "";
}
Its equal method is this
public boolean equals(Object otherObject) {
boolean isEqual = false;
if(otherObject != null && otherObject instanceof Person) {
Person otherPerson = (Person) otherObject;
if(this.name.equals(otherPerson.name))
isEqual = true;
}
return isEqual;
}
I have a subclass that extends the Person class.
public class Student extends Person {
private int studentNumber;
public Student() {
super();
studentNumber = 0;
}
How would I go about writing its equal method that will compare two object of type Student and will compare both variables. Name and studentNumber.
So far i have it similar to the Person class.
public boolean equals(Object otherObject) {
boolean isEqual = false;
if(otherObject != null && otherObject instanceof Student) {
Student otherPerson = (Student) otherObject;
if(this.studentnumber.equals(otherPerson.studentnumber))
isEqual = true;
}
return isEqual;
}
First of all: you need not test for null if you test instanceof; null is not instanceof anything. So, equals() for method can be simplified to:
public boolean equals(final Object obj)
{
if (!(o instanceOf Person))
return false;
final Person other = (Person) obj;
return name.equals(other.name);
}
THis means that in your Student class you may write your equals() method like this:
public boolean equals(final Object obj)
{
if (!super.equals(obj))
return false;
if (!(obj instanceof Student))
return false;
return studentnumber == other.studentnumber;
}
Note: do not forget hashCode().
You can use super.equals(). I have corrected some errors also with the studentNumber comparision.
public boolean equals(Object otherObject) {
boolean isEqual = false;
if (otherObject != null && otherObject instanceof Student) {
Student otherPerson = (Student) otherObject;
if (super.equals(otherObject) && this.studentNumber == otherPerson.studentNumber) {
isEqual = true;
}
}
return isEqual;
}

== vs. equals() references/pointers JAVA

I'm learning about == vs. equals() and doing various examples. For this one, I understand why r==s is false, but why is r.equals(s) false if they now both have the same content?
public class StringProgram{
public static void main(String[] args) {
Person r = new Person("A");
Person s = new Person("J");
s.setName("A");
System.out.println(r.getName());//A
System.out.println(s.getName());//A
System.out.println(r==s);//false
System.out.println(r.equals(s));//false
}
}
Here is the code of Person :
public class Person{
private String name;
public Person(String d){
name=d;
}
public void setName(String a){
name=a;
}
public String getName(){
return name;
}
}
EDIT: I see that I have to override it now, but I still don't understand how the assignment works in the example below. I just don't understand why t.getName() is Keen if t is assigned to u.
public class StringProgram{
public static void main(String[] args) {
Person t = new Person("Gene");
Person u = t;
u.setName("Keen");
System.out.println(t.getName());//Keen
System.out.println(t.equals(u));//true
}
}
All classes in java inherit from the Object class. When you do an r.equals(s), it uses the .equals() method as defined in Object class. To make it work as you desire, you need to define the .equals() method for the Person class by overriding the .equals() method.
Eg. Add this in your Person class:
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
Note: I used eclipse to generate the equals and hashcode method for me. You may read this thread to understand why hashcode() needs to be overridden.
equals() is just a method, there's nothing special about it. In particular, it doesn't automatically know how to compare the content of two objects.
The equals() method of class Object does the same thing as ==. If you do not override the equals() method in your class Person, then it's not automatically going to compare the content of Person objects.
You have to override the equals() method in class Person so that it does the comparison in the way you want.
public class Person {
private String name;
// ...
#Override
public boolean equals(Object o) {
if (!(o instanceof Person)) {
return false;
}
return ((Person) o).name.equals(this.name);
}
}
Java can't guess what you want to do, you have to tell it that two persons are the same if they have the same name. You must override equals :
#Override
public boolean equals(Object o) {
if (o == this) return true;
if (o == null) return false;
if (!(o instanceof Person)) return false;
Person that = (Person) o;
if (name == null && that.name == null) return true;
if (name == null || that.name == null) return false;
return that.name.equals(name);
}
All java classes inherits from Object, and to properly compare your class you should override the equals() method.
public Person(String d){
name=d;
}
public void setName(String a){
name=a;
}
public String getName(){
return name;
}
#Override
public boolean equals(Object obj) {
return obj instanceof Person &&
this.getName().equals(((Person)obj).getName());
}

Overriding hashcode and equals method in java?

I have the classes below:
public class Sample implements java.io.Serializable{
//POJO with two fields and getters/setters
private String name;
private Integer id;
//This POJO does not override equals() and hashCode()
}
public class B{
private Sample sample;
//here i need override hashcode and equals() based on **sample** property.
}
When i tried overriding equals() and hashCode() in the B class I got the error below in Eclipse.
The field type com.mypackage.Sample does not implement hashCode() and equals() - The resulting code may not work correctly.
Now how can I compare two B instances whether equals or not based on the Sample property?
I cannot modify Sample class.
are you looking something like following? Just try it, as from your question i think you want to compare contents of your Sample class also.
class Sample implements java.io.Serializable{
//POJO with two fields and getters/setters
private String name;
private Integer id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
//This POJO does not override equals() and hashCode()
}
public class Beta implements Comparable{
private Sample sample;
public Sample getSample() {
return sample;
}
public void setSample(Sample sample) {
this.sample = sample;
}
#Override
public int compareTo(Object o) {
if(!(o instanceof Beta)){
return -1;
}
}if(((Beta)o).getSample().getName().equals(this.sample.getName())){
return 0; // return true if names are equal
}
if(((Beta)o).getSample().getId().equals(this.sample.getId())){
//if name are notequal and IDs are equal, do what you need to do
}
return -1;
}
public static void main(String[] args) {
Beta b = new Beta();
Sample s = new Sample();
s.setId(10);
s.setName("Name1");
b.setSample(s);
Beta b2 = new Beta();
Sample s2 = new Sample();
s2.setId(20);
s2.setName("Name2");
b2.setSample(s2);
System.out.println(b2.compareTo(b));
Beta b3 = new Beta();
Sample s3 = new Sample();
s3.setId(10);
s3.setName("Name1");
b3.setSample(s3);
System.out.println(b3.compareTo(b));
}
}
Overriding approach
class Sample implements java.io.Serializable{
//POJO with two fields and getters/setters
private String name;
private Integer id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
//This POJO does not override equals() and hashCode()
}
public class Beta /*implements Comparable*/{
private Sample sample;
public Sample getSample() {
return sample;
}
public void setSample(Sample sample) {
this.sample = sample;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Beta other = (Beta) obj;
if ((this.getSample() == null) && (other.getSample() == null)){
return true;
}
if ((this.getSample().getId().equals(other.getSample().getId())) && (this.getSample().getName().equals(other.getSample().getName()))) {
return true;
}
return false;
}
#Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.getSample().getName() != null ? this.getSample().getName().hashCode() : 0);
hash = 53 * hash + (this.getSample().getId() != null ? this.getSample().getId().hashCode() : 0);
return hash;
}
/* #Override
public int compareTo(Object o) {
if(!(o instanceof Beta)){
return -1;
}
if(((Beta)o).getSample().getId().equals(this.sample.getId()) && ((Beta)o).getSample().getName().equals(this.sample.getName())){
return 0;
}
return -1;
}*/
public static void main(String[] args) {
Beta b = new Beta();
Sample s = new Sample();
s.setId(10);
s.setName("Name1");
b.setSample(s);
Beta b2 = new Beta();
Sample s2 = new Sample();
s2.setId(20);
s2.setName("Name2");
b2.setSample(s2);
System.out.println(b2.equals(b));
Beta b3 = new Beta();
Sample s3 = new Sample();
s3.setId(10);
s3.setName("Name1");
b3.setSample(s3);
System.out.println(b3.equals(b));
}
If you don't explicitly override .equals(), they will be compared based solely off of their references (despite not having a equals(), every object inherits one from Object). If you only want B to be compared based off of Sample, then simply do the following:
#Override
public boolean equals(Object o)
{
if (o istanceof B)
{
return sample.equals(o.sample)
}
return false;
}
Additionally, you should then override hashCode() (and compareTo()) to maintain the contract between equals() and hashCode(). Hence, you should also have the following:
#Override
public int hashCode()
{
return sample.hashCode();
}
EDIT (in response to comment):
My requirement is first i need to check equals property against "name"
property of Sample. IF names are equals then both objects are equal.
If names are not equals then i need to check for equality against "ID"
property of Sample. How can i do that? Thanks!
Determining whether Samples are equivalent should be handled in Sample, by overriding equals(). If equals() for Sample bases off of name and id, then you're fine. If you want to compare Samples in B differently than they are normally compared, then you're not going to be able to maintain the contract between equals() and hashCode() for B if you use hashCode() or equals() from Sample, which means that your hashCode() and equals() for B should be cannot call equals() or hashCode() from Sample. See this tutorial for how to override based on specific fields.

how to insert into set without changing the equals and hashcode

I'm looking for a suggestion.
I have a Person class with String firstName and String lastName
When i'm tying to insert the list values with the same String like :
set.add(new Person("firstName","lastName"))
set.add(new Person("firstName","lastName"))
The set doesn`t filter the objects and they still getting in the set.
There is any suggestion to create set list without overriding the equales and hashcode functions?
Maybe with guava or some groovy list?
Thanks,
Or.
In Guava there's an Equivalence class designed to such things. Create your own Equivalence class like this one:
import com.google.common.base.Equivalence;
import com.google.common.base.Objects;
public class PersonEquivalence extends Equivalence<Person> {
#Override
protected boolean doEquivalent(Person p1, Person p2) {
return Objects.equal(p1.getFistName(), p2.getFistName())
&& Objects.equal(p1.getLastName(), p2.getLastName());
}
#Override
protected int doHash(Person person) {
return Objects.hashCode(person.getFistName(), person.getLastName());
}
}
And then this code
Set<Equivalence.Wrapper<Person>> set = Sets.newHashSet();
PersonEquivalence personEquivalence = new PersonEquivalence();
set.add(personEquivalence.wrap(new Person("Joe", "Doe")));
set.add(personEquivalence.wrap(new Person("Joe", "Doe")));
set.add(personEquivalence.wrap(new Person("Jane", "Doe")));
System.out.println(set);
prints
[PersonEquivalence#8813f2.wrap(Person{firstName=Jane, lastName=Doe}),
PersonEquivalence#8813f2.wrap(Person{firstName=Joe, lastName=Doe})]
Of course it's a bit verbose, but you can create ForwardingSet to automatically wrap and unwrap Persons for you.
You can create a TreeSet with your own Comparator.
Set<Person> set = new TreeSet<Person>(new Comparator<Person>() {
#Override
public int compare(Person p1, Person p2) {
// Your own compare logic
}
});
You can't, without violating the contract of Set. Either don't use a Set, or wrap the Person in another class that implements equals and hashcode based on the inner Person (see the other answer for a way to do this in Guava).
Here's a rough attempt at my map suggestion.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class PeopleCarrier implements Iterable<Person>{
private Map<PersonKey, Person> storage = new HashMap<PersonKey, Person>();
public void add(Person p) {
PersonKey pk = new PersonKey(p);
storage.put(pk, p);
}
public boolean contains(Person p) {
return storage.containsKey(new PersonKey(p));
}
#Override
public Iterator<Person> iterator() {
return new Iterator<Person>() {
private Iterator<PersonKey> i = storage.keySet().iterator();
#Override
public void remove() {
throw new UnsupportedOperationException();
}
#Override
public Person next() {
return storage.get(i.next());
}
#Override
public boolean hasNext() {
return i.hasNext();
}
};
}
private class PersonKey {
private String firstname;
private String lastname;
public PersonKey(Person p) {
this.firstname = p.getFirstname();
this.lastname = p.getLastname();
}
/* (non-Javadoc)
* #see java.lang.Object#hashCode()
*/
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result
+ ((firstname == null) ? 0 : firstname.hashCode());
result = prime * result
+ ((lastname == null) ? 0 : lastname.hashCode());
return result;
}
/* (non-Javadoc)
* #see java.lang.Object#equals(java.lang.Object)
*/
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof PersonKey))
return false;
PersonKey other = (PersonKey) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (firstname == null) {
if (other.firstname != null)
return false;
} else if (!firstname.equals(other.firstname))
return false;
if (lastname == null) {
if (other.lastname != null)
return false;
} else if (!lastname.equals(other.lastname))
return false;
return true;
}
private PeopleCarrier getOuterType() {
return PeopleCarrier.this;
}
}
}

Categories