add(E e)向List末尾添加元素,返回true而非元素本身;add(int index, E e)为插入;ArrayList扩容按比例(如1.5倍)且初始容量JDK14+为0;LinkedList add()是O(1)但内存开销大;非线程安全List并发add会抛ConcurrentModificationException。

add() 方法怎么用,参数和返回值别搞错
向 List 末尾添加元素,直接调用 add(E e) 就行,它不返回元素本身,而是返回 true(因为 List 接口继承自 Collection,约定如此)。别误以为它像 ArrayList.add() 返回旧值或索引——它根本不返回元素。
- 错误写法:
String s = list.add("hello");→ 编译失败,类型不匹配 - 正确写法:
list.add("hello");,想确认是否成功可接if (list.add("x")) { ... }(但通常没必要) - 注意重载方法
add(int index, E element)是插入而非追加,用错会导致元素位置偏移
ArrayList 自动扩容不是“每次+1”,而是按比例增长
底层是数组,满了就得换更大的数组拷贝过去。扩容不是线性加 1,而是旧容量 * 1.5(JDK 17+),再向上取整到 8 的倍数(早期版本逻辑略有差异)。这意味着少量 add 不影响性能,但频繁小步扩容会触发多次 Arrays.copyOf(),拖慢整体速度。
- 初始容量默认是 0(JDK 14+),首次 add 才分配 10 个元素空间;老版本默认 10
- 如果预估要存 1000 个元素,建议构造时指定:
new ArrayList(1024),避免中途扩容 3–4 次 - 扩容本质是新建数组 +
System.arraycopy(),对大列表(如 >10w 元素)要注意 GC 压力
LinkedList 的 add() 看似一样,但内部开销完全不同
LinkedList.add(E) 也是加到末尾,但它不涉及数组复制,而是新建节点、改指针。时间复杂度稳定 O(1),但每个元素额外占用两个引用字段(prev/next),内存占用比 ArrayList 高 50% 以上。
- 别因为“链表适合增删”就无脑选
LinkedList:随机访问get(i)是 O(n),遍历反而更慢 - 只有在**频繁在头尾增删 + 几乎不随机访问**的场景下,
LinkedList才有优势 - 调试时看到
LinkedList.size()是 O(1)(它自己维护计数),但别误以为size()调用便宜就能滥用——重点还是看你的访问模式
并发环境下 add() 直接报 ConcurrentModificationException
所有非线程安全的 List 实现(ArrayList、LinkedList)都不支持多线程同时 add。哪怕只是“读多写少”,只要有一个线程在 add,其他线程迭代或 get 都可能触发异常。
立即学习“Java免费学习笔记(深入)”;
- 常见错误:用 for-each 遍历
ArrayList同时另一个线程调用add()→ 立刻抛ConcurrentModificationException - 临时方案:用
Collections.synchronizedList(new ArrayList()),但仅保证单个操作原子,复合操作(如检查再添加)仍需手动同步 - 真正高并发追加场景,优先考虑
CopyOnWriteArrayList(适合读远多于写的场景)或ConcurrentLinkedQueue(若不需要 List 语义)
实际用的时候,扩容阈值、线程安全、内存布局这些细节不会立刻暴露问题,但一旦数据量上来或并发一加,它们就成了最硬的卡点。










