Java集合的size()方法时间复杂度几乎都是O(1),因标准实现(如ArrayList、HashMap等)直接返回内部size字段;例外仅见于未缓存大小的自定义或惰性集合。

Java集合的size()方法时间复杂度几乎都是O(1)
绝大多数标准Java集合类(如ArrayList、LinkedList、HashMap、HashSet、ArrayDeque)的size()方法都直接返回一个内部维护的int字段,不遍历、不计算,纯读取操作。
例外情况极少,仅出现在某些特殊实现中,比如:
- 基于装饰器模式的自定义包装类,未重写
size()而委托给底层但底层本身不支持O(1) - 某些惰性求值或流式集合(如Guava的
Iterables.size()对任意Iterable),但这不属于JDK集合接口的size()
size()为O(n)的罕见场景及如何识别
如果你发现调用collection.size()明显卡顿,大概率不是JDK集合本身的问题,而是你实际持有的对象并非标准集合实现。
常见误判点:
立即学习“Java免费学习笔记(深入)”;
- 传入的是
Iterable或自定义Collection子类,且未缓存大小(例如每次调用都遍历迭代器) - 使用了某些ORM框架返回的“伪集合”,如Hibernate的
PersistentBag在未初始化时调用size()可能触发SQL查询 - 调用了
Stream.count()误以为是Collection.size()——Stream.count()必为O(n),且不可复用
验证方式:打印实际运行时类型:
System.out.println(collection.getClass().getName());
不同集合的size()实现差异(源码级事实)
JDK中关键实现一目了然:
-
ArrayList:持有size字段,size()直接return size; -
LinkedList:JDK 8+ 同样维护size字段;JDK 7及之前也是O(1) -
HashMap:有size字段,同时modCount用于fail-fast,但size()不检查一致性 -
TreeSet/TreeMap:红黑树节点含size或通过子树维护,size()仍是O(1)
唯一明确非O(1)的是Collections.emptySet()等单例——它们也返回常量0,仍是O(1)。
别把size() == 0和isEmpty()混为一谈
虽然两者语义等价,但isEmpty()更直白、意图更清晰,且部分集合(如ConcurrentHashMap)的isEmpty()有微小优化(例如避免读size字段的内存屏障开销)。
建议写法:
- 判断空用
collection.isEmpty(),而非collection.size() == 0 - 需要具体数值时才用
size(),比如分页计算Math.min(offset + limit, list.size()) - 避免在循环条件里反复调用
size()(虽然O(1),但可读性和JIT优化角度仍推荐提取到局部变量)
真正容易被忽略的是:第三方库或业务代码里手写的Collection实现,只要没显式维护size字段并同步更新,size()就不可信——这种情况下,它不是“慢”,而是“错”。











