Let's say I have an object that has a number range as two properties, a start and an end to define a numeric band. I want to load these objects into a HashMap. But when I look up on the hashcode and equals with a key object, I want to match on a given number that falls into the range. So I want the hashcode and equals to take any number in a key, and return the object where it falls between the startRange and endRange. So if an object has a startRange of 7 and endRange of 14, passing 9 in a key would retrieve that object. How do I do this? The equals would be straightforward as I would use >= and <=, but I don't want to break the hashcode... I know I can iterate item by item but I'd like to avoid that for performance reasons.
public class MyClass {
private final int marketID;
private final int startRange;
private final int endRange;
...
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + marketID;
/*I don't want to match on startRange and endRange, I want to fall between it! */
result = prime * result + endRange;
result = prime * result + startRange;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyClass other = (MyClass) obj;
if (endRange!= other.endRange)
return false;
if (marketID != other.marketID)
return false;
if (startRange!= other.startRange)
return false;
return true;
}
}
Related
This question already has an answer here:
Should Equality Comparison of Float / Double Instance Variables in an Equals Method be Exact?
(1 answer)
Closed 3 years ago.
An object of class Foo is considered equal if the double members are within a given range of the other object. Such an error can easily be introduced due to floating point arithmetic.
The method isDoubleEquals and doubleArrayEquals will take care of the equals part but the contract states that the hashcode has to be identical for equal objects.
The default hashcode of doubles will not map close values to the same value, therefore what would be a good approach to get the same hash value for matching doubles?
public class Foo {
double[] values;
public Foo(double[] values) {
this.values = values;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
//TODO Arrays.hashCode will not work with the contract
result = prime * result + Arrays.hashCode(values);
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Foo other = (Foo) obj;
if (!doubleArrayEquals(values, other.values,1e-10))
return false;
return true;
}
private boolean doubleArrayEquals(double[] arr, double[] arr2, double epsilon) {
if (arr== arr2)
return true;
if (arr == null || arr2 == null)
return false;
int length = arr.length;
if (arr2.length != length)
return false;
for(int i = 0; i < length; i++) {
if(!isDoubleEquals(arr[i],arr2[i],epsilon)) {
return false;
}
}
return true;
}
private boolean isDoubleEquals(double needle, double target, double epsilon) {
return Math.abs(needle - target) <= epsilon;
}
}
You cannot properly write equals and hashCode methods which deal with approximate equality.
The contract of equals requires transitivity, and approximate equality is not transitive.
That's not to say that approximate equality is not a useful thing: it's just not what Java's equals (and hashCode) methods are for, and therefore you should define your own method - say, isApproximatelyEqualTo - to support it, without overloading the well-known Java methods.
I have a sorted map
Map<RangeValues,String> cIndexes = new TreeMap(new StartIndexComparator());
where key is an object of the class containing two integer values.
public class RangeValues {
private int startIndex;
private int endIndex;
}
So, when i am trying to access the value from that map, I am getting null as a value.
Set<RangeValues> keySet = cIndexes.keySet();
RangeValues[] keys = keySet.toArray(new RangeValues[keySet.size()]);
for(int index = 0; index < keys.length-1; index++)
{
**cIndexes.get(keys[index]) // this is giving null**
int nextIndex = keys[index+1].getStartIndex();
}
Implementation of comparator is
public class StartIndexComparator implements Comparator<RangeValues> {
#Override
public int compare(RangeValues r1, RangeValues r2) {
if(r1.getStartIndex() > r2.getStartIndex())
{
return 1;
}
else
{
return -1;
}
}
}
filling of map
RangeValues range = new RangeValues();
range.setStartIndex();
range.setEndIndex();
cIndexes.put(range,conjunctions.get(conjIndex));
How can I get the value from that map using object as key.
You need to override equals and hashCode methods in your object that is being used as a key
public class RangeValues {
private int startIndex;
private int endIndex;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + endIndex;
result = prime * result + startIndex;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RangeValues other = (RangeValues) obj;
if (endIndex != other.endIndex)
return false;
if (startIndex != other.startIndex)
return false;
return true;
}
}
The above equals and hashCode methods assume that both startIndex and endIndex will take part in deciding whether two objects of your class are equal.
You are probably not fulfilling the equals / hashCode contract in your RangeValues class.
Threfore, there is no guarantee that cIndexes.get(keys[index]) will not return null.
I'm about to create two methods for creating and changing customer profiles. Creating profile is no problem. Everything seems to go well there. But, when I shall then go in and change the profile, I get it not to work.
The indexOf() gives me -1, even though the value I search for available :S
Anyone have a good solution to this?
The problem is in the editProfile-method!
public class Profile{
String name;
long id;
int accNr = 1000;
double balance;
}
ArrayList<Profile> profileList = new ArrayList<Profile>();
public boolean newProfile(long id, String name, int amount){
Profile newProfile = new Profile();
Profile accNr = new Profile();
int ACC = accNr.accNr++;
newProfile.accNr = ACC;
newProfile.id = id;
newProfile.name = name;
newProfile.balance = amount;
profileList.add(newProfile);
return true;
}
public void editProfile(long id, String newName){
int ID = (int)id;
System.out.print(ID);
int index = profileList.indexOf(id);
System.out.print(index);
profileList.get(index);
}
The indexOf method will use the equals method to determine if your Profile exists in the list. You must override the equals method in Profile to return the proper result.
Second, it won't find your Profile, because you are passing a long to indexOf, and neither a long nor a Long will be found in the list. If you must retrieve the Profile by a long, then it makes more sense to have a Map<Long, Profile> instead of an ArrayList<Profile>. Then you can call get(id) to retrieve the Profile. Usually, you should override the hashCode method if you override equals, but because a Profile isn't being used as the key here, it's not necessary.
profileList contains Profile instances and you are trying to get the index of a long.
One solution would be overriding equals method in Profile class.
#Override
public boolean equals(Object obj) {
...
}
Another solution (not very recommended) would be looping over elements of profileList and manually checking for matches, like:
for (Profile element : profileList)
if (element.getID() == id)
...
Probably your Profileneeds to override equals and hashCode methods. Eclipse can generate then, Would be like taking your example:
public class Profile {
String name;
long id;
int accNr = 1000;
double balance;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + accNr;
long temp;
temp = Double.doubleToLongBits(balance);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + (int) (id ^ (id >>> 32));
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 (getClass() != obj.getClass())
return false;
Profile other = (Profile) obj;
if (accNr != other.accNr)
return false;
if (Double.doubleToLongBits(balance) != Double
.doubleToLongBits(other.balance))
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
I want to store a set of Edges:
class Edge {
int u;
int v;
char symbol;
}
The problem is that it's possible for two Edge objects to have the same u, v and symbol, but they can both be stored in a HashSet because they're not the same object even though I want them to be considered the same object. How can I store only one object that has a unique (u, v, symbol) in a Set?
You need to override the following two methods equals and hashcode.
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof Edge)) return false;
// return true if they are the same, otherwise false
}
public int hashCode() {
// return an int that represents similarity
// Example: name.hashCode(), if they are the same with the same name
}
Depends on what kind of set you want to use; The below applies for HashSet for instance, but not for any subclass of SortedSet
By overriding equals() and hashCode():
class Edge {
int u;
int v;
char symbol;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + symbol;
result = prime * result + u;
result = prime * result + v;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Edge other = (Edge) obj;
return symbol == other.symbol && u == other.u && v == other.v;
}
}
You have to override equals(). Like this:
public boolean equals(Object obj) {
//do the comparison here; remember to cast obj to Edge
}
I have defined a simple private class named SetOb which contains an int and a Set data structure. I have a HashMap in the 'main' method with SetOb as Key and Integer as value. Now as you can see in the main method, when I feed the HashMap with a SetOb instance and then look for an instance with exactly the same value, it returns 'null'. This has happened with me quite a few times before when I use my own defined data structures like SetOb as Key in HashMap. Can someone please point me what am I missing ?
Please note that in the constructor of SetOb class, I copy the Set passed as argument.
public class Solution {
public static Solution sample = new Solution();
private class SetOb {
public int last;
public Set<Integer> st;
public SetOb(int l , Set<Integer> si ){
last = l;
st = new HashSet<Integer>(si);
}
}
public static void main(String[] args) {
Map<SetOb, Integer> m = new HashMap< SetOb, Integer>();
Set<Integer> a = new HashSet<Integer>();
for(int i =0; i<10; i++){
a.add(i);
}
SetOb x = sample.new SetOb(100, a);
SetOb y = sample.new SetOb(100, a);
m.put(x,500);
Integer val = m.get(y);
if(val!= null) System.out.println("Success: " + val);
else System.out.println("Failure");
}
}
Your x and y are not the same object instances hence contains is not able to match y against x, which ends up not finding the matching key/value in the Map.
If you want the match to succeed, please implement(override) hasCode & equals method in SetOb which will compare the field values.
Sample methods(Eclipse generated) as below:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + last;
result = prime * result + ((st == null) ? 0 : st.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;
SetOb other = (SetOb) obj;
if (last != other.last)
return false;
if (st == null) {
if (other.st != null)
return false;
} else if (!st.equals(other.st))
return false;
return true;
}
The default implementation of hashCode uses object identity to determine the hash code. You will need to implement hashCode (and equals) in your private class if you want value identity. For instance:
private class SetOb {
public int last;
public Set<Integer> st;
public SetOb(int l , Set<Integer> si ){
last = l;
st = new HashSet<Integer>(si);
}
#Override
public boolean equals(Object other) {
if (other.class == SetOb.class) {
SetOb otherSetOb = (SetOb) other;
return otherSetOb.last == last && otherSetOb.st.equals(st);
}
return false;
}
#Override
public int hashCode() {
return 37 * last + st.hashCode();
}
}
SetOb needs to override the hashCode() and thus the equals() methods.
Hash-based collections use these methods to store (hashCode()) and retrieve (hashCode()) and equals()) your objects.