ArrayList尾部add均摊O(1),中间插入需移动元素;LinkedList任意位置add本为O(1),但add(int,E)先遍历查节点致O(n);高频尾加选ArrayList,已知位置插删可用LinkedList配合listIterator()。
因为 ArrayList 底层是数组,add() 默认追加时均摊 O(1),但中间插入(如 add(index, e))需移动后续元素;LinkedList 是双向链表,任意位置 add() 都是 O(1),但前提是已持有目标节点引用——而 add(int, E) 会先调用 node(int) 遍历查找,实际变成 O(n)。
ArrayList(注意预设 initialCapacity 避免多次扩容)LinkedList,但必须配合 listIterator() 或直接操作 Node(不推荐手动)LinkedList 当“更灵活的 List”——它在随机访问、内存占用、缓存局部性上全面劣于 ArrayList
因为 HashMap 查找流程是:先算 hashCode() 定位桶(bucket),再用 equals() 在该桶内比对键。若只重写 equals() 不重写 hashCode(),两个逻辑相等的对象可能被散列到不同桶,导致 get() 返回 null;若只重写 hashCode(),所有对象哈希值相同,退化为链表遍历,性能暴跌。
hashCode()/equals() 覆盖全部参与比较的字段String、自定义 final 类)作 key 最安全;若用可变对象,确保修改后 hashCode() 不变Objects.hash(a, b, c) 比手写 31 * a.hashCode() + b.hashCode() 更简洁且防空指针JDK 7 的 ConcurrentHashMap 用 Segment 数组分段加锁,粒度粗、内存开销大;JDK 8 改为 synchronized 锁单个 Node(即哈希桶头结点),配合 CAS + 红黑树优化长链表场景,锁粒度更细、扩容支持并发、代码更简洁。
ConcurrentHashMap 远优于 Collections.synchronizedMap()
computeIfAbsent() 是线程安全的“查无则建”,避免先 get() 再 put() 的竞态条件size() 实时准确——它返回估算值;需精确计数建议用 mappingCount()
从 JDK 16 开始,Collectors.toList() 默认返回的是不可修改的 List(内部是 ImmutableCollections.ListN),调用 add() 或 clear() 会抛 UnsupportedOperationException。这不是 bug,而是为了防止意外修改流式处理结果。
new ArrayList(stream.collect(Collectors.toList()))
Collectors.toCollection(ArrayList::new)
toSet()、toMap() 在新版本中也倾向返回不可变实现,留意运行时异常Listmutable = stream .map(String::toUpperCase) .collect(Collectors.toCollection(ArrayList::new));
集合框架的坑不在 API 多难,而在默认行为随 JDK 版本悄悄变化——尤其是不可变性、并发策略和性能边界这些隐性契约。