How to get() specific element from HashSet? Kotlin

Viewed 2530

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?

2 Answers

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() }

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

Related