List的核心契约是位置敏感的有序序列,明确定义基于索引的操作;允许重复和null,不依赖equals/hashCode;与Set的本质区别在于语义而非实现性能。

Java中List接口保证元素有序且允许重复
它不只“按添加顺序保存”,而是明确定义了get(int)、set(int, E)、add(int, E)等基于索引的操作,这意味着位置(index)是其核心契约。只要没调用sort()或replaceAll()等修改逻辑顺序的方法,list.get(0)永远返回第一个添加的元素——哪怕底层是ArrayList还是LinkedList。
List和Set最根本的区别在语义而非实现
很多人误以为“List快是因为数组,Set慢是因为哈希”,其实错在混淆了接口与实现。关键差异在于:
-
List允许null多次(list.add(null); list.add(null);合法) -
Set把equals()+hashCode()作为唯一性判断依据,List完全不关心这个 -
List.contains(Object)是O(n)遍历比较,Set.contains(Object)平均O(1),但这是实现决定的,不是接口规定的
常见误用:把List当无序容器传参或做去重
以下写法看似省事,实则埋坑:
public void process(Listitems) { Set unique = new HashSet<>(items); // 丢掉顺序! // 后续逻辑依赖原始添加顺序?这里已不可逆 }
如果调用方传入的是Arrays.asList("a", "b", "c"),你转成HashSet后迭代顺序完全不确定。真要保留顺序去重,得用LinkedHashSet,或者明确要求参数类型为Collection而非List。
立即学习“Java免费学习笔记(深入)”;
选ArrayList还是LinkedList?看操作模式,不是看“需不需要链表”
别被名字误导:LinkedList在现代JVM上绝大多数场景比ArrayList慢,除非你频繁在首尾做add(0, e)或remove(0)。真实建议:
- 95%以上场景用
ArrayList:随机访问O(1),扩容成本可控,缓存友好 - 只有当你持续调用
list.addFirst(e)或list.removeLast()(且不用get(i))时,LinkedList才有意义 -
Vector和Stack已过时,同步需求用Collections.synchronizedList(new ArrayList())或CopyOnWriteArrayList
List当成“可变长数组”的直觉——它本质是**位置敏感的序列协议**,所有违反位置语义的操作(比如用HashSet清空重复却不管顺序),都会让下游逻辑悄悄失效。










