HashMap doesn't work with user defined key class [duplicate] - java

This question already has answers here:
Overriding the java equals() method - not working?
(8 answers)
Closed 7 years ago.
I am using Processing language (derived from Java PApplet) version 3.01
Here is my code...
Pig a = new Pig(1);
Pig b = new Pig(1);
HashMap<Pig, String> m = new HashMap<Pig, String>();
m.put(a,"foo");
String c = m.get(b);
println(c);
class Pig {
int a;
Pig(int x) { a=x;}
boolean equals(Pig b) { return b.a==a;}
int hashCode() { return a;}
}
As you can see I am using Pig for key, I defined the equals and hashCode. I expect output "foo", however, I get output null.
Any idea what is going on here? Why doesn't this work?

Try that, you didn't override Object methods at all / correctly :
class Pig {
int a;
Pig(int x) {
a = x;
}
#Override
public boolean equals(Object b) {
if(b == null) return false;
if(!(b instanceof Pig)) return false;
if(b == this) return true;
return ((Pig) b).a == a;
}
#Override
public int hashCode() {
return a;
}
}

You didn't override equals(Object), but you did implement an unrelated equals(Pig) method. HashMap uses the former, your method isn't even called.

Related

Why doesn't my custom object Java HashMap handle unique keys? [duplicate]

This question already has an answer here:
When do you need to override hashcode() and equals() when using a hashmap [duplicate]
(1 answer)
Closed 9 months ago.
Below code returns false. Trying to make Equation object act as a key in a custom HashMap. Pretty sure method overrides are implemented correctly, hashCode is the exact same between the two equation instances. What is exactly wrong?
class Solution {
public boolean maxPoints(int[][] points) {
Map<Equation, Integer> map = new HashMap<>();
Equation eq1 = new Equation(1, 2);
Equation eq2 = new Equation(1, 2);
map.put(eq1, 1);
if (map.containsKey(eq1)) return true;
return false;
}
class Equation {
private int slope;
private int yIntercept;
public Equation(int slope, int yIntercept) {
this.slope = slope;
this.yIntercept = yIntercept;
}
public boolean equals(Equation other) {
if (other == this) return true;
return (this.slope == other.slope && this.yIntercept == other.yIntercept);
}
public int hashCode() {
return Objects.hash(this.slope, this.yIntercept);
}
}
}
If you want to overwrite a method annotade it with #Override.
The compiler would give you an error for your equals because you do not overwrite but overload the method
#Override
public boolean equals(Object other) {
...
}
equals needs to be public boolean equals(Object other) to match the signature in Object.
Your equals method doesn't override the one that the code in HashMap is calling.
Usually an equals method looks like this:
#Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof Equation)) {
return false;
}
// instanceof above also covers null
Equation otherEquation = (Equation) other;
return otherEquation.slope == this.slope
&& otherEquation.yIntercept == this.yIntercept;
}
...or use Project Lombok's #EqualsAndHashCode annotation.

HashMap ignoring overridden hashCode and equals methods

I am loading data on network traffic from a file. The information I'm loading is attacker IP address, victim IP address, and date. I've combined these data into a Traffic object, for which I've defined the hashCode and equals functions. Despite this, the HashMap I'm loading them into treats identical Traffic objects as different keys. The entire Traffic object complete with some simple test code in the main method follows:
import java.util.HashMap;
public class Traffic {
public String attacker;
public String victim;
public int date;
//constructors, getters and setters
#Override
public int hashCode() {
long attackerHash = 1;
for (char c:attacker.toCharArray()) {
attackerHash = attackerHash * Character.getNumericValue(c) + 17;
}
long victimHash = 1;
for (char c:victim.toCharArray()) {
victimHash = victimHash * Character.getNumericValue(c) + 17;
}
int IPHash = (int)(attackerHash*victimHash % Integer.MAX_VALUE);
return (IPHash + 7)*(date + 37) + 17;
}
public boolean equals(Traffic t) {
return this.attacker.equals(t.getAttacker()) && this.victim.equals(t.getVictim()) && this.date == t.getDate();
}
public static void main(String[] args) {
Traffic a = new Traffic("209.167.099.071", "172.016.112.100", 7);
Traffic b = new Traffic("209.167.099.071", "172.016.112.100", 7);
System.out.println(a.hashCode());
System.out.println(b.hashCode());
HashMap<Traffic, Integer> h = new HashMap<Traffic, Integer>();
h.put(a, new Integer(1));
h.put(b, new Integer(2));
System.out.println(h);
}
}
I can't speak to the strength of my hash method, but the outputs of the first two prints are identical, meaning it at least holds for this case.
Since a and b are identical in data (and therefore equals returns true), and the hashes are identical, the HashMap should recognize them as the same and update the value from 1 to 2 instead of creating a second entry with value 2. Unfortunately, it does not recognize them as the same and the output of the final print is the following:
{packagename.Traffic#1c051=1, packagename.Traffic#1c051=2}
My best guess at this is that HashMap's internal workings are ignoring my custom hashCode and equals methods, but if that's the case then why? And if that guess is wrong then what is happening here?
The problem here is your equals method, which does not override Object#equals. To prove this, the following will not compile with the #Override annotation:
#Override
public boolean equals(Traffic t) {
return this.attacker.equals(t.getAttacker()) &&
this.victim.equals(t.getVictim()) &&
this.date == t.getDate();
}
The implementation of HashMap uses Object#equals and not your custom implementation. Your equals method should accept an Object as a parameter instead:
#Override
public boolean equals(Object o) {
if (!(o instanceof Traffic)) {
return false;
}
Traffic t = (Traffic) o;
return Objects.equals(attacker, t.attacker) &&
Objects.equals(victim, t.victim) &&
date == t.date;
}

AssertionError - imaginary and real numbers adder

I'm getting error:
java.lang.AssertionError: expected: learning.java.advancedoop2.MyComplex<(2.0+10.0i)> but was: learning.java.advancedoop2.MyComplex<(2.0+10.0i)>
Expected :learning.java.advancedoop2.MyComplex<(2.0+10.0i)>
Actual :learning.java.advancedoop2.MyComplex<(2.0+10.0i)>
I'm now working on MyComplex Class like shown on 3.1:
http://www.ntu.edu.sg/home/ehchua/programming/java/J3f_OOPExercises.html#zz-2
Here's a part of the code that's relevant:
package learning.java.advancedoop2;
public class MyComplex {
private double real = 0.0;
private double imag = 0.0;
public MyComplex() {
}
public MyComplex add(MyComplex right) {
this.imag += right.imag;
this.real += right.real;
return this;
}
}
I tried to make tests, and when I ran them, error that I've got is upside, here's part of my test code:
#Test
public void add() {
MyComplex myComplexFirst = new MyComplex(1, 5);
MyComplex myComplexSecond = new MyComplex(1, 5);
MyComplex myComplexThird = new MyComplex(myComplexFirst.getReal() + myComplexSecond.getReal(), myComplexFirst.getImag() + myComplexSecond.getImag());
myComplexFirst.add(myComplexSecond);
MyComplex newComplex = myComplexFirst;
assertEquals(newComplex, myComplexThird);
}
Did you override the equals method in your custom class ?
If you didn't, the default behavior is to compare the references. That would explain the error message you get.
It is confusing because you have overriden the toString method which displays both instances to have the same values.
You need to override the equals method so that java knows how to compare two MyComplex Objects. Right now it doesn't know that you want to compare the objects based on the real and imag values.
#Override
public boolean equals(Object o) {
// If the object is compared with itself then return true
if (o == this) {
return true;
}
// Check if o is an instance of MyComplex
if (!(o instanceof MyComplex)) {
return false;
}
// typecast o to MyComplex so that we can compare data members
MyComplex c = (MyComplex) o;
// Compare the data members and return accordingly
return Double.compare(real, c.getReal()) == 0
&& Double.compare(imag, c.getImag()) == 0;
}

problems with hashmaps get function [duplicate]

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.

trouble comparing equality between two objects in java [duplicate]

This question already has answers here:
Trouble over riding and using equals method in java
(2 answers)
Closed 9 years ago.
I've been having numerous problems getting this project to work correctly but I'm currently stuck on getting this class to work properly. Whats its suppose to do is take the current station from the radio class and pass it along to this class. The problem is i'm trying to select between AM and FM and display the current station. I'm not sure if i'm using the equals method correctly as it keeps returning 0.0 which is the default value of currentStation.
public class AutoRadioSystem
{
private Radio selectedRadio;
private AMRadio radioAM;
private FMRadio radioFM;
private XMRadio radioXM;
public AutoRadioSystem()
{
selectedRadio = new AMRadio();
}
public double getCurrentStation()
{
if (selectedRadio.equals(radioAM))
{
return radioAM.getCurrentStaion();
}
else if (selectedRadio.equals(radioFM))
{
return radioFM.getCurrentStaion();
}
return 0.0;
}
public void selectRadio()
{
//if (selectedRadio.equals(radioAM))
// selectedRadio = radioFM;
}
public boolean equals (Object o)
{
if (o == null)
return false;
if (! (o instanceof AutoRadioSystem))
return false;
AutoRadioSystem other = (AutoRadioSystem) o;
return this.selectedRadio == other.selectedRadio;
}
public static void main (String [] args) {
AutoRadioSystem c = new AutoRadioSystem();
//c.selectRadio();
double b = c.getCurrentStation();
System.out.println(b);
}
}
Looks like you are doing it wrong (tm).
The Radio interface should expose the getCurrentStaion() method which will be implemented by all 3 radio classes. Then your getCurrentStation() method can just invoke selectedRadio.getCurrentStation() and return its result.
Also you are implementing equals() method in the AutoRadioSystem which will have no effect when comparing Radio instances.
Objects in Java are a bit differents when it comes to comparing them, as you can compare objects in two ways.
You can compare the objects's references, by using ==.
And you can let a self-made method do a customized check, by using o1.equals(o2) where o1,o2 are objects.
Note that o instanceof AutoRadioSystem returns false if o==null and does not throw a NullPointerException.
PS: There are other issues as well as answered in your previous question.
Note that simply with
private Radio selectedRadio;
private AMRadio radioAM;
private FMRadio radioFM;
private XMRadio radioXM;
public AutoRadioSystem()
{
selectedRadio = new AMRadio();
}
radioAM, radioFM, and radioXM are all initialized to null, by default.
Any proper equals() method will immediately return false when comparing with a null reference. So in this
public double getCurrentStation()
{
if (selectedRadio.equals(radioAM))
{
return radioAM.getCurrentStaion();
}
else if (selectedRadio.equals(radioFM))
{
return radioFM.getCurrentStaion();
}
return 0.0;
}
none of the if will evaluate to true, and you will get the last case of returning 0.0.
On top of implementing their equals() method correctly, you'll need to initialize them correctly as well.

Categories

Resources