1

I am trying to perform bi directional A star search, where in I encountered this issue.

Suppose I have an object A in a hashset H. Suppose there is another object B, such that A.equals(B) is true (and their hash values are also the same), though A and B point to different objects. Now, when I check if object B is in hashset H, it returns true, as expected. However, suppose now, I want to access some attribute in object A based on this, I then need to access object A. Note that accessing the same attribute in B will not work since they are equal only under the equals method, but do not point to the same object. How can I achieve this?

One way is to use a Hashmap, such that the value type is the same as the key type, and every time I store some key in the hashmap, I store along with it the same object as its value. But this incurs extra memory overhead of storing the value, when what I really need is a copy of the key itself. Is there any other way to achieve this?

piedpiper
  • 1,222
  • 3
  • 14
  • 27
  • 1
    So if you can not store A and B in the same hashset, because of collision. How you plan to access them ? – Damian Leszczyński - Vash Mar 05 '14 at 13:43
  • @Vash: The point is to find the `A` value based on the `B` value. The `B` value doesn't need to be stored in the set... it's just being used as a lookup key. Unfortunately `Set` doesn't let you find any way of finding "the value which is stored in the set, which happens to be equal to the key value you've provided". – Jon Skeet Mar 05 '14 at 13:44
  • @JonSkeet, that is strong assumption. but in that case the answer will be that calling map.get(objectB) will return with instance of `objectA. And that is why Map interface do not use generic type in `get` and `contains` method. – Damian Leszczyński - Vash Mar 05 '14 at 13:48
  • @JonSkeet, and that what you have described in your answer. In meed time. Nice. – Damian Leszczyński - Vash Mar 05 '14 at 13:49
  • @Vash: I wouldn't say it's a "strong assumption" - it's an interpretation of the question, and I think it's a pretty reasonable one. *Everything* in the question agrees with it. – Jon Skeet Mar 05 '14 at 13:57

3 Answers3

4

I don't believe there's any way of doing this efficiently using HashSet<E>. (You could list all the keys and check them for equality, of course.)

However, although the HashMap approach would be irritating, it wouldn't actually take any more memory. HashSet is implemented using HashMap (at least in the stock JDK 7) so there's a full map entry for each set entry anyway... and no extra memory is taken to store the value, because they'd both just be references to the same object.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thats fine, but atleast a copy of the reference itself will also be stored, right? I know that is little memory, but why store that too? – piedpiper Mar 05 '14 at 13:55
  • 1
    @ashu: A `HashSet` is *already* storing a copy of a reference - it's just that it's a reference to `HashSet.PRESENT`. It's not like storing a non-null reference takes any more space than a null reference, either: as I say, it's using `HashMap` internally, so there's logically a value for every entry, and there'll be a field associated with that value, regardless of what the value actually is. So there is *no* more memory usage. – Jon Skeet Mar 05 '14 at 13:59
2

One way is to use a Hashmap, such that the value type is the same as the key type, and every time I store some key in the hashmap, I store along with it the same object as its value. But this incurs extra memory overhead of storing the value, when what I really need is a copy of the key itself.

In actual fact, the implementation of HashSet in (at least) the Oracle Java SE Library in Java 7 has a HashMap inside it. So your concern about the extra memory usage of HashMap is unwarranted.

Here is a link to the source code: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/util/HashSet.java/

Incidentally, the internal map is declared as HashMap<E, Object> rather than HashMap<E, E>. Thought exercise for the reader: why did they do that?


The only other reasonable alternative (using HashSet) would be to iterate over the set elements, testing each one to see if it is equal to the one you are looking for. That is obviously very (time) inefficient.

Community
  • 1
  • 1
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • I can't think of an answer to the thought experiment part. Can you please give some insight on that? – piedpiper Mar 05 '14 at 14:08
  • @ashu - It is subtle. When you use the value returned by a `get(...)` on a `HashMap`, there is an *implicit* typecast of the result to the `E` type. But in this case, the `HashSet` is only going to test if the value is `null` or not, or call `Object.equals` on it. Neither of these require an `E`. So by using `Object` instead, the implementation avoids an unnecessary typecast in certain `Set` operations. – Stephen C Mar 05 '14 at 15:04
  • But I do think that E is required for the equals method – piedpiper Mar 05 '14 at 16:45
  • @ashu - It isn't required at compile time to make the call. (It is required to execute the call, but that's handled at runtime by normal Java polymorphic method dispatching.) – Stephen C Mar 05 '14 at 22:45
  • Okay, I understand it now! – piedpiper Mar 06 '14 at 23:31
0

You could iterate over all values in the HashSet and check the equals method of B. If the iterator returns an object which is equal to B you have found A.

Steven Pessall
  • 983
  • 5
  • 15