一句话说透Java里面的HashSet和TreeSet的区别

233 阅读2分钟

一句话总结

  • HashSet 像 乱序的储物柜,存取快但不关心顺序。
  • TreeSet 像 自动整理的书架,元素按顺序排列但操作稍慢。

一、核心区别对比

对比项HashSetTreeSet
底层实现基于哈希表(HashMap)基于红黑树
元素顺序无序自然排序或自定义排序(通过比较器)
查询速度O(1)(平均)O(log n)
插入速度O(1)(平均)O(log n)
内存占用更低(仅存元素和哈希值)更高(每个节点存左右子节点指针)
额外功能支持范围查询(如 subSet()tailSet()

二、使用场景

1. 用 HashSet 当

  • 需求:快速存取,不关心顺序

    • 示例场景:

      • 存储唯一用户名,快速判断用户是否存在。
      • 缓存临时数据,无需排序。
    Set<String> usernames = new HashSet<>();  
    usernames.add("张三");  
    if (usernames.contains("李四")) { /* 检查是否存在 */ }  
    

2. 用 TreeSet 当

  • 需求:元素自动排序,或需要范围查询

    • 示例场景:

      • 存储成绩单,按分数从高到低排序。

      • 查找某个范围内的数据(如年龄在20-30岁的用户)。

    Set<Integer> scores = new TreeSet<>((a, b) -> b - a); // 降序排序  
    scores.add(90);  
    scores.add(85);  
    // 输出:[90, 85]  
    
    // 范围查询:获取 >=80 且 <90 的分数  
    SortedSet<Integer> subScores = ((TreeSet<Integer>)scores).subSet(80, 90);  
    

三、性能取舍

  • HashSet 更快,但无序

    • 适合频繁插入、删除、判断是否存在。
    • 不关心顺序时优先选它。
  • TreeSet 有序,但稍慢

    • 适合需要排序或范围操作的场景。
    • 用自定义对象时,对象必须实现 Comparable 或传入 Comparator

四、经典例子

1. 存储不重复的随机数

  • 用 HashSet

    Set<Integer> randomNumbers = new HashSet<>();  
    while (randomNumbers.size() < 10) {  
        randomNumbers.add((int)(Math.random() * 100));  
    }  
    // 结果:10个不重复的随机数(无序)  
    

2. 维护排行榜

  • 用 TreeSet

    Set<Player> leaderboard = new TreeSet<>((p1, p2) -> p2.score - p1.score);  
    leaderboard.add(new Player("Alice", 100));  
    leaderboard.add(new Player("Bob", 95));  
    // 结果:按分数降序排列  
    

五、总结口诀

「HashSet 快如风,存取无序效率高
TreeSet 自动排,范围查询是绝招
要快还是要顺序,根据场景选对宝!」