Accessing a HashMap value with a custom Pair Object as a key - java

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;
}

Related

Creating a Hash table with a tuple as a key

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 got a list of pairs how can i check whether the list contains a specific element or not.The list.contains function is always giving false

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.

Deep equal issue about boolean method with a Object argument

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;
}

indexOf() will not find a custom object type

The following code does not give me right answer.
class Point {
int x; int y;
public Point(int a,int b){
this.x=a;this.y=b;
}
}
class A{
public static void main(String[] args){
ArrayList<Point> p=new ArrayList<Point>();
p.add(new Point(3,4));
p.add(new Point(1,2));
System.out.println(p.indexOf(1,2));
}
}
This gives -1;
In general if arraylist of point is given, how can we find index of a particular point in in array ?
indexOf requires the object as input. If it does not find the object you are passing in, it will return -1. You need to pass the object whose location in the arraylist you are looking for as the input into the indexOf function. You should also override hashcode and equals for your class in this case.
Override hashcode and equals in your class Point. Then once you create instances of this class Point (using the new keyword) and add them to the arrayList, you can use the indexOf call on the arrayList using any of the Point objects as a parameter to the indexOf call.
Class Point
public class Point {
int x;
int y;
public Point(int a, int b) {
this.x=a;this.y=b;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Point other = (Point) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
Class Test (you called it "a"):
import java.util.ArrayList;
public class Test {
public static void main(String[] args){
ArrayList<Point> p=new ArrayList<Point>();
Point p1 = new Point(3,4);
Point p2 = new Point(1,2);
p.add(new Point(3,4));
p.add(new Point(1,2));
System.out.println(p.indexOf(p1));
}
}
You need to create a point to pass into the indexOf method.
p.indexOf(new Point(1,2));
But that change by itself will still return -1. See the api doc for indexOf:
public int indexOf(Object o)
Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element. More formally, returns the lowest index i such that (o==null ? get(i)==null : o.equals(get(i))), or -1 if there is no such index.
It's using equals to decide whether it's found a match. You haven't overridden the equals method on your point class, so it's using the default implementation in java.lang.Object, which compares the references, and only returns true if the two references point to the same object.
Override equals and hashcode on your point class, like:
#Override public boolean equals(Object other) {
if (!(other instanceof point)) {
return false;
}
point otherPoint = (point)other;
return otherPoint.x == this.x && otherPoint.y == this.y;
}
#Override public int hashCode() {
return x + y; // same values should hash to the same number
}
and that way two different instances of the class can be compared by value.
ArrayList.indexOf() doesn't accept two integers as argument. You must input one object, which is supposed to be a Point object.
If you still want to call ArrayList.indexOf(int, int), then you must create a subclass of ArrayList, implementing indexOf(int,int).
The following code should find the wanted object for you. First, you'll need to override the equals method from the Object class in the Point class, in order to compare two points.
public class Point {
private int x;
private int y;
#Override
public boolean equals(Object anotherObject) {
if (!(anotherObject instanceof Point)) {
return false;
}
Point p = (Point) anotherObject;
return (this.x == p.x && this.y == p.y);
}
}
Second, you can call indexOf(Object):
ArrayList<Point> p = new ArrayList<Point>();
// Create the point to find in the list.
Point findMe = new Point(1,2);
// Search the array and save the found index.
int index = p.indexOf(findMe);
PS: You should follow the Java naming conventions; classes must start with an uppercase letter.
how can we find index of a particular point in in array ?
ArrayList<point> p=new ArrayList<point>();
point p1 = new point(3,4));
point p2 = new point(1,2));
p.add(p1);
p.add(p2);
System.out.println(p.indexOf(p1));
The argument to indexOf() is an Object. Pass it one of your point objects.

LinkedHashSet Not Removing Duplicates

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).

Categories