恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Java Set集合深度解析:HashSet、TreeSet与LinkedHashSet的实现原理与选型指南

  • 首页
  • 资讯中心
  • /
  • Java Set集合深度解析:HashSet、TreeSet与LinkedHashSet的实现原理与选型指南

相关资讯

Seata分布式事务框架:核心原理、四大模式与Spring Cloud整合实战 2026/8/26 9:16:38
老演唱会数字化修复实战:FFmpeg音频修复与HLS点播链路 2026/8/26 9:16:38
OpenClaw工具调用原理与实战:从架构设计到性能优化全解析 2026/8/26 9:16:38

最新资讯

基于RT-Thread AT组件实现STM32F407与AIR724UG Cat.1模块的稳定断电自恢复联网方案
C语言编程等级考试真题深度解析:指针、结构体与算法核心考点精讲
K3和GLM5.2抢不到Plan?API Key接入与多模型切换指南
高加速产品测试(HALT)实战指南:从方案设计到失效整改
AI绘画实战:Skill技能包如何让东方仙侠壁纸从平庸到惊艳
从3D CNN到Web应用:阿尔兹海默MRI诊断系统完整落地实践

今日推荐

Python random 模块常用函数详解:从入门到实战
Hermes接入团队协作后,我推翻了三个效率假设
免费AI大模型调教指南:打造专属网文写作助手

本周热门

Nextcloud 桌面客户端:把同步交给它,你只管改文件
如何将 HTML 转成 Word 文档且格式不丢失?html-to-docx 使用教程
Anki 批量操作卡片完整指南:一次搞定上千张,不再逐张修改

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

Java Set集合深度解析:HashSet、TreeSet与LinkedHashSet的实现原理与选型指南

发布时间:2026/8/26 9:21:38
Java Set集合深度解析:HashSet、TreeSet与LinkedHashSet的实现原理与选型指南 1. 从“集合”到“Set”为什么Java需要它如果你刚开始学Java或者已经写了一些代码大概率已经用过ArrayList或者HashMap了。它们一个管有序列表一个管键值对用起来挺顺手。但不知道你有没有遇到过这样的场景你需要管理一堆数据但要求这些数据绝对不能重复。比如你要统计一篇文章里出现了哪些不同的单词或者管理一个用户系统中所有已注册的邮箱地址。这时候如果你用ArrayList每次添加新元素前都得手动写个循环去检查是否已经存在代码又啰嗦效率又低。HashMap的键虽然不重复但它是键值对结构用来存单一元素有点“杀鸡用牛刀”而且语义上也不够清晰。Set接口就是为了解决“唯一性集合”这个核心需求而生的。它的名字就很直白——数学里的“集合”核心特性就是元素唯一。在Java的集合框架里Set是Collection接口的一个子接口它继承了Collection的所有方法但增加了一个最重要的契约不包含重复元素。更具体地说对于Set中的任意两个元素e1和e2如果e1.equals(e2)返回true那么Set中最多只能包含其中一个。这听起来简单但背后的实现却各有千秋直接影响了我们该在什么场景下选择哪个Set。今天我们不只讲Set怎么用更要拆开来看HashSet、TreeSet、LinkedHashSet这几个常用实现类的“内脏”看看它们是怎么保证元素唯一的各自的性能特点是什么以及那些面试官最爱问的源码级问题到底是怎么回事。理解了这些你才能写出既正确又高效的代码而不是仅仅停留在API调用的层面。2. HashSet速度之王与哈希的魔法当你需要一个不重复的集合并且对元素的顺序没有任何要求时HashSet几乎总是默认的首选。它的名字就揭示了其核心哈希表Hash Table。2.1 底层结构HashMap的“马甲”这是理解HashSet性能的关键。翻开HashSet的源码你会发现一个“惊人”的事实它内部维护了一个HashMap实例。// HashSet 源码节选 public class HashSetE extends AbstractSetE implements SetE, Cloneable, java.io.Serializable { private transient HashMapE,Object map; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT new Object(); public HashSet() { map new HashMap(); } public boolean add(E e) { return map.put(e, PRESENT)null; } public boolean contains(Object o) { return map.containsKey(o); } // ... 其他方法大多委托给内部的map }看到了吗HashSet的每个元素实际上都是内部HashMap的一个键Key。而那个PRESENT只是一个固定的、毫无意义的对象值用来充当HashMap中所有键对应的值Value。所以HashSet的“不重复”特性完全是由HashMap的键不重复特性来保证的。add方法成功返回true的条件就是map.put(e, PRESENT)返回了null这意味着之前这个键不存在。提示正因为这个设计HashSet的所有性能特征添加、删除、查找的时间复杂度都和HashMap的键操作一致。在理想情况下哈希冲突少这些操作的时间复杂度都是O(1)即常数时间。这是它被称为“速度之王”的原因。2.2 哈希、桶与冲突解决既然底层是HashMap那我们就必须理解哈希表的工作原理。当你调用set.add(“apple”)时计算哈希码首先调用”apple”.hashCode()方法得到一个int类型的哈希值。定位桶Bucket这个哈希值会经过一个扰动函数为了减少碰撞和取模运算最终确定这个元素应该放在内部数组称为哈希表的哪个索引位置这个位置就是一个“桶”。处理冲突如果计算出的桶位置已经有一个元素了哈希冲突那么HashMap也就是HashSet会在这个桶上形成一个链表Java 8之后当链表长度超过一定阈值会转换为红黑树。然后它会遍历这个链表/树对于每个节点用equals()方法比较新元素和已有元素是否“相等”。如果找到相等的则添加失败对于HashSet就是add返回false如果没找到则将新元素挂在链表/树的末尾。这个过程解释了HashSet两个最重要的行为判断重复的标准先看hashCode()如果hashCode不同直接认为是不同对象放入不同桶。如果hashCode相同再用equals()方法进行精确比较。因此要正确地将自定义对象存入HashSet必须同时重写hashCode()和equals()方法并且要保证逻辑一致equals为true的两个对象其hashCode必须相同。无序性元素的存储顺序由哈希值决定与添加顺序无关。遍历HashSet时你看到的顺序是不可预测的并且可能随着扩容rehash而改变。2.3 实战心得与避坑指南1. 初始化容量与负载因子HashSet有两个影响性能的关键参数初始容量initialCapacity和负载因子loadFactor。初始容量哈希表数组创建时的大小。默认是16。负载因子一个0到1之间的浮点数默认0.75。它决定了哈希表在多少满的时候进行扩容。当元素数量 容量 * 负载因子时哈希表会扩容通常是翻倍并重新计算所有元素的位置rehash。// 如果你预先知道大概要存1000个不重复元素可以这样初始化 SetString largeSet new HashSet(1024); // 初始容量设为2的幂次1024略大于1000 // 或者同时指定负载因子如果你对内存敏感可以调高负载因子如0.8减少扩容次数但会增加哈希冲突概率。 SetString memorySensitiveSet new HashSet(1024, 0.8f);注意rehash是一个相对耗时的操作。如果你能预估元素的大致数量在构造时指定一个合适的初始容量可以避免或减少扩容提升性能。但也不宜设置得过大以免浪费内存。2. 自定义对象作为元素这是新手最容易踩的坑。假设你有一个Person类只重写了equals方法没重写hashCode。class Person { String id; String name; // 构造器、getter/setter省略 Override public boolean equals(Object o) { if (this o) return true; if (o null || getClass() ! o.getClass()) return false; Person person (Person) o; return Objects.equals(id, person.id); // 根据id判断是否同一人 } // 忘记重写 hashCode }SetPerson set new HashSet(); Person p1 new Person(001, 张三); Person p2 new Person(001, 张三); // id相同应该是同一个人 set.add(p1); set.add(p2); // 这行会成功因为p1和p2的hashCode()不同默认是对象地址它们被放到了不同的桶equals()根本没被调用。 System.out.println(set.size()); // 输出 2违背了Set的唯一性修复方法必须同时重写hashCode()确保相同id的对象返回相同的哈希值。Override public int hashCode() { return Objects.hash(id); // 使用id字段计算哈希码 }3. 并发修改异常HashSet不是线程安全的。如果在多线程环境下一个线程在遍历HashSet例如用Iterator另一个线程修改了它增删元素很可能会抛出ConcurrentModificationException。解决方案是使用Collections.synchronizedSet(new HashSet())包装或者使用ConcurrentHashMap.newKeySet()创建的线程安全Set。3. TreeSet有序的代价与比较的哲学如果你需要一个元素自动排序的Set那么TreeSet就是你的菜。它实现了SortedSet和NavigableSet接口意味着元素总是处于排序状态并且你可以方便地进行范围查询如子集、头部集、尾部集和获取相邻元素。3.1 底层结构红黑树的威力TreeSet的内部依赖于一个TreeMap实例和HashSet依赖HashMap如出一辙。而TreeMap的底层是一颗红黑树Red-Black Tree。红黑树是一种自平衡的二叉查找树。它通过在插入和删除时进行特定的旋转和变色操作来保证树的高度大致平衡从而确保查找、插入、删除的最坏时间复杂度都是O(log n)。这就是TreeSet有序的代价——它的各项操作比HashSet的O(1)要慢但换来了有序性和强大的范围操作能力。3.2 排序的规则Comparable vs ComparatorTreeSet如何知道怎么给元素排序呢有两种方式1. 自然排序实现Comparable接口让你的元素类实现ComparableT接口并定义compareTo(T o)方法。class Student implements ComparableStudent { int score; String name; // ... 构造器等 Override public int compareTo(Student other) { // 按分数降序分数相同按名字字母序 int scoreCompare Integer.compare(other.score, this.score); // 降序 if (scoreCompare ! 0) { return scoreCompare; } return this.name.compareTo(other.name); } } public static void main(String[] args) { SetStudent rankSet new TreeSet(); rankSet.add(new Student(90, Alice)); rankSet.add(new Student(85, Bob)); rankSet.add(new Student(90, Charlie)); // 分数与Alice相同按名字排 // 遍历时顺序将是: Alice(90), Charlie(90), Bob(85) }2. 定制排序传入Comparator比较器在创建TreeSet时传入一个Comparator对象。这种方式更灵活尤其适用于无法修改元素类比如使用的是第三方库的类或者需要多种不同排序规则的场景。// 按字符串长度排序长度相同则按字典序 SetString lengthOrderedSet new TreeSet(new ComparatorString() { Override public int compare(String s1, String s2) { int lenDiff Integer.compare(s1.length(), s2.length()); if (lenDiff ! 0) { return lenDiff; } return s1.compareTo(s2); } }); lengthOrderedSet.add(apple); lengthOrderedSet.add(banana); lengthOrderedSet.add(cat); // 遍历顺序: cat, apple, banana重要TreeSet判断元素是否“重复”依赖的不是equals()和hashCode()而是比较器Comparator或Comparable。如果compareTo()或compare()方法返回0TreeSet就认为两个元素是“相等”的不会插入第二个。因此必须确保比较逻辑与equals逻辑一致虽然不强制但强烈建议否则会产生违反直觉的行为。3.3 高级操作NavigableSet接口TreeSet实现了NavigableSet提供了非常方便的导航方法TreeSetInteger scores new TreeSet(Arrays.asList(56, 72, 85, 90, 95)); // 获取大于等于80的最小元素 Integer ceiling scores.ceiling(80); // 85 // 获取小于80的最大元素 Integer floor scores.floor(80); // 72 // 获取严格大于80的最小元素 Integer higher scores.higher(80); // 85 // 获取严格小于80的最大元素 Integer lower scores.lower(80); // 72 // 获取子集 [72, 90] SortedSetInteger subSet scores.subSet(72, true, 90, true); // 获取头部集小于85的部分 SortedSetInteger headSet scores.headSet(85, false); // (56, 72) // 获取尾部集大于等于85的部分 SortedSetInteger tailSet scores.tailSet(85, true); // [85, 90, 95] // 获取第一个和最后一个元素 Integer first scores.first(); // 56 Integer last scores.last(); // 95 // 弹出第一个元素 Integer polledFirst scores.pollFirst(); // 56 同时从set中移除这些方法在做区间查询、排行榜、范围筛选等功能时极其有用。3.4 性能考量与使用场景优点元素有序支持高效的范围查询和导航。缺点插入、删除、查找的平均和最坏时间复杂度都是O(log n)比HashSet慢。适用场景需要元素自动排序如排行榜、有序去重列表。需要频繁进行范围查询如查找某个分数区间的学生。元素需要按特定顺序遍历。4. LinkedHashSet鱼与熊掌的兼得有没有一种Set既能像HashSet一样有O(1)时间复杂度的查找性能又能维护元素的插入顺序LinkedHashSet就是答案。4.1 双重链接列表的妙用LinkedHashSet是HashSet的子类。它在HashSet的“数组链表/红黑树”结构基础上增加了一个双向链表。这个链表贯穿了所有元素并且记录了元素的插入顺序。// 简化理解其内部结构 public class LinkedHashSetE extends HashSetE implements SetE, Cloneable, java.io.Serializable { // 它继承了HashSet的所有能力并维护了一个双向链表 }当你插入元素时和HashSet一样通过哈希算法决定桶的位置处理冲突。同时将这个新元素节点添加到内部双向链表的尾部。当你遍历LinkedHashSet时通过iterator()或for-each迭代器实际上是沿着这个双向链表从头走到尾因此你看到的顺序就是元素被add进来的顺序。4.2 特性与性能分析有序性维护插入顺序insertion-order。注意这不是排序顺序只是先来后到。性能由于比HashSet多维护了一个链表在插入和删除时会有微小的额外开销主要是维护链表的指针。但add,contains,remove操作的平均时间复杂度依然是O(1)。遍历速度甚至比HashSet略快因为直接遍历链表是O(n)而遍历HashSet需要遍历所有桶可能涉及空桶。内存占用比HashSet占用更多内存因为每个元素节点需要额外的before和after引用来维护链表。4.3 典型应用场景LinkedHashSet在需要预测的迭代顺序和快速查找的场景下非常有用。LRU最近最少使用缓存的基础实现虽然完整的LRU缓存需要更复杂的逻辑但LinkedHashSet维护插入顺序的特性可以很容易地改造。例如访问一个元素时先将其从Set中移除再重新插入它就会被移到链表末尾最近使用。链表头部就是最久未使用的元素。需要去重且保留原始顺序比如解析一个配置文件需要收集所有出现过的配置项键名去重但最后输出警告或文档时希望按照它们在文件中首次出现的顺序列出。连接池或资源池管理需要快速判断一个连接是否在池中contains同时又需要按照申请或释放的顺序来管理资源。// 一个简单的去重并保留顺序的例子 String text apple banana apple orange banana; String[] words text.split( ); SetString uniqueWordsInOrder new LinkedHashSet(); for (String word : words) { uniqueWordsInOrder.add(word); } System.out.println(uniqueWordsInOrder); // 输出 [apple, banana, orange] 保持了首次出现的顺序5. 性能对比与选型决策指南了解了三种主要Set的实现原理我们来做一次横向对比这能帮助你在实际编码中做出最合适的选择。特性HashSetTreeSetLinkedHashSet底层数据结构哈希表 (基于HashMap)红黑树 (基于TreeMap)哈希表 双向链表元素顺序无保证基于哈希值自然排序或定制排序插入顺序add,contains,remove平均时间复杂度O(1)O(log n)O(1)(略高于HashSet)是否允许null元素允许一个null如果使用自然排序不允许因为null无法比较。如果提供能处理null的Comparator则允许。允许一个null线程安全否否否内存开销较低较低树节点较高多两个链表引用核心接口SetSet,SortedSet,NavigableSetSet判断重复的依据hashCode()与equals()compareTo()或compare()(返回0)hashCode()与equals()典型使用场景通用去重集合对顺序无要求追求最高性能。需要元素自动排序或需要进行范围查询。需要去重且保留元素插入顺序同时要求较高的查找性能。选型决策流程问顺序你需要元素有序吗不需要- 优先考虑HashSet。需要- 进入第2步。问何种顺序你需要的是排序顺序还是插入顺序排序顺序- 选择TreeSet。插入顺序- 选择LinkedHashSet。考虑性能和功能如果选择了HashSet但后来发现需要插入顺序可以轻松替换为LinkedHashSet因为后者是前者的子类API兼容。如果选择了TreeSet要确认元素类实现了Comparable或者你能提供Comparator。同时接受O(log n)的性能。如果内存非常紧张且不需要插入顺序HashSet是内存最省的。如果需要线程安全任何Set都需要用Collections.synchronizedSet()包装或者考虑ConcurrentHashMap.newKeySet()。6. 源码级高频面试题深度剖析面试中关于Set的问题很少只问API通常会深入到源码和设计理念。这里剖析几个经典问题。6.1 HashSet如何保证元素唯一性与HashMap的关系这个问题考察对HashSet本质的理解。回答要点HashSet内部持有一个HashMap实例所有元素作为这个HashMap的键存储值则是一个固定的PRESENT对象。当调用add(e)时实际调用map.put(e, PRESENT)。HashMap的put方法通过hashCode()定位桶通过equals()在冲突链上比较确保键的唯一性。因此HashSet的“唯一性”完全委托给HashMap的键唯一性机制来实现。这也意味着存入HashSet的自定义对象必须正确重写hashCode()和equals()方法。6.2 TreeSet的排序原理是什么compareTo返回0代表什么这个问题考察TreeSet的排序和去重机制。TreeSet基于TreeMap实现底层是红黑树。排序原理依赖两种方式元素实现Comparable接口的compareTo方法或在构造TreeSet时传入Comparator比较器。红黑树在插入新元素时会从根节点开始根据比较器的结果负数、零、正数决定向左子树还是右子树查找插入位置。最关键的一点在TreeSetTreeMap中compareTo或compare方法返回0并不直接表示两个对象equals而是表示在排序视角下它们“相等”。TreeSet会将返回0视为重复元素拒绝插入。这意味着即使两个对象的equals方法返回false只要比较器认为它们相等返回0后者也无法加入TreeSet。这有时会导致与HashSet行为不一致设计时需要留意。6.3 LinkedHashSet如何维护插入顺序这个问题考察LinkedHashSet的独特设计。LinkedHashSet继承自HashSet。在HashSet的哈希表结构之外它内部维护了一个双向链表。这个链表独立于哈希表的桶结构它按插入顺序链接了所有的元素条目Entry。当新元素插入时除了像HashSet一样放入对应的哈希桶还会被链接到这个双向链表的尾部。当迭代LinkedHashSet时迭代器是沿着这个双向链表进行遍历的因此迭代顺序就是元素的插入顺序。这个设计以微小的内存和性能开销维护链表指针换取了可预测的迭代顺序。6.4 如何选用合适的Set实现在多线程环境下呢这是对综合知识的考察。选型如上一节所述无顺序要求用HashSet要排序用TreeSet要插入顺序用LinkedHashSet。多线程环境所有基本的Set实现HashSet,TreeSet,LinkedHashSet都是非线程安全的。最简单的同步方法是使用Collections.synchronizedSet()包装SetString syncSet Collections.synchronizedSet(new HashSet());对于高并发场景更推荐使用ConcurrentHashMap衍生出的线程安全SetSetString concurrentSet ConcurrentHashMap.newKeySet();它的性能通常优于同步包装器因为它采用了分段锁或CAS等更细粒度的并发控制。注意ConcurrentHashMap.newKeySet()返回的Set不保证像LinkedHashSet那样的插入顺序如果需要有序的并发Set可能需要考虑ConcurrentSkipListSet基于跳表实现有序线程安全。7. 进阶话题与最佳实践7.1 使用Set进行集合运算Set接口提供了丰富的集合运算方法这些方法直接对应数学集合论中的操作非常直观高效。SetInteger setA new HashSet(Arrays.asList(1, 2, 3, 4)); SetInteger setB new HashSet(Arrays.asList(3, 4, 5, 6)); // 并集 (Union) SetInteger union new HashSet(setA); union.addAll(setB); // union {1, 2, 3, 4, 5, 6} // 交集 (Intersection) SetInteger intersection new HashSet(setA); intersection.retainAll(setB); // intersection {3, 4} // 差集 (Difference, A - B) SetInteger difference new HashSet(setA); difference.removeAll(setB); // difference {1, 2} // 对称差集 (Symmetric Difference, (A ∪ B) - (A ∩ B)) SetInteger symmetricDiff new HashSet(setA); symmetricDiff.addAll(setB); // 先并 SetInteger tmp new HashSet(setA); tmp.retainAll(setB); // 再交 symmetricDiff.removeAll(tmp); // symmetricDiff {1, 2, 5, 6} // 判断子集 boolean isSubset setB.containsAll(new HashSet(Arrays.asList(4, 5))); // true7.2 遍历Set的几种方式及其性能增强for循环 (for-each)最常用、最简洁的方式。底层使用的是Iterator。for (String item : hashSet) { System.out.println(item); }显式使用Iterator可以在遍历时安全地移除元素使用iterator.remove()这是其他方式做不到的。IteratorString iterator hashSet.iterator(); while (iterator.hasNext()) { String item iterator.next(); if (someCondition(item)) { iterator.remove(); // 安全移除 } }注意在for-each循环中直接调用Set的remove()方法会引发ConcurrentModificationException。Java 8 Stream API适用于函数式操作如过滤、映射、收集。hashSet.stream() .filter(s - s.startsWith(A)) .map(String::toUpperCase) .forEach(System.out::println);并行流对于非常大的Set可以利用多核。hashSet.parallelStream().forEach(...); // 谨慎使用需确保操作是线程安全的。转换为数组toArray()方法但通常没必要直接遍历更好。性能对于HashSet和LinkedHashSet遍历的时间复杂度是O(n)n是元素数量。LinkedHashSet的遍历由于是链表可能略快且稳定。TreeSet的遍历中序遍历也是O(n)并且是按顺序的。7.3 内存优化与大型Set处理当Set中元素数量极大数十万、百万级时需要特别关注内存和性能。初始化容量对于HashSet和LinkedHashSet务必在构造时指定一个足够大的初始容量避免频繁扩容和rehash。一个粗略的估计是初始容量 预计元素数量 / 负载因子。例如预计有100万元素负载因子0.75初始容量可设为(int)(1_000_000 / 0.75) 1 ≈ 1,333,334取一个接近的2的幂次比如1,048,576(2^20) 或2,097,152(2^21)。负载因子提高负载因子如0.8或0.9可以减少哈希表数组的大小节省内存但会增加哈希冲突可能降低查找性能。这是一个时间换空间的权衡。对象本身的大小如果Set中存储的是自定义对象确保对象本身是精简的。避免在作为Set元素的对象中持有不必要的引用或大数组。考虑替代方案如果元素是数值型或可以编码为数值型可以考虑使用BitSet位集来模拟集合它极其节省内存。如果元素是字符串且范围有限如IP地址、电话号码可以考虑使用前缀树Trie结构。如果数据量巨大到单机内存无法承受需要考虑使用分布式缓存如Redis的Set结构或数据库。7.4 常见陷阱与调试技巧陷阱可变对象作为Set元素如果一个对象在加入HashSet后其用于计算hashCode()的字段被修改了那么这个对象在Set中就“丢失”了——你无法再通过contains找到它也无法删除它因为它所在的哈希桶已经变了但Set内部还把它放在老的桶里。class MutableKey { String value; // ... 构造器 getter/setter Override public int hashCode() { return value.hashCode(); } Override public boolean equals(Object o) { ... } // 基于value比较 } MutableKey key new MutableKey(initial); SetMutableKey set new HashSet(); set.add(key); System.out.println(set.contains(key)); // true key.setValue(modified); // 修改了关键字段 System.out.println(set.contains(key)); // false! 灾难发生了。最佳实践尽量使用不可变对象如String,Integer作为Set的键。如果必须用可变对象确保在对象加入Set后不再修改其影响hashCode和equals的字段。调试技巧查看HashSet内部状态虽然不能直接访问但可以通过反射仅用于调试或打印一些聚合信息来辅助。观察大小和容量关系set.size()是元素数。可以通过反射获取内部HashMap的table长度来估算容量不推荐生产环境用。使用Profiler工具如VisualVM, YourKit, JProfiler它们可以查看对象的内存布局包括HashMap的桶分布帮助你发现哈希冲突是否严重。TreeSet与null值使用自然排序即元素实现Comparable的TreeSet不能包含null。因为compareTo方法在遇到null时会抛出NullPointerException。如果你需要一个可排序且能包含null的Set必须在构造时提供一个能处理null的Comparator例如将null视为最小或最大值。// 一个将null视为最小的Comparator ComparatorString nullsFirstComparator Comparator.nullsFirst(String::compareTo); SetString treeSetWithNull new TreeSet(nullsFirstComparator); treeSetWithNull.add(null); treeSetWithNull.add(apple); // 顺序是 [null, apple]理解Set不仅仅是记住几个API更是理解其背后的数据结构设计哲学和性能权衡。从HashSet的哈希魔法到TreeSet的红黑树秩序再到LinkedHashSet的链表巧思每一种实现都是为了在特定场景下取得最佳的效率与功能平衡。在实际开发中根据“是否需要顺序”、“需要何种顺序”、“性能要求如何”、“内存是否敏感”这几个问题你就能快速准确地选出最合适的Set。而深入理解其源码机制不仅能让你在面试中游刃有余更能让你在遇到性能瓶颈或诡异Bug时拥有直指问题根源的洞察力。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号