I try to draw lines between different GridPositions(x,y). Every GridPos has 4 Connections North, East, South, West. The Problem is if I paint a line from GridPos(1,1) to GridPos(2,2) the program will paint also a line in reverse direction between GridPos(2,2) and GridPos(1,1) later.
I tried to solve the problem with this class (WarpGate is the same as GridPos):
public class GateConnection {
private WarpGate gate1 = null;
private WarpGate gate2 = null;
public GateConnection(WarpGate gate1, WarpGate gate2) {
super();
this.gate1 = gate1;
this.gate2 = gate2;
}
#Override
public int hashCode() {
final int prime = 31;
int result = prime * ((gate1 == null) ? 0 : gate1.hashCode());
result += prime * ((gate2 == null) ? 0 : gate2.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;
}
GateConnection other = (GateConnection) obj;
if ((gate1.equals(other.gate1) || gate1.equals(other.gate2)) && (gate2.equals(other.gate2) || gate2.equals(other.gate1))) {
return true;
}
return false;
}
}
This Class could be added to an HashSet and the double painting would be gone but I don't know if the hashValue is always unique.
HashCode of WarpGate (auto-generated by eclipse):
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + gridX;
result = prime * result + gridY;
return result;
}
For now I use an ArrayList. I look if the GateConnection exists, if not then add. But this version takes much more ressources than using a HashSet.
EDIT:
The white rectangles are the connections which are painted, the numbers are the GridPositions(x|y) and the red Arrows are the two directions the rectangle is painted because GridPos(2|2) has a connection to GridPos(4|2) and (4|2) to (2|2)
A TreeSet neither uses hashCode() nor equals(). It uses compareTo(), though you should ensure it is consistent with equals() to respect Set semantics.
For a HashSet, the hashCode() of a stored object does not have to be unique. In fact, you can return the same code for every item if you want and they will still be stored without losing any items, if your equals() is implemented correctly. A good hashCode() will improve performance only.
The only critical rule is that two equal items must generate the same hash code.
Your implementation looks OK as long as you can guarantee that gate1 and gate2 are never equal within the same GateConnection object. If they are equal, two GateConnection objects could have different hash codes but be reported as equal. That would lead to unpredictable behaviour if they are stored in a HashSet.
E.g. GateConnection((1,1), (1,1)) equals GateConnection((1,1), (7,9)) but the hash codes are different.
Related
The criteria is that equals() method where the objects are considered equal if the value of the double variable is within +/- 10 of the other object's value of the double variable.
I'm not sure how to correctly implement hashCode() so that the hashCode would be equal if it satisfies the conditions of the equals() method.
I would really appreciate your input! Thanks!
public class Test
{
private double value;
private boolean isEqualValues (final double valueOne, final double valueTwo)
{
if(valueOne == valueTwo)
{
return true;
}
else if((valueOne - valueTwo <= 10) &&
(valueOne - valueTwo >= -10))
{
return true;
}
else
{
return false;
}
#Override
public boolean equals(final Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
Test test = (Test) o;
if(isEqualValues(test.value, value))
{
return true;
}
else
{
return false;
}
}
//How to implement hashCode()
#Override
public int hashCode()
{
//unsure how to correctly implement hashCode() so that the hashCode would be equal if it
//satisfies the conditions of the equals() method above
}
}
There's no way to consistently implement this, since equals() demands transitivity:
It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
new Test(1), new Test(9) and new Test(14) would fail that test (assuming a trivial one-argument constructor that assigns its argument to value).
One way to work around that is to not check for the absolute distance, but "categorize" your objects using some formula, for example take the floor of value / 10 and compare that.
This way some "close" values like new Test(9) and new Test(11) would compare as not-equal, but other than that you'd get a similar result to what you described.
private long getEquivalenceGroup() {
return Math.floorDiv((long) value, 10);
}
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Test test = (Test) o;
return test.getEquivalenceGroup() == this.getEquivalenceGroup();
}
#Override
public int hashCode()
{
return Long.hashCode(getEquivalenceGroup());
}
As long as getEquivalenceGroup() is implemented in a stable manner this will produce "groups" of slightly different objects that still compare as equal and has a valid hashCode() implementation.
Note: if you want a comparison as described in the question but you don't necessarily need it to be returned by equals() then adding a boolean isClose(Test other) is perfectly fine. The only problem is you are trying to implement the equals method specifically with that semantic.
You can't and you shouldn't.
You should implement a comparator and do such operations using that.
This question already has answers here:
Java HashSet contains duplicates if contained element is modified
(7 answers)
Closed 7 years ago.
In our application I often see generated equals and hashCode methods.
I don't know why they are overriden however I am worried about that they are generated (I think so as the are very similar). The below example shows the problem. One SomeBean instnace is created and it firlsty exists and then the set doesnt containt this object. It is because the change of "a" value and hashcode is changed.
HashSet caches hashcodes wright? So every change of an objec previously put in Hashset is dangerous?
private class SomeBean{
private Integer a = 0;
public void setA(Integer a) {
this.a = a;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SomeBean someBean = (SomeBean) o;
if (a != null ? !a.equals(someBean.a) : someBean.a != null) return false;
return true;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + a.hashCode();
return result;
}
}
#Test
public void test() throws Exception {
SomeBean sb = new SomeBean();
Set set = new HashSet<>();
set.add(sb);
sb.hashCode();
System.out.println( set.contains(sb));;
sb.setA(4);
System.out.println(set.contains(sb));;
}
Should the hashCode be cached by SomeBean class and never changed? It could look like this:
#Override
public int hashCode() {
if (_hashCode == 0) {
final int prime = 31;
int result = 1;
result = prime * result + a.hashCode();
return result;
} else return _hashCode;
}
}
But now I risk that object with "a" = 0 and every other new object created with "a"=0 and then changed are the same objects..
It won't work. The reason for that is that HashSet is based on the assumption that hash code of an object won't change, but you change value of your object:
sb.setA(4);
that changes the value of a hash code.
Internally hash map is based on an array. Hash code is used to select a position in an array that is used for hash-map implementation and since hash-code is changed from one call to another HashSet is looking into a different position in the array. Since another element in the array is empty HashSet assumes that such element does not exists in the datastructure.
I have a custom class MarioState that I want to use in a HashMap. The class represents a possible state in a state space of the Mario game. Below is a simplified version of the class MarioState.
In my HashMap I want to store these states. However, not ever property in the MarioState is something that should be considered when comparing two MarioState's. For example if one MarioState has the stuck property set to true and a distance of 30 and another MarioState also has the stuck property set to true but a different distance value (e.g. 20) then they still should be considered the same.
I know for this to work in my HashMap I have to implement the .equals() and .hashcode() methods, which is what I did (by letting them be automatically generated by the InteliJ IDE).
public class MarioState{
// Tracking the distance Mario has moved.
private int distance;
private int lastDistance;
// To keep track of if Mario is stuck or not.
private int stuckCount;
private boolean stuck;
public MarioState(){
stuckCount = 0;
stuck = false;
distance = 0;
lastDistance = 0;
}
public void update(Environment environment){
// Computing the distance
int tempDistance = environment.getEvaluationInfo().distancePassedPhys;
distance = tempDistance - lastDistance;
lastDistance = tempDistance;
// If Mario hasn't moved for over 25 turns then this means he is stuck.
if(distance == 0){
stuckCount++;
} else {
stuckCount = 0;
stuck = false;
}
if(stuckCount > 25){ stuck = true; }
}
public float calculateReward(){
float reward = 0f;
reward += distance * 2;
if(stuck){ reward += -20; }
return reward;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MarioState that = (MarioState) o;
if (stuck != that.stuck) return false;
return true;
}
#Override
public int hashCode() {
return (stuck ? 1 : 0);
}
}
The problem is however that when running the code some of the keys are considered different when it shouldn't be according to their .equals() and .hashcode() functions. What can possibly cause this? Did I forget something?
The code used when inserting states in the HashMap (additional information can be provided if necessary):
public float[] getActionsQValues(MarioState state){
if(!table.containsKey(state)) {
float[] initialQvalues = getInitialQvalues(state);
table.put(state, initialQvalues);
return initialQvalues;
}
return table.get(state);
}
A screenshot when I'm in debug mode shows my table containing two keys with different values, but the keys itself are the same (but in the HashMap it is considered different).
Your hash code computation and equality comparison are both based on stuck - but that can change over time.
If you mutate an object after adding it as a key within a hash map, in such a way that the hash code changes, then the key will not be found when you later request it - because the hash code that was stored when the key was first added will no longer be the same as its current hash code.
Wherever possible, try to avoid using mutable objects as keys within a map (even a TreeMap which doesn't use the hash code would have the same problem if you changed the object in a way which would change relative ordering). If you must use mutable objects as keys within a map, you should avoid mutating them after adding them as keys.
I have a class
final class BuildingPair {
int mBA;
int mBB;
public BuildingPair(int pBuildingA,int pBuildingB) {
mBA = pBuildingA;
mBB = pBuildingB;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + mBA;
result = prime * result + mBB;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BuildingPair other = (BuildingPair) obj;
if ((mBA==other.mBA&&mBB==other.mBB)||(mBA==other.mBB&&mBB==other.mBA)) return true;
return false;
}
}
I want to compare two objects , and when both have the same buildings ids they are equal
so they need to be equal in both directions when :
BuildingPair(1,2) vs BuildingPair(2,1)
BuildingPair(1,2) vs BuildingPair(1,2)
BuildingPair(2,1) vs BuildingPair(1,2)
i think equals method is ok, but hashcode is wrong.
You need something that computes the same result whether passed A,B or B,A. There may be far more subtle solutions, but I'd probably just go for:
#Override
public int hashCode() {
return mBA * mBB;
}
Or anything else which uses an operator that is commutative.
Alternatively, you could change your constructor so that it always stores min(a,b) in mBA and max(a,b) in mBB - you can then simplify your comparison code and keep your hash code as it currently is.
You need a symmetric hashcode (hashcode(a,b) == hashcode(b,a)), for example:
return mBB ^ mBA;
(your current code is not symmetric - for example: hascode (2,1) = 1024 but hashcode(1,2) = 994)
Note: this is inspired from the hashcode of Long:
return (int)(value ^ (value >>> 32));
If they are unordered you can use an arbitrary order which simplifies the rest of the code.
public BuildingPair(int pBuildingA,int pBuildingB) {
mBA = Math.min(pBuildingA, pBuildingB);
mBB = Math.max(pBuildingA, pBuildingB);
}
code the rest of the methods as normal and BuildingPair(2,1) will be exactly the same as BuildingPair(1,2)
I've been using ArrayList for my project to store a cricket team players and order them.
I started thinking about using a TreeSet because of its advantage of removing duplicates.
However the problem I'm having is that if for example I create the following two players:
P p1 = new P("Jack","Daniel",33(age),180(height),78(weight),41(games played),2300
(runs scored),41(dismisses))
P p2 = new P("Jack","Daniel",37(age),185(height),79(weight),45(games played),2560
(runs scored),45(dismisses))
Notice that the two players have the same first and last name, but everything else is different. When I try to add these two players to the TreeSet, it considers them duplicates because of the names similarities and removes the second one. Obviously I don't want this to happen and I want the Set to remove a player only if everything he has is the same as another player, and not just the first and last names.
Is there a way of achieving this?
Also my TreeSet takes a Player object.
Originally, this answer neglected the fact that a TreeSet does its comparisons based on compareTo(), rather than equals(). Edits have been made to address this.
You need to define equals(), hashCode() and compareTo() for your Player object correctly. (Since it's a TreeSet and not a HashSet, implementing hashCode() isn't so important - but it's good practice.)
Equals and hashCode need to take into account all of the fields. Eclipse can auto-generate one for you that will look similar to this (Source > Generate hashcode and equals).
If you already have a natural sort order that doesn't use all of the fields, then you could supply a custom comparator to your TreeSet. However, even if you really only want to sort by a subset of the fields, there's nothing stopping you sorting by all fields (with the uninteresting fields only playing a part of the interesting parts are identical). The important thing to note here is that a TreeSet determines equality not by the equals() method, but by compareTo() == 0.
Here's an example equals():
#Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Player that = (Player) obj;
return this.age == that.age &&
this.height == that.height &&
this.weight == that.weight &&
this.games == that.games &&
this.runs == that.runs &&
this.dismisses == that.dismisses &&
this.given.equals(that.given) &&
this.family.equals(that.family);
}
And here's hashcode:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.age;
result = prime * result + this.dismisses;
result = prime * result + this.family.hashCode());
result = prime * result + this.games;
result = prime * result + this.given.hashCode());
result = prime * result + this.height;
result = prime * result + this.runs;
result = prime * result + this.weight;
return result;
}
Finally, here's a compareTo:
public int compareTo(Player that)
{
int result;
result = this.family.compareTo(that.family);
if (result != 0) // is the family name different?
{
return result; // yes ... use it to discriminate
}
result = this.given.compareTo(that.given);
if (result != 0) // is the given name different?
{
return result; // yes ... use it to discriminate
}
result = this.age - that.age; // is the age different?
if (result != 0)
{
return result; // yes ... use it to discriminate
}
... (and so on) ...
... with the final one ...
return this.dismisses - that.dismisses; // only thing left to discriminate by
}
a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal. The behavior of a set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.
From Java Platform Standard Edition 8 Documentation TreeSet part.
class Student implements Comparable<Student> {
String name;
public Student(String name) {
this.name=name;
}
public String toString(){
return name;
}
public int compareTo(Student gStudent) {
if(!this.name.equals(gStudent.getName()))
return 1;
return 0;
}
private String getName() {
return name;
}
}