Java中能否不使用size()以快于O(n)的时间获取List最后一个元素?
首先得明确:SonarQube的这个提示是针对部分集合的size()方法是O(n)时间复杂度的情况(比如它提到的ConcurrentLinkedQueue,但这是Queue不是List),而Java里大部分常用List的size()其实都是O(1)的——比如ArrayList维护了一个size变量,直接返回;LinkedList同样维护了size字段,也是O(1)。但如果你确实需要避免调用size(),可以根据List的具体实现用下面的方法:
针对LinkedList的最优解
LinkedList本身提供了直接获取首尾元素的方法,完全不需要调用size():
Long neededID = ((LinkedList<Long>) ids).getLast();
或者用反向迭代器:
Long neededID = ids.descendingIterator().next();
这两种方法对LinkedList来说都是O(1)时间复杂度,因为LinkedList是双向链表,直接持有尾节点的引用,不需要遍历整个集合。
通用兼容所有List的方案(仍需注意空集合)
如果不确定getIds()返回的List具体类型,又想尽量避免size(),可以用反向迭代器的方式,虽然对于ArrayList来说,descendingIterator()内部还是会用到size(),但至少代码层面没显式调用:
if (!ids.isEmpty()) { Long neededID = ids.descendingIterator().next(); }
这里一定要先判断集合非空,否则next()会抛出NoSuchElementException,和原来的get(size()-1)抛出IndexOutOfBoundsException类似,都需要做空判断。
额外说明
如果getIds()返回的是ArrayList这类基于数组的List,其实完全不用纠结——它的size()是O(1)的,Sonar的提示在这里属于过度预警,直接用原来的写法也没问题。只有当返回的是一些特殊的List实现(比如自定义的、没有维护size字段的链表),才需要考虑替换写法。
内容的提问来源于stack exchange,提问作者Ram Reso

