Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the time complexity performance of HashSet.contains() in Java?

I'm tempted to think that the HashSet.contains(Object) method performs in constant time. It simply gets the hash code of an object and then looks it up in a hash table.

First, could someone please confirm whether this is true?

Second, if it is true, is there any risk of collisions, where two objects might have the same hash code and thus the HashSet thinks it has both when it only has one?

like image 901
ktm5124 Avatar asked Aug 11 '14 16:08

ktm5124


People also ask

What is the time complexity of HashSet contains?

On average, the contains() of HashSet runs in O(1) time. Getting the object's bucket location is a constant time operation.

What is the time complexity of HashMap get () and put () method?

HashMap has complexity of O(1) for insertion and lookup. HashMap allows one null key and multiple null values.

What HashSet contains in Java?

HashSet. contains() method is used to check whether a specific element is present in the HashSet or not. So basically it is used to check if a Set contains any particular element. Syntax: Hash_Set.contains(Object element) Parameters: The parameter element is of the type of HashSet.

Why is a HashSet O 1?

Yes, accessing and finding an element in HashSet is O(1). HashSet stores elements using the hashing technique. Another important property of HashSet is that it contains only unique elements. For example you can check if the element already exists in HashSet.


2 Answers

It runs in O(1) expected time, as any hash table (assuming the hash function is decent). It is backed by a HashMap where the key is the Object.

Two objects might have the same hash code, but the HashSet wouldn't think they are identical, unless the equals method for these objects says they are the same (i.e. returns true).

The contains method calls (indirectly) getEntry of HashMap, where key is the Object for which you wish to know if it's in the HashSet.

As you can see below, two objects can be stored in the HashMap/HashSet even if their key is mapped to the same value by the hash function. The method iterates over all keys that have the same hash value, and performs equals on each one to find the matching key.

final Entry<K,V> getEntry(Object key) {          int hash = (key == null) ? 0 : hash(key.hashCode());          for (Entry<K,V> e = table[indexFor(hash, table.length)];               e != null;               e = e.next) {              Object k;              if (e.hash == hash &&                  ((k = e.key) == key || (key != null && key.equals(k))))                  return e;          }          return null;      } 
like image 87
Eran Avatar answered Oct 07 '22 20:10

Eran


The worst-case performance of contains will be O(log n) for Java 8, and O(n) for Java 7, but average case closer to O(1). This is because the hashset is backed by a hashmap, and thus has the same efficiency as hashmap lookup (ie, HashMap.get(...)). The actual mapping in a hashmap is constant time (O(1)), but the need to handle collisions brings the cost to log n. That is, multiple elements that hash to the same array index must be stored in a secondary data structure (aka bucket), and it is this bucket that determines the worst-case performance. In Java, hashmap collision-handling is implemented using a self-balanced tree.

Self-balanced trees guarantee O(log n) for all operations, hence, insertion and lookup in hashmap (and hashset) has a total cost of O(1) + O(log n) = O(log n). The use of a self-balanced tree for collision-handling was introduced in Java 8 as an improvement over chaining (used until java 7), which uses a linked-list, and has a worst case of O(n) for lookup and insertion (as it needs to traverse the list). Notice that chaining would have constant time for insertion (as opposed to lookup), since elements can be added to a linked list in O(1), but the set property (no duplicates) is imposed on the linked-list in the case of hashmap, and it thus need to traverse the linked-list also in the case of insertion to ensure that the element does not already exist in the list/bucket, and we end up with O(n) for both insertion and lookup.

References:

This class implements the Set interface, backed by a hash table (actually a HashMap instance). https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html

Buckets containing a large number of colliding keys will store their entries in a balanced tree instead of a linked list after certain threshold is reached. (https://www.nagarro.com/en/blog/post/24/performance-improvement-for-hashmap-in-java-8)

like image 23
Daniel Valland Avatar answered Oct 07 '22 22:10

Daniel Valland