6

Assume myHashSet = HashSet<SomeClass>

where SomeClass.hashcode() = someField.hashcode()

How can I return an element with the specified hashcode, i.e:

myHashSet.getElementWithHashCode((other as SomeClass).someField.hashcode())

other and the objects inside the HashSet are different objects with different property values except someField value. In other words, these two different type of objects have a common field that might have the same value.

It is weird that there is no such function in HashSet. No one needed that before? What is the quickest way around?

Xfce4
  • 557
  • 5
  • 28
  • You can search for elements in collections with functions like `find` or `first`. Pass it the criteria as a lambda: `mySet.find { it.hashcode() == other.hashcode() }`. – Malte Hartwig Mar 06 '20 at 01:03
  • @MalteHartwig Yeap. It worked. Actually this is what a `getItem()` function would do inside. So the performance won't be affected I suppose. If you write this as an answer I can accept it. – Xfce4 Mar 06 '20 at 01:23
  • 1
    No, performance will definitely be affected; e.g. if there is no element with the hashcode you want, it will iterate over the entire set. – Alexey Romanov Mar 06 '20 at 05:28
  • @AlexeyRomanov Right. It is not same with fetching the value directly from the memory with the key. So is there a way to use the hashcode to get the element from the key location? – Xfce4 Mar 06 '20 at 05:40
  • I gave the answer. – Alexey Romanov Mar 06 '20 at 05:43

2 Answers2

2

I don't know whether this could you in your case, it would depends on whether it used hashCode or equals internally. Some sources online that have a similar problem are looking for an equals-based solution.

Anyway, you can use built-in functions like find or first to implement it yourself:

fun <E> HashSet<E>.findByHashCode(other: E): E? = firstOrNull { it.hashCode() == other.hashCode() }
Malte Hartwig
  • 4,477
  • 2
  • 14
  • 30
1

There's no standard solution; but since HashMap.get(key) specifies that it compares parameter to stored keys (key.equals(k)) and not the other way around, you could achieve the desired result with this nasty hack (nasty because it breaks the equals contract):

class HasHash(private val hash: Int) {
    override fun hashCode() = hash
    override fun equals(other: Any?) = other != null && other.hashCode() == hash
}

But even then HashSet on JVM doesn't expose the details you need (like getElement in Kotlin/Native), so the only solution I can come up with is

val set: HashSet<T> = ...
val map: Map<Any, T> = set.associateBy { it }

fun findByHashCode(hash: Int): T? = map[HasHash(hash)]

which needs to iterate over the set to construct map, but only once, so it can still be useful if you need to find many elements in a single set.

In Kotlin/Native it's just

fun <T> HashSet<T>.findByHashCode(hash: Int): T? = getElement(HasHash(hash))

Also if there are multiple elements with the desired hashcode in the set you'll just get one of them.

So you are mapping each item with itself as key? Does it mean that associateby{} automatically takes the hashcode() of the element as key

No. The idea is:

  1. Say set contains "a" (hashcode 1; not really but assume so for this example), "b" (hashcode 1), and "c" (hashcode 2).

  2. Then map is "a": "a", "b": "b", "c": "c".

  3. Call findByHashCode(1) = map[HasHash(1)].

  4. Hashcode of HasHash(1) is 1, so it looks up the slot containing keys "a" and "b" (let's say in that order). HasHash(1).equals("a") returns true, so the value stored with key "a" is returned and that's "a".

or getElement() function automatically checks according to the hashcode of the input element?

What it says is

Return the element from the set equal to element, or null if no such element found.

so it should return "a" if it compares them in order HasHash(1).equals("a").

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Could not understand `(this as HashSet)[HasHash(hash)]` part. Not sure if I ever seen such a syntax before. – Xfce4 Mar 06 '20 at 06:02
  • Which part? `this` is how you refer to the receiver parameter in extension functions, `as` is a cast, `[...]` is syntax sugar for `get` method. – Alexey Romanov Mar 06 '20 at 06:29
  • So you used `get(HasHash(hash))`. But no `get()` method is in the suggestion list for HashSet objects in Android Studio. – Xfce4 Mar 06 '20 at 06:43
  • Oops, I was mixing it up with `Map` API. So fixed, but with an additional bad limitation :( – Alexey Romanov Mar 06 '20 at 09:48
  • So you are mapping each item with itself as key? Does it mean that `associateby{}` automatically takes the hashcode() of the element as key or `getElement()` function automatically checks according to the hashcode of the input element? Because the output should be not HasHash(hash) itself but its hashTwin. – Xfce4 Mar 06 '20 at 14:55
  • No and no. I explained in more detail. – Alexey Romanov Mar 06 '20 at 16:24
  • Thank you for details. So the functions do their job on equality and we define equality based on `hashcode()`. As you mentioned, turning into map is useful if multiple searches are to be done. In my case single search is enough. But anyway I consider turning the whole set into Map all over the project for its speed and flexibility over HashSet. – Xfce4 Mar 07 '20 at 02:09