List允许重复元素,因其设计目标是按插入顺序保存并支持索引访问,所有标准实现(如ArrayList)均不检查重复,add()仅追加元素,无内置去重机制。

为什么 List 允许重复元素?因为它不强制唯一性约束
List 接口的设计目标是“按插入顺序保存元素,并支持通过索引随机访问”,它本身不定义去重逻辑。是否允许重复,取决于具体实现类是否主动检查——而所有标准实现(如 ArrayList、LinkedList、Vector)都选择不做检查。
-
add(E e)方法只是把元素追加到末尾或指定位置,不比较已有元素 - 没有内部哈希表或红黑树结构,无法高效判断“是否已存在”
- 重复判定依赖
equals(),但List不在增删时调用它做拦截
List 的有序性体现在索引、插入顺序和迭代顺序三者一致
“有序”在这里不是指元素大小排序,而是指“记录你什么时候加的、加在哪”。只要没手动调用 sort() 或用 Collections.reverse(),get(0) 永远返回第一个 add() 的元素。
- 多次调用
add("a"),indexOf("a")返回第一次出现的位置,lastIndexOf("a")返回最后一次 -
listIterator()从头到尾遍历,顺序严格对应插入顺序 - 即使元素内容相同(如两个
new Integer(1)),它们仍是独立对象,各自占一个索引位
想禁止重复?别硬改 List,换集合类型更合理
强行在 List 上每次 add() 前用 contains() 判断,时间复杂度是 O(n),且破坏了语义——你真正要的是“唯一+有序”,这正是 LinkedHashSet 的定位。
-
LinkedHashSet:插入顺序 + 去重,add()返回false表示已存在 - 如果还需索引访问,可封装一层:用
LinkedHashSet存数据,另用ArrayList同步维护顺序(但通常没必要) -
TreeSet虽然去重,但按自然序或比较器排序,不是插入序,不符合“有序集合”的常见预期
LinkedHashSetset = new LinkedHashSet<>(); set.add("a"); set.add("b"); set.add("a"); // 这次 add() 返回 false,集合不变 // 遍历时顺序仍是 ["a", "b"]
容易忽略的细节:equals() 和 hashCode() 影响的是“是否重复”,不是“是否有序”
重复判断只发生在你显式调用 contains()、remove(Object) 或使用 Set 类型时;List 自身的 add()、get()、size() 全部无视这两个方法。
立即学习“Java免费学习笔记(深入)”;
- 哪怕两个自定义对象
equals()返回true,往ArrayList里加两次,它们就是两个独立元素 - 反过来说,如果你重写了
equals()却忘了同步改hashCode(),后续放进HashSet就可能出问题——但这和List无关 - “有序”是结构保证的,不依赖对象的任何方法;“重复”是语义判断,才依赖
equals()










