What are the differences between HashSet and TreeSet?

\| Aspect \| HashSet \| TreeSet \|

\|-------------------------\|------------------------------------------------\|----------------------------------------------\|

\| Underlying Data Structure \| HashMap (internally stores elements using a hash table) \| Red-Black Tree (self-balancing binary search tree) \|

\| Ordering \| No ordering is guaranteed. The elements are stored in an unordered manner and their order may change over time. \| Sorted in natural order (ascending order by default) or by a custom comparator if provided. \|

\| Performance (Time Complexity) \| Faster for basic operations (like add, remove, contains) since operations are performed in constant time, O(1) on average. \| Slower for basic operations because operations are performed in O(log n) time due to tree traversal. \|

\| Duplicates \| Does not allow duplicates (same as TreeSet). \| Does not allow duplicates (same as HashSet). \|

\| Null Elements \| Allows one null element. \| Does not allow null elements (will throw NullPointerException). \|

\| Ordering Control \| No control over element order, as it's based on hash codes. \| Can control order via natural ordering (e.g., integers are ordered from smallest to largest) or by providing a custom comparator. \|

\| Use Case \| Use when you need a fast lookup without caring about the order of elements. \| Use when you need a sorted set and care about element order. \|

\| Iterating Elements \| Iteration order is unpredictable. \| Iteration order is sorted according to natural order or custom comparator. \|

\| Performance for Insertions/Deletions \| O(1) on average, but depends on the hash function. Worst case is O(n) if too many collisions occur. \| O(log n) for insertions and deletions due to the red-black tree balancing. \|

\| Interface Implemented \| Implements Set interface. \| Implements Set and NavigableSet interfaces, which provide methods for sorted sets like headSet(), tailSet(). \|

When to Use:

  • HashSet: Use when you need fast performance (O(1) average time complexity) for adding, removing, and checking for the existence of elements, and you don't care about the order of elements.
  • TreeSet: Use when you need the elements to be sorted in a specific order, or if you need additional operations like finding subsets (headSet(), tailSet()), but are okay with slightly slower performance (O(log n)).

Examples:

HashSet Example:

import java.util.HashSet;
public class HashSetExample {  

public static void main(String args) {

HashSet<Integer> hashSet = new HashSet<>();

hashSet.add(3);

hashSet.add(1);

hashSet.add(2);

System.out.println(hashSet);  // Output might be in any order, e.g., [1, 2, 3]

}


}  

TreeSet Example:

import java.util.TreeSet;
public class TreeSetExample {  

public static void main(String args) {

TreeSet<Integer> treeSet = new TreeSet<>();

treeSet.add(3);

treeSet.add(1);

treeSet.add(2);

System.out.println(treeSet);  // Output: [1, 2, 3] (sorted order)

}


}  

Summary:

  • HashSet is best when you prioritize performance over order and need constant time for basic operations.
  • TreeSet is best when you need elements to be sorted and don't mind paying a performance cost with logarithmic time for operations.