选择关键看操作模式:随机访问多用ArrayList(O(1)),中间增删多用LinkedList(O(1)插入删除);误用是用for循环遍历LinkedList导致O(n²)。

ArrayList 和 LinkedList 的选择关键看操作模式
不是“ArrayList 更快所以默认用它”,而是得看你的主要操作是随机访问多,还是频繁在中间增删多。ArrayList 底层是数组,get(i) 是 O(1),但 add(index, e) 或 remove(index) 在中间位置会触发数组拷贝,平均 O(n);LinkedList 是双向链表,按索引查元素要从头或尾遍历,get(i) 是 O(n),但 addFirst、addLast、以及已有 ListIterator 时的插入/删除都是 O(1)。
常见误用:for (int i = 0; i 配合 LinkedList —— 这会变成 O(n²)。改用增强 for 循环或迭代器即可避免。
- 读多写少、需要下标快速访问 → 选
ArrayList - 高频头/尾插入、已有迭代器且需边遍历边删 →
LinkedList仍有价值(但注意 JDK 21+ 中SequencedCollection接口让ArrayList也支持高效头插语义) - 别为了“看起来更通用”而声明为
LinkedList却只用add(e)和get(i)
HashMap 的线程不安全体现在哪?ConcurrentHashMap 怎么规避
HashMap 在多线程 put 时可能触发扩容重哈希,若两个线程同时判断需扩容、又同时执行 transfer()(JDK 7)或 split()(JDK 8),会导致链表成环(JDK 7)或数据丢失(JDK 8+)。这不是“偶尔错”,而是明确未定义行为,get() 可能死循环或返回 null。
ConcurrentHashMap 不是简单加锁整张表:JDK 7 用分段锁(Segment 数组),JDK 8 改为 CAS + synchronized 控制单个桶(Node 链表头或红黑树根),写操作只锁必要范围,读操作完全无锁。
立即学习“Java免费学习笔记(深入)”;
- 只要没显式要求强一致性(如“所有线程立刻看到最新值”),
ConcurrentHashMap比Collections.synchronizedMap(new HashMap())并发吞吐高得多 -
computeIfAbsent()是原子的,适合缓存场景;但map.get(key) == null ? map.put(key, compute()) : map.get(key)是经典竞态,必须换用原子方法 -
size()在并发下是弱一致的(可能滞后),如需精确计数,考虑mappingCount()(返回 long,更准确)
ArrayList.subList() 返回的是视图,不是新集合
subList(fromIndex, toIndex) 返回的是原 ArrayList 的一个动态视图,底层共享同一份 elementData 数组。对子列表的结构性修改(如 add()、clear())会直接影响原列表,反之亦然。
ArrayListlist = new ArrayList<>(Arrays.asList("a", "b", "c", "d")); List sub = list.subList(1, 3); // ["b", "c"] sub.add("x"); // list 变成 ["a", "b", "c", "x", "d"] list.remove(0); // sub 变成 ["c", "x", "d"] —— 注意 sub.size() 现在是 3!
- 需要独立副本?显式构造:
new ArrayList(list.subList(from, to)) - 传给不可信 API 时尤其危险:对方调
clear()会清空你原列表的一部分 - 子列表的
indexOf()、contains()正常工作,但retainAll()这类结构性操作要格外小心
Map 接口里 keySet()、values()、entrySet() 的性能与用途差异
三者都返回视图,不复制数据,但迭代成本不同:entrySet() 是最高效的遍历方式,一次拿到 key 和 value;keySet() 迭代后若还需取 value,每次 map.get(key) 是额外哈希查找;values() 无法反查 key,且某些 Map 实现(如 IdentityHashMap)的 values 视图甚至不保证顺序或唯一性。
典型低效写法:for (String key : map.keySet()) { String value = map.get(key); ... } —— 多了一倍哈希计算。
- 只关心 key?用
keySet() - 只关心 value?用
values(),但注意它不支持remove()(除非是LinkedHashMap的 values 视图) - 要同时用 key 和 value?必须用
entrySet(),且优先用Map.Entry::getKey和Map.Entry::getValue -
HashMap的values()视图在并发修改时可能抛ConcurrentModificationException,和 keySet/entrySet 一样
subList() 的视图语义和 entrySet() 的遍历必要性——它们不报错,但会在数据规模上去后突然暴露性能或逻辑问题。










