add()添加单个元素并返回boolean,addall()批量添加collection且仅当集合变化时返回true;二者均需注意返回值、异常、类型安全及并发安全。

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










