0

I'm trying to store a set of possible choices and eliminate duplicates so I'm storing the choices I've made in a HashSet. I have two pieces of data for each step and the combination of both must not be unique in order for it to be considered a duplicate (e.g. [2,0], [0,2], [2,2] would all be new steps but then going to [2,2] would be a duplicate).

I believe I need to override equals in order to properly determine if the step is already in the HashSet but I am not using a custom class, just an array of Integers, so most of what I've found isn't applicable (to my knowledge). This seems like it may be useful, suggesting the possibility of subclassing HashSet but I'd like to avoid that if possible. I was hoping the equals method I have commented out would work but it was never called. Do I need to override hashCode() as well? I know they go hand in hand. Do I just need to go write my own class or is there another method of doing what I want?

import java.util.HashSet;

public class EP2 {

 public static long count = 0;
 public static HashSet<Integer []> visits = new HashSet<Integer []>();

 //@Override
 //public boolean equals(Object j){     
 // return true;
 //}

 public static void main(String[] args) {

    int position = 0;
    int depth = 0;

    walk(position, depth);
    System.out.println(count);
 }

 public static void walk(int position, int depth){

    count++;

    Integer[] specs = new Integer[2];
    specs[0] = position;
    specs[1] = depth;
    visits.add(specs);


    Integer[] specL = new Integer[]{position - 1, depth+1};
    Integer[] specR = new Integer[]{position + 1, depth+1};


            //doesn't avoid [0,2] duplicates
    if(depth < 2){
        if(!visits.contains(specL)){
            walk(position - 1, depth+1); //walk left
        }
        if(!visits.contains(specR)){
            walk(position + 1, depth+1); //walk right
        }
    }

 }  

}
Community
  • 1
  • 1
Daniel
  • 2,435
  • 5
  • 26
  • 40
  • 1
    Sounds like just using [Point](http://docs.oracle.com/javase/7/docs/api/java/awt/Point.html) may suit your needs. – Bernhard Barker May 23 '13 at 13:33
  • I don't understand. Why can't you simply make a new class with a pair of `int`s? – Balázs Németh May 23 '13 at 13:33
  • I did not know that existed but in this case yes, i may go with that. thanks. However, I still would like the question answered – Daniel May 23 '13 at 13:34
  • @BalázsMáriaNémeth I could, but I'm curious about solving the problem in the current manner. I wouldn't learn as much going that way. – Daniel May 23 '13 at 13:35
  • But the question does not really make sense in this form for me. You cannot override the `equals` of Integer[] because that's not even a class :) – Balázs Németh May 23 '13 at 13:39
  • The `equals()` you commented out never gets called because it isn't related to the `Integer[]`, it's a method of your class `EP2`. The `Integer[]` class has its own `equals()` method. – Sotirios Delimanolis May 23 '13 at 13:39
  • And I would not go with the suggested Point class since the classes in java.awt are "the classes for creating user interfaces and for painting graphics and images". Unless of course it is what you intend to do :) – Balázs Németh May 23 '13 at 13:40
  • @BalázsMáriaNémeth if learning about this method means I've learned I'm doing it a stupid way, it still counts :) – Daniel May 23 '13 at 13:51
  • Of course. One more thing which is not related to the question. Instead of declaring a `HashSet`, declare a `Set` simply, so you don't make your code tight-coupled to the implementation of the `HashSet`. So `public static Set visits = ...` – Balázs Németh May 23 '13 at 13:54

3 Answers3

2

In Java, hashCode() and equals(Object) go together. If you override one, you should override the other. When Java looks up an object in a HashSet, it first computes the hashCode to determine which bucket the object might be found in. Then it uses equals(Object) to see whether the set has the object. Additionally, changing an object that's in a HashSet will lead to problems, as it might end up in the wrong bucket, and never be found again.

You may want to write your own immutable class, Position, that contains a constructor, a position and depth variables, getters, equals(Object), and hashCode(). The members of the Integer[] arrays have meaning, so you should probably state those explicitly.

Eric Jablow
  • 7,874
  • 2
  • 22
  • 29
1

The problem is that equals() for an Array checks if the arrays are the same instance. In your case, they are probably not. See a good question and answers here.

Your HashSet will call equals() for all elements in the set, hence it will return false unless all arrays are the same instance.

Changing the array to a List would probably work, since it checks that all containing elements are equal to one another. For Integers, this of course works.

I would however, implement my own class. If you don't have any such restrictions, you should do so.

Community
  • 1
  • 1
Magnilex
  • 11,584
  • 9
  • 62
  • 84
1

If you're simply trying to check for duplicates, write a custom method to check if a Set contains an int[].

 public static boolean contains(HashSet<Integer []> set, int[] step) {
     Iterator<Integer []> it = set.iterator();
     boolean flag = false;
     while(it.hasNext()) {
         Integer[] inner = it.next();
         if (step.length == inner.length) {
             for (int i = 0; i < inner.length; i++) {
                 if (inner[i].equals(step[i]))
                     flag = true;
                 else 
                     flag = false;
             }
             if (flag)
                 return true;
         }
     }
     return false;
 }

You should follow your rules. For example, if you know the size of the arrays is always going to be 2, then maybe you don't need to do the check and can quickly just check each value at the same index of each array.

You would call this method any time you wanted to add something to the Set.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724