I am trying to create a search algorithm that stores coordinate pairs in a wrapper class called HashSquareSpec. In order to avoid duplicates, and maintain insertion order, I am inserting each HashSquareSpec into a LinkedHashSet. Even though I have overridden the equals() method and hashCode() methods, the LinkedHashSet still accepts two HashSquareSpec objects with
the same coordinate pairs.
public static void main(String [] args)
{
LinkedHashSet<HashSquareSpec> firedShots = new HashLinkedSet<HashSquareSpec>();
HashSquareSpec a = new HashSquareSpec(1,2);
HashSquareSpec b = new HashSquareSpec(2,2);
HashSquareSpec c = new HashSquareSpec(1,2);
HashSquareSpec d = new HashSquareSpec(3,2);
firedShots.add(a);
firedShots.add(b);
firedShots.add(c);
firedShots.add(d);
System.out.println(a.equals((SquareSpec)c));
Iterator l = firedShots.iterator();
while(l.hasNext())
{
System.out.println(l.next().hashCode());
}
}
Output:
true
38444474
38474265
38444474
38504056
HashSquare class
public class HashSquareSpec extends SquareSpec
{
public HashSquareSpec(int sx, int sy)
{
super(sx,sy);
}
public HashSquareSpec(String codeString)
{
super(codeString);
}
#Override
public int hashCode()
{
return this.toString().hashCode();
}
public boolean equals(HashSquareSpec other)
{
if(this.toString().equals(other.toString()))
return true;
else
return false;
}
}
and the super class of HashSquareSpec
public class SquareSpec {
public int x;
public int y;
public SquareSpec(int sx, int sy) {
this.x = sx;
this.y = sy;
}
public SquareSpec(String codeString) {
this.x = Integer.parseInt(codeString.substring(1,2));
this.y = Integer.parseInt(codeString.substring(3,4));
}
public String toString() {
return("(" + x + "," + y + ")");
}
public boolean equals(SquareSpec other) {
return (other.x == this.x &&
other.y == this.y );
}
}
Despite many different hashCode variations and Eclipse equals and hashCode generation, the
firedShots data structure keeps accepting duplicates. What is wrong with my code?
You are on the right track, overriding hashcode and equals, except you are incorrectly overriding the equals method from Object in HashSquareSpec (and SquareSpec). The parameter must be an Object. Because it's not overridden, equals from Object is called, which compares object references to see if they're the same object. They aren't, so the "duplicate" is allowed.
Try:
#Override
public boolean equals(Object other)
{
if(this.toString().equals(other.toString()))
return true;
else
return false;
}
You should also test if other is null and then ensure that other is the same type.
Include the #Override annotation so that the compiler will complain if the method doesn't actually override anything.
It's still accepting because you are not overriding the equals method. You need to override boolean equals(Object). The problem is that you're defining a new method like boolean equals(SquareSpec).
Here is the method that LinkedHashSet#add(T) eventually invokes:
HashMap#put(K, V):
#Override public V put(K key, V value) {
if (key == null) {
return putValueForNullKey(value);
}
int hash = secondaryHash(key.hashCode());
HashMapEntry<K, V>[] tab = table;
int index = hash & (tab.length - 1);
for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
if (e.hash == hash && key.equals(e.key)) {
preModify(e);
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
As you can see, it compares using hashCode and equals(Object).
Related
As the title says i want to create a Hash table that takes tuples of integers as keys.What i have don so far:
class Tuple {
public Tuple (int x, int y) {
this.x = x;
this.y = y;
}
public int k;
#Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + this.x;
hash = 31 * hash + this.y;
return hash;
}
private int x;
private int y;
}
public class Class1{
public static void main(String[] args){
Tuple t= new Tuple (1,1);
HashMap<Tuple, Integer> seen = new HashMap<Tuple, Integer>();
seen.put(t,33);
System.out.println(seen.get(t));
}
}
My problem is this: I would like to acquire the value 33 only by knowing the tuple (1,1) and not actually knowing the object t.
Something like seen.get((1,1)) and have the value 33.
How could this be achieved?
You would do
System.out.println(seen.get(new Tuple(1, 1));
The key concept is that since you override equals and hashCode any Tuple object that satisfies equality and hashCode equality will work as a key, and not just the t object.
Edit:
Note that your Tuple class also must override the public boolean equals(Object o) method in a correct fashion for any of this to work correctly:
public class Tuple {
private int x;
private int y;
public Tuple(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + this.x;
hash = 31 * hash + this.y;
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tuple other = (Tuple) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
Then test with:
public static void main(String[] args) {
Tuple t = new Tuple(1, 1);
Map<Tuple, Integer> seen = new HashMap<Tuple, Integer>();
seen.put(t, 33);
System.out.println(seen.get(t));
System.out.println(seen.get(new Tuple(1, 1)));
}
Actually, you can use any key for the map if:
key is immutable;
key implements equals() and hashCode().
In your example, you have to use Tuple instance. You already could know it or create the new one with known x and y (and this new instance will be absolutely the same key because all values x and y are used in equals() and hashCode() are also the same).
// It's better to make class immutable
public final class Tuple {
private final int x;
private final int y;
public static Tuple of(int x, int y) {
// you can add internal cashe here
return new Tuple(x, y);
}
private Tuple(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Tuple))
return false;
return x == ((Tuple)obj).x && y == ((Tuple)obj).y;
}
#Override
public int hashCode() {
return Objects.hash(x, y);
}
}
Demo
Map<Tuple, Integer> map = new HashMap<>();
map.put(Tuple.of(1, 1), 33);
System.out.println(map.get(Tuple.of(1, 1))); // 33
I have created a pair class and list of objects of this class. how can i check whether the list contains a object or not
i have tried list.contains method
//the pair class
public static class pair
{
int x,y;
public pair(int x,int y)
{
this.x=x;
this.y=y;
}
public int getY() {
return y;
}
public int getX() {
return x;
}
}
//this line is always giving false as output
if (!l.contains(new pair(x-1,y)))
count_1++;
By default, objects in Java are only equal if the instances are the same (when you say new Pair() you're making a new instance!).
We can override this behavior by overriding the equals() method of your Point class. Like so:
public class Point {
private final x;
private final y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
private static boolean isPoint(Object o) {
return o != null && o instanceof Point;
}
private boolean coordsEqual(Point p) {
return getX() == p.getX() && getY() == p.getY();
}
public boolean equals(Object o) {
return isPoint(o) && coordsEqual((Point) o); //is o is a point? Does it have equal coords?
}
Now Point.equals() will return true if our coordinates match instead of the default object equals behavior. Since Collection.contains() uses the equals() method to check for equality, your code will behave as expected.
You'll need to write an equals method in your Pair class, so that the List can work out whether it contains the Pair that you're looking for. The List will use your equals method when looking for matches.
The simplest way to write your equals method is to let your IDE generate it for you.
I'm new to Java. I'm writing a 2d game and I've decided to use a HashMap to store my map data because I need to support negative indices for my map coordinates. This is because the size of the map can grow during the game.
I have written a custom Pair class that stores final x and y values. I am using this Pair object as my key for my HashMap. My values are instances of a custom Cell class.
I have declared my HashMap as follows:
HashMap<Pair, Cell> tiles = new HashMap<Pair, Cell>();
Next, I add entries to my map using:
tiles.put(new Pair(0,0), new Cell());
0,0 is obviously my unique x,y coordinate for this cell.
How do I access the fields and methods of Cell using the .get() method of HashMap, specific to an individual Pair? Such as Pair(0,0) or Pair(0,1). If the key was simply a string or an int, I would have no problem. I just can't figure out how to format my key for an object with a specific coordinate.
You need to override the equals and hashCode method of the Pair class. Right now, if you have two instances of Pair as such:
Pair p1 = new Pair(0,0);
Pair p2 = new Pair(0,0);
These two instances in your program would not be seen as equal, and hence if you said:
tiles.put(p1, XXX);
tiles.put(p2, YYY);
the behavior would be such that your map would have two distinct keys, with two distinct values - where I believe what you would want is one key, with the last value YYY after these statements execute.
After implementing hashCode and equals, you could write a static helper method that instantiates a new Pair with some given coordinates, and performs a map lookup:
static Cell lookup(int x, int y) {
return tiles.get(new Pair(x, y));
}
Here's a basic Pair class to help you get started:
public class Pair {
private final int x;
private final int y;
public Pair(final int x, final int y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Pair)) {
return false;
}
final Pair pair = (Pair) o;
if (x != pair.x) {
return false;
}
if (y != pair.y) {
return false;
}
return true;
}
#Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
}
You just write tiles.get(new Pair(0, 0)), just like you did for put.
You will need to override the methods .equals() and .hashCode() for your Pair() type. They are needed in order to use a type in a HashMap. Equals can just check that the 2 values are equal:
#Override public boolean equals(Object o) {
if(this == o) return true;
if(!(o instanceof Pair)) return false;
Pair p = (Pair)o;
return p.x == x && p.y == y;
}
For hashCode, you must generate a value that is unique:
private volatile int hashCode; // define this as a member of the class
#Override public int hashCode() {
int result = hashCode;
if(result == 0) {
result = 17 * x + 31 * y;
hashCode = result;
}
return result;
}
Then you can access the cell at (0, 0) by simply calling
tiles.get(new Pair(0, 0));
You need to override hashCode(). Something like:
public int hashCode() {
return 17 * this.key + 31 * this.value;
}
I am writing a class called Coord. I have created a constructor:
public final int r,c;
public Coord (int r, int c){
this.r = r;
this.c = c;
}
I also did another two methods
//Creates and returns a new Coord value with the same row/column
public Coord copy(){
Coord copy = new Coord (r,c);
return copy;
}
//Given another object, is it also a Coord with the same row and column values?
public boolean equals(Object o){
return this==o; //this may be incorrect.
}
Now I can not pass some test cases as following:
Coord c = new Coord (5,10);
#Test (timeout=2000) public void coord() {
assertEquals(c, c.copy());
assertEquals(c, c);
assertFalse(c.equals(new Coord (2,3))); // #(5,10) != #(2,3).
assertFalse(c.equals("hello")); // must work for non-Coords.
}
I think the problem may arise from my boolean equals method, but I have tried a lot I still cannot pass the test. Is there a deep equal issue here? Can someone help me?
Is there a deep equal issue here?
Well yes, your equals method just checks whether the value passed to it is the same reference. Your comment says what you want to do:
//Given another object, is it also a Coord with the same row and column values?
So that's what you need to implement:
#Override public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o.getClass() != getClass()) {
return false;
}
Coord other = (Coord) o;
return other.r == r && other.c == c;
}
I'd also encourage you to make the class final (in which case you can use instanceof instead of calling getClass()) and you need to implement hashCode() to be consistent with equals too. For example:
#Override public int hashCode() {
int hash = 23;
hash = hash * 31 + r;
hash = hash * 31 + c;
return hash;
}
According to what I have read,
to use an object as the key to a hashMap, it has to provide a correct
override and implementation of the equals and hashCode
method. HashMap get(Key k) method calls hashCode method on the key
object and applies returned hashValue to its own static hash
function to find a bucket location(backing array) where keys and
values are stored in form of a nested class called Entry (Map.Entry).
HashMap's internal hash Method defends against poor quality hash
functions.
To test these contracts, I have written a bean class with incorrect but legal implementations of the equals and hashCode method.
The class:
public class HashVO {
private String studentName;
private int age;
private boolean isAdult;
public HashVO(String studentName, int age, boolean isAdult) {
super();
this.studentName = studentName;
this.age = age;
this.isAdult = isAdult;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isAdult() {
return isAdult;
}
public void setAdult(boolean isAdult) {
this.isAdult = isAdult;
}
#Override
public String toString() {
return studentName + " : " + age + " : " + isAdult;
}
#Override
public boolean equals(Object obj) {
return false;
}
#Override
public int hashCode() {
return 31;
}
}
In this case, the hash method of the HashMap,
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
should also return same value everytime because the hashcode always returns 31. So if objects of class HashVO are used as key of a hashMap, the get method should not work, as it should go to the same bucket to retrieve the objects and the equals method always returns false so it will not be able to able to find a match for the key object.
But when I am using this method,
public static void main(String[] args) {
HashMap<HashVO, String> voMap = new HashMap<HashVO, String>();
HashVO vo = new HashVO("Item1", 25, true);
HashVO vo1 = new HashVO("Item2", 12, false);
HashVO vo2 = new HashVO("Item3", 1, false);
voMap.put(vo, "Item");
voMap.put(vo1, "Item1");
voMap.put(vo2, "Item2");
System.out.println(voMap.get(vo));
System.out.println(voMap.get(vo1));
System.out.println(voMap.get(vo2));
}
the output is correct, and showing
Item
Item1
Item2
I want to understand why this correct output is coming even as the Equals and HashCode method implementation is incorrect.
HashMap has a little trick where it compares object references before using equals. Since you're using the same object references for adding the elements and for retrieving them, HashMap will return them correctly.
See Java 7 source here (Java 8 did a pretty big revamp of HashMap but it does something similar)
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
// HERE. Uses == with the key
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
Note that this isn't part of the docs, so don't depend on it.
The HashMap works like this:
1) Index of table cell in which (Key,Value) will save calculates as key.hashCode();
2) Keys in HashMap compare by equals() or by reference comparing.
So, in your situation all pairs of (K,V) will store in one cell of HashMap table as LinkedList.
And you can get them from Map because references for keys will equals