add()返回boolean表示元素是否实际加入,addAll()是取差集操作且时间复杂度O(n×m),containsAll()在ArrayList中为O(n×m)需优化。

add() 和 addAll():添加元素时的类型与返回值陷阱
Collection 的 add(E e) 方法看似简单,但新手常忽略它返回 boolean——不是“成功就完事”,而是明确告诉你“这个元素是否**实际被加入**”。比如 Set 实现(如 HashSet)遇到重复元素会返回 false,而 ArrayList 总是返回 true。不检查返回值,可能误以为去重失败或插入异常。
addAll(Collection extends E> c) 同样返回 boolean,但它的语义是“**至少有一个元素被新增**”,不是“全部加成功”。如果目标集合是只读的(如 Collections.unmodifiableList()),调用它会直接抛 UnsupportedOperationException,而非静默失败。
- 务必根据业务判断是否需要检查返回值,尤其在去重、幂等写入场景
- 批量添加前确认目标集合是否支持修改;若不确定,先用
new ArrayList(c)做安全兜底 - 泛型约束必须匹配:
Collection不能addAll一个Collection,编译期直接报错
remove() 与 removeAll():删除逻辑差异极大
remove(Object o) 只删**第一个匹配项**,且使用 equals() 判断相等性——这意味着如果你存的是自定义对象,没重写 equals() 和 hashCode(),它永远找不到你想要删的对象。
removeAll(Collection> c) 是“取差集”操作:从当前集合中删掉所有在参数集合中出现过的元素。注意:它不要求参数集合与当前集合同类型,只要元素能用 equals() 比较即可;但它会遍历整个当前集合,时间复杂度为 O(n×m),大数据量时慎用。
立即学习“Java免费学习笔记(深入)”;
- 删自定义对象前,必须确保该类已正确重写
equals()(和hashCode()) - 避免用
removeAll()处理上万级数据;可先将参数集合转为HashSet加速查找 -
remove(null)在多数实现中合法,但部分集合(如ConcurrentHashMap的 key 集合)禁止null,需查文档确认
contains() 和 containsAll():性能敏感点藏在背后
contains(Object o) 表面只是“查有没有”,但底层依赖集合实现:对 ArrayList 是 O(n) 遍历,对 HashSet 是 O(1) 哈希查找。同一段代码换实现类,性能可能差百倍。
containsAll(Collection> c) 更危险:它默认对参数集合中每个元素都调一次 contains()。如果参数集合很大,而目标集合是 ArrayList,就是典型的 O(n×m) 嵌套循环——1000 × 1000 就是百万次比较。
- 高频查询场景优先选
HashSet或TreeSet,别用ArrayList当查找容器 - 用
containsAll()前,先把参数集合转成Set,再遍历它查目标集合,可降为 O(m + n) - 传入空集合给
containsAll()总是返回true(空集是任意集合的子集),这是数学约定,不是 bug
toArray() 与 toArray(T[] a):数组转换最容易出 ArrayStoreException
toArray() 返回 Object[],安全但需强制转型,运行时才暴露类型问题;toArray(T[] a) 才是类型安全的推荐用法——但它有个反直觉规则:如果传入数组足够大,直接填充并返回;如果不够大,会新建一个指定类型的数组返回。很多人写成 list.toArray(new String[0]),以为省事,其实每次都要新建数组,有额外开销。
- 永远用带参版:
String[] arr = list.toArray(new String[list.size()]),复用容量、避免扩容 - 传入
new Object[0]在现代 JVM(JDK 11+)中已被优化,但语义不清,建议明确类型 - 若集合含
null元素,而目标数组类型是基本类型包装类(如Integer[]),不会报错;但后续拆箱时可能触发NullPointerException
真正难的不是记住方法名,而是理解每个方法在不同实现类下的行为差异、性能拐点和空值/泛型边界条件。写完一行 collection.remove(x),得清楚它背后是哈希寻址还是线性扫描,是删了一个还是删了一片,否则线上排查时只会看到日志里安静消失的数据。










