Java常用数据结构需理解适用场景与问题:ArrayList随机访问O(1)但add(0,e)触发数组复制,频繁头插应选LinkedList;HashMap作key的自定义类必须重写hashCode()和equals(),且放入后勿修改影响哈希的字段;递归慎转循环,回溯与多分支场景手动模拟易错。

Java 里真正常用的数据结构不是靠死记 API,而是理解「什么时候该用哪个」以及「不用会出什么问题」。算法基础也不是背排序代码,而是能看懂 ArrayList 的扩容机制、HashMap 的哈希冲突处理、LinkedList 的插入开销——这些才是日常写 bug 或调性能时实际卡住你的点。
为什么 ArrayList 随机访问快但频繁 add(0, e) 很慢
ArrayList 底层是数组,get(i) 是 O(1),但往开头或中间插入元素会触发 System.arraycopy,把后续所有元素往后挪一位。10 万个元素时 add(0, x) 一次就要移动 10 万次引用。
- 真要头插多、遍历少,改用
LinkedList(但注意它没有真正的随机访问) - 如果只是偶尔在开头加,不如先
add(e)到末尾,最后Collections.reverse() - 初始化时预估大小:比如知道要存 5000 条日志,就用
new ArrayList(5000),避免多次扩容复制
HashMap 的 key 为什么必须重写 hashCode() 和 equals()
不重写的话,默认用 Object.hashCode()(基于内存地址),两个内容相同的自定义对象会算出不同 hash 值,被分到不同桶里;即使侥幸落到同一桶,equals() 也返回 false,导致查不到、重复插入。
- 只要用了自定义类作 key,就必须同时重写这两个方法,缺一不可
- IDE 生成的
hashCode()要包含所有参与equals()判断的字段 - 字段值在放入 map 后又被修改了?那 hash 值可能失效——
HashMap不会重新定位,这个 key 就“丢了”
递归写法容易栈溢出,但不是所有递归都能简单改成循环
像求斐波那契、二叉树深度这种线性递归,确实可以压栈模拟成 while + Stack;但涉及回溯(如全排列、N 皇后)、多分支递归(如归并排序拆分)时,手动维护状态复杂度陡增,反而更容易错。
立即学习“Java免费学习笔记(深入)”;
- 先确认递归深度:如果数据规模可控(比如树高
- 对尾递归,部分 JVM(如 GraalVM)能优化,但 OpenJDK 8/11 不支持,别依赖
- 真爆栈了,优先考虑减少单次递归的局部变量数量,而不是硬转循环
Arrays.sort() 和 Collections.sort() 用的不是同一个算法
Arrays.sort(int[]) 对基本类型用双轴快排(Dual-Pivot Quicksort),最坏 O(n²);而 Collections.sort(List 对对象列表用的是 TimSort(归并+插入混合),稳定且最坏 O(n log n)。
- 排序
Integer[]和int[]性能差异可能达 3 倍以上,因为前者要装箱、后者直接操作内存 -
Arrays.sort()对对象数组(如String[])也用 TimSort,和Collections.sort()行为一致 - 自定义比较逻辑时,用
Comparator.comparing(...)比匿名内部类更简洁安全
ComparatorbyAgeThenName = Comparator.comparing(User::getAge) .thenComparing(User::getName);
最容易被忽略的是:集合类的行为边界往往藏在文档小字里,比如 LinkedHashSet 迭代顺序是插入顺序,但它的 contains() 时间复杂度仍是 O(1);又比如 PriorityQueue 不保证整体有序,只保证 peek() 是最小(或最大)元素。不翻源码或 Javadoc,光靠名字猜,十有八九踩坑。










