7

Hi I'm wondering if it is possible to access the contents of a HashSet directly if you have the Hashcode for the object you're looking for, sort of like using the HashCode as a key in a HashMap.

I imagine it might work something sort of like this:

MyObject object1 = new MyObject(1); 

Set<MyObject> MyHashSet = new HashSet<MyObject>();

MyHashSet.add(object1)

int hash = object1.getHashCode

MyObject object2 = MyHashSet[hash]???

Thanks!

edit: Thanks for the answers. Okay I understand that I might be pushing the contract of HashSet a bit, but for this particular project equality is solely determined by the hashcode and I know for sure that there will be only one object per hashcode/hashbucket. The reason I was pretty reluctant to use a HashMap is because I would need to convert the primitive ints I'm mapping with to Integer objects as a HashMap only takes in objects as keys, and I'm also worried that this might affect performance. Is there anything else I could do to implement something similar with?

Kode47
  • 127
  • 1
  • 6
  • No, it's not possible. Why would you want to do this? Seems like you want a map. – Sotirios Delimanolis Nov 18 '14 at 04:33
  • 2
    No such public API. And even if there was, it might return multiple objects as hash codes collide. – Thilo Nov 18 '14 at 04:34
  • It is not possible to get the object that way. It makes sense since two different objects can have the same hashcode but may not be equal. Hashset internally compares two objects with the equals method if their hashcode matches. – BatScream Nov 18 '14 at 04:34
  • Okay I understand that I might be pushing the contract of HashSet a bit, but for this particular project equality is solely determined by the hashcode and I know for sure that there will be only one object per hashcode/hashbucket. The reason I was pretty reluctant to use a HashMap is because I would need to convert the primitive ints I'm mapping with to Integer objects as a HashMap only takes in objects as keys, and I'm also worried that might affect performance. Is there anything else I could do to implement similar code instead? – Kode47 Nov 18 '14 at 04:39
  • Here is a thread with a few libraries that provide Maps keyed by primitive int: http://stackoverflow.com/questions/16148575/hashmap-and-int-as-key – Thilo Nov 18 '14 at 04:44
  • Ya that's where I got the idea to use the integer object from, I just thought that there might be a simpler way. – Kode47 Nov 18 '14 at 04:58

4 Answers4

2

HashSet is internally backed by a HashMap, which is unavailable through the public API unfortunately for this question. However, we can use reflection to gain access to the internal map and then find a key with an identical hashCode:

private static <E> E getFromHashCode(final int hashcode, HashSet<E> set) throws Exception {
    // reflection stuff
    Field field = set.getClass().getDeclaredField("map");
    field.setAccessible(true);

    // get the internal map
    @SuppressWarnings("unchecked")
    Map<E, Object> interalMap = (Map<E, Object>) (field.get(set));

    // attempt to find a key with an identical hashcode
    for (E elem : interalMap.keySet()) {
        if (elem.hashCode() == hashcode) return elem;
    }
    return null;
}

Used in an example:

HashSet<String> set = new HashSet<>();
set.add("foo"); set.add("bar"); set.add("qux");

int hashcode = "qux".hashCode();

System.out.println(getFromHashCode(hashcode, set));

Output:

qux
August
  • 12,410
  • 3
  • 35
  • 51
  • Thanks! That's a brilliant way of doing it, however it requires searching through the HashSet with a for loop, I know what I'm asking for is impossible now but I just thought there might be a way to get directly to the data at that hashcode, like it's a list or something. – Kode47 Nov 18 '14 at 05:02
  • I suspect it's possible to do this in constant time with even more reflection hackery, as `HashMap` does not let you get the bucket at a specific index in the table through the public API either. – August Nov 18 '14 at 05:26
2

The common implementation of HashSet is backed (rather lazily) by a HashMap so your effort to avoid HashMap is probably defeated.

On the basis that premature optimization is the root of all evil, I suggest you use a HashMap initially and if the boxing/unboxing overhead of int to and from Integer really is a problem you'll have to implement (or find) a handcrafted HashSet using primitive ints for comparison. The standard Java library really doesn't want to concern itself with boxing/unboxing costs. The whole language sold that performance issue for a considerable gain in simplicity long ago. Notice that these days (since 2004!) the language automatically boxes and unboxes which reveals a "you don't need to be worrying about this" policy. In most cases it's right.

I don't know how 'richly' featured your HashKeyedSet needs to be but a basic hash-table is really not too hard.

Persixty
  • 8,165
  • 2
  • 13
  • 35
0

This is not possible as HashSet is an object and there is no public API as such. Also multiple objects can have the same hashcode but the objects can be different.

Finally only arrays can be accessed using myArray[<index>] syntax.

Parth Satra
  • 513
  • 2
  • 16
0

You can easily write code that will directly access the internal data structures of the HashSet implementation using reflection. Of course, your code will depend on the implementation details of the particular JVM you are coding to. You also will be subject to the constraints of the SecurityManager (if any).

A typical implementation of HashSet uses a HashMap as its internal data structure. The HashMap has an array, which is indexed by the key's hashcode mapped to an index in the array. The hashcode mapping function is available by calling non-public methods in the implementation - you will have to read the source code and figure it out. Once you get to the right bucket, you will just need to find (using equals) the right entry in the bucket.

Rob
  • 6,247
  • 2
  • 25
  • 33