ArrayList的get()和set()是O(1)操作,LinkedList则需遍历;set()不扩容、不插入,仅替换存在索引处元素;get()返回引用,修改对象内容不影响集合结构;二者均非线程安全,遍历时调用set()可能触发ConcurrentModificationException。

get() 和 set() 方法的底层行为必须理解清楚
Java 中 ArrayList 的 get() 和 set() 是 O(1) 操作,但仅限于支持随机访问的实现类(如 ArrayList);LinkedList 虽然也实现了这两个方法,但内部是遍历查找,索引越大越慢。别默认“List 都一样快”——用 LinkedList 频繁调用 get(10000) 会明显卡顿。
set() 修改元素前必须确认索引存在
set() 不会自动扩容,越界直接抛 IndexOutOfBoundsException;它只替换已有位置的值,不插入、不移动其他元素。常见错误是把 set() 当成 add() 用:
-
list.set(5, "x")要求list.size() > 5,否则崩溃 - 想在末尾追加?用
list.add("x")或list.add(list.size(), "x") - 想在中间插入并移位?必须用
list.add(index, element),不是set()
get() 返回的是引用,修改对象内容不触发集合变更检测
如果 List 存的是可变对象(比如 ArrayList<StringBuilder>),get(i) 拿到的是原对象引用,后续调用 .append() 等操作会真实改变集合里那个元素的状态——但集合自身结构(size、modCount)不会变,所以不会触发 ConcurrentModificationException,也不会被观察者感知。这容易造成隐蔽的逻辑错乱:
StringBuilder sb = new StringBuilder("a");
list.add(sb);
StringBuilder got = list.get(0);
got.append("b"); // list.get(0).toString() 现在是 "ab"
// 但 list 本身没“被修改”,迭代器不会失效,size 仍是 1
并发场景下 get/set 不是线程安全的
即使只读地反复调用 get(),只要其他线程同时在 add() 或 remove(),就可能遇到 ConcurrentModificationException(fail-fast);而 set() + get() 组合也不是原子操作,中间可能被其他线程插队改掉同一位置的值。真要安全操作,要么加锁,要么换 CopyOnWriteArrayList(注意:它的 set() 是线程安全的,但 get() 仍可能读到旧快照)。
最常被忽略的一点:用增强 for 循环或迭代器遍历时,千万别在循环体内调用 set()——看似没改结构,但某些 List 实现(如 ArrayList)的迭代器仍会检查 modCount,而 set() 会更新它,导致抛异常。

















