该用Set而非List当需自动去重且不关心顺序或索引;HashSet平均O(1)、LinkedHashSet保插入序、TreeSet支持排序但O(log n),注意null限制。
什么时候该用
而不是
当你需要自动去重、且不关心元素顺序或索引访问时,
是更合适的选择。比如读取一批用户 ID 并统计唯一数量,用
后手动去重不仅慢(
),还容易漏掉边界情况。
最常用:底层是哈希表,插入/查找平均
,但不保证顺序
适合需要按插入顺序遍历的场景,比如最近访问的资源缓存
用于需要自然排序或自定义排序的集合,比如排行榜分数去重后升序展示,但增删查是
注意:
不能存
(
直接抛
;
允许一个
,但要小心后续比较逻辑)
的键为什么必须是不可变对象
因为
(尤其是
)依赖键的
和
定位和比较元素。如果键对象在放入后被修改,导致
变化,就再也找不到它了——不是丢失,是“藏起来了”。
常见踩坑:用可变的
或自定义类作键,没重写
和
,或重写了但字段参与计算后又被改
安全做法:优先用
、
等不可变类型;若必须用自定义类,确保所有参与
/
的字段在构造后不可变
例如:
和
在实际业务中怎么配合用
很多场景下二者不是二选一,而是协同:用
存主数据 + 关联关系,用
做快速存在性判断或去重中间结果。
立即学习
“
Java免费学习笔记(深入)
”;
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
权限校验:把用户拥有的角色 ID 存进
,检查是否包含某个角色时用
,比遍历
快得多
批量更新防重:从消息队列收到一批订单 ID,先塞进
去重,再查
批量加载已有订单,避免重复 DB 查询
配置白名单:把合法的 API 路径存在
,拦截器里直接判断
性能关键点:别为了“看起来统一”而强行把
当
用——语义不清,内存浪费,还可能因
值引发误判。
并发环境下选哪个实现
单线程用
/
没问题,但多线程写入必须换线程安全版本,否则会出错或死循环(JDK 7 的
扩容时可能形成环形链表)。
简单读多写少:用
/
,但锁粒度大,吞吐低
高并发读写:优先选
;它没有
,但可用
(JDK 8+)替代
注意:
的
不是精确值(为避免锁竞争而妥协),需精确计数建议用
单独维护
最容易被忽略的是:即使用了
,复合操作如“检查不存在再 put”仍需
或
,否则仍有竞态。
SetListSetListO(n²)HashSetO(1)LinkedHashSetTreeSetO(log n)SetnullTreeSetNullPointerExceptionHashSetnullMapMapHashMaphashCode()equals()hashCode()StringBuilderhashCode()equals()StringIntegerhashCode()equals()Map userScore = new HashMap<>();
userScore.put("alice", 95);
// 后续用 "alice" 能稳定取到,因为 String 不可变 SetMapMapSetHashSetroles.contains("ADMIN")ListSetMapSetif (!allowedPaths.contains(path)) reject();MapSetnullHashSetHashMapHashMapCollections.synchronizedSet()synchronizedMap()ConcurrentHashMapConcurrentHashSetConcurrentHashMap.newKeySet()ConcurrentHashMapsize()LongAdderConcurrentHashMapcomputeIfAbsent()putIfAbsent()