In Java, a HashSet
implements the Set
interface. It is backed by a HashMap
and is primarily used to store unique
elements (no duplicates). Since it uses hashing for its internal implementation, operations like add, remove, and search
can be performed in constant time on average.
Advantages #
- Fast Performance: Most operations like adding, removing, and checking if an element exists have an average time
complexity of
O(1)
due to the underlying hash table. - No Duplicates: Ensures that all elements are unique, which is useful when you need to maintain a collection without duplicates.
- Allows Null:
HashSet
allows a singlenull
element. - Unordered: Elements are not stored in any particular order, which can be more efficient when order doesn’t matter.
- Good for large data sets: Efficient for handling large collections due to its fast average-time complexity for fundamental operations.
Disadvantages #
- No Ordering: Does not maintain the insertion order of elements. If you need order, you should
consider
LinkedHashSet
orTreeSet
. - Poor Performance for Large Load Factors: If the hash function is inefficient or the load factor (ratio of elements to table size) becomes too high, performance can degrade significantly.
- Requires Proper Hashing: Performance heavily depends on a good implementation of the
hashCode()
method for the elements. Poorly implementedhashCode()
can lead to excessive collisions and degrade performance toO(n)
for some operations. - Extra Memory: Requires additional space to store the hash table.
Time and Space Complexity #
Operation | Average Time Complexity | Worst-Case Time Complexity | Space Complexity | Description |
---|---|---|---|---|
Add | O(1) | O(n) | O(n) | O(1) on average; O(n) in the worst case when there are too many collisions. |
Remove | O(1) | O(n) | O(n) | O(1) on average; O(n) in the worst case when collisions occur. |
Contains | O(1) | O(n) | O(n) | O(1) on average; O(n) in the worst case when collisions occur. |
Size | O(1) | O(1) | O(n) | |
isEmpty | O(1) | O(1) | O(n) |
Examples #
Add (add): Adds an element to the set if it’s not already present.
- Time Complexity: O(1)
HashSet<String> hashSet = new HashSet<>(); // Adding elements hashSet.add("Apple"); // [ "Apple" ] hashSet.add("Banana"); // [ "Apple", "Banana" ] hashSet.add("Cherry"); // [ "Apple", "Banana", "Cherry" ] // Duplicates are not allowed hashSet.add("Apple"); // [ "Apple", "Banana", "Cherry" ]
Remove (remove): Removes a specified element from the set.
- Time Complexity: O(1)
hashSet.remove("Banana"); // [ "Apple", "Cherry" ]
Contains (contains): Checks if a particular element exists in the set.
- Time Complexity: O(1)
hashSet.contains("Apple"); // true
Size (size): Returns the number of elements in the set.
- Time Complexity: O(1)
hashSet.size(); // 2
isEmpty: Checks if the set is empty.
- Time Complexity: O(1)
hashSet.isEmpty(); // false
- Time Complexity: O(n)
for (String item : hashSet) { System.out.println(item); }
Conclusion #
- HashSet is highly efficient for basic operations like adding, removing, and searching for elements as long as you don’t need the order of elements.
- For applications that require ordering,
LinkedHashSet
orTreeSet
may be better alternatives, though with different time complexities. - When working with a
HashSet
, always ensure that the elements have properly implementedhashCode()
andequals()
methods to maintain good performance.