add()添加单个元素并返回boolean,addAll()批量添加Collection且仅当集合变化时返回true;二者均需注意返回值、异常、类型安全及并发安全。
add() 方法只能添加单个元素,且返回 boolean 值
调用
时,集合会尝试插入一个元素,成功则返回
;若集合不允许重复(如
)且该元素已存在,或集合是只读的,就返回
。注意:
实现(如
)几乎总是返回
,但不能假设所有实现都如此——比如
包装后的列表调用
会直接抛出
。
常见错误是忽略返回值、误以为“调用即成功”。尤其在并发或不可变封装场景下,不检查返回值或异常会导致逻辑静默失败。
接收单个参数,类型必须与集合泛型一致,否则编译报错:
对
会触发类型检查失败
某些集合(如
)的
有隐式排序开销,频繁调用不如批量构建后一次性初始化
对
的支持取决于具体实现:
允许,
若未指定
则禁止
addAll() 必须传入另一个 Collection,不是可变参数
的签名是
,它只接受一个
实例,不是
那样的可变参数。试图写成
会编译失败。
典型误用是把数组或多个值直接传进去,结果出现
这类错误。
若手头是数组,需先转为
:
或 Java 8+ 的
若想添加多个字面量,必须显式构造集合:
返回
当且仅当目标集合因此发生了变化(即至少有一个新元素被加入),空集合或全重复元素时返回
addAll() 在不同集合上的行为差异很大
虽然接口统一,但底层逻辑差别直接影响性能和语义。例如向
尾部
是 O(n),而向头部插入大量元素(用
)可能退化为 O(n²);
的
本质是多次
,但会复用哈希计算,比循环调用
略快;
的
会复制整个底层数组,大数据量时内存和时间开销显著。
不保证原子性:如果中途抛异常(如
),部分元素可能已插入,剩余未插入
对
(如
),传入的集合无需有序,但每个元素仍要满足排序规则,否则抛
或
若源集合本身正在被其他线程修改,
可能抛
(取决于具体实现是否 fail-fast)
别在 foreach 循环里对正在遍历的集合调用 add 或 addAll
这是最常踩的运行时坑:
不是因为多线程,而是因为单线程中迭代器检测到集合结构被修改。例如:
即使你用
手动遍历,也**不能**用集合自身的
或
—— 必须用
(仅
支持)或收集待添加项后批量处理。
安全做法是先收集要加的元素到临时集合,循环结束后再调用
若需边遍历边插入且保持顺序,考虑用
获取
,然后用其
方法
的
同样不安全,不要在其中调用外部集合的修改方法
实际编码时,add 和 addAll 的选择往往不是“功能差异”,而是“语义明确性”和“边界条件控制”。很多人只记得“能加就行”,却忘了检查返回值、忽略并发场景下的异常类型、或者误判了
对源集合的依赖程度——这些细节在测试覆盖不到的分支里最容易暴露。
add()trueSetfalseListArrayListtrueCollections.unmodifiableList()add()UnsupportedOperationExceptionadd()list.add("hello")ListPriorityQueueadd()nullArrayListTreeSetComparatornulladdAll()boolean addAll(Collection extends E> c)Collectionadd(...)list.addAll(1, 2, 3)Cannot resolve method 'addAll(int, int, int)'CollectionArrays.asList(arr)Arrays.stream(arr).collect(Collectors.toList())list.addAll(Arrays.asList("a", "b", "c"))addAll()truefalseLinkedListaddAll()addAll(0, other)HashSetaddAll()add()add()CopyOnWriteArrayListaddAll()addAll()NullPointerExceptionSortedSetTreeSetClassCastExceptionNullPointerExceptionaddAll()ConcurrentModificationExceptionConcurrentModificationExceptionfor (String s : list) {
list.add("new"); // ? 抛 ConcurrentModificationException
}Iteratoradd()addAll()Iterator#add()ListIteratoraddAll()list.listIterator()ListIteratoradd()StreamforEach()addAll()