一步到位的方法是
list.subList(1, 4).clear();
如 java.util.LinkedList#subList(int, int) 的 Javadoc 中所述。
检查了源代码后,我发现这最终会一次删除一个元素。 subList 继承自 AbstractList。此实现返回一个List,当您在其上调用clear 时,它仅在后备列表上调用removeRange。 removeRange也继承自AbstractList,实现是
protected void removeRange(int fromIndex, int toIndex) {
ListIterator<E> it = listIterator(fromIndex);
for (int i=0, n=toIndex-fromIndex; i<n; i++) {
it.next();
it.remove();
}
}
如您所见,这会一次删除一个元素。 listIterator 是 在LinkedList 中被覆盖,它首先通过从列表的开头或结尾跟随链接来跟随链找到第一个节点(取决于fromIndex 是否在列表的前半部分或后半部分)。这意味着list.subList(i, j).clear() 具有时间复杂度
O(j - i + min(i, list.size() - i)).
除了最好从头开始并以相反的顺序删除元素的情况之外,我不相信有明显更快的解决方案。测试代码的性能并不容易,很容易得出错误的结论。
没有办法使用LinkedList类的公共API一次性删除中间的所有元素。这让我感到惊讶,因为 唯一 使用 LinkedList 而不是 ArrayList 的原因是您应该能够有效地从中间插入和删除元素,所以我认为这个值得优化的案例(特别是因为它很容易编写)。
如果您绝对需要 O(1) 性能,您应该能够从诸如
的调用中获得
list.subList(1, list.size() - 1)).clear();
您要么必须编写自己的实现,要么通过反射做一些脆弱和不明智的事情,如下所示:
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
for (int a = 0; a < 5; a++)
list.add(a);
removeRange_NEVER_DO_THIS(list, 2, 4);
System.out.println(list); // [0, 1, 4]
}
public static void removeRange_NEVER_DO_THIS(LinkedList<?> list, int from, int to) {
try {
Method node = LinkedList.class.getDeclaredMethod("node", int.class);
node.setAccessible(true);
Object low = node.invoke(list, from - 1);
Object hi = node.invoke(list, to);
Class<?> clazz = low.getClass();
Field nextNode = clazz.getDeclaredField("next");
Field prevNode = clazz.getDeclaredField("prev");
nextNode.setAccessible(true);
prevNode.setAccessible(true);
nextNode.set(low, hi);
prevNode.set(hi, low);
Field size = LinkedList.class.getDeclaredField("size");
size.setAccessible(true);
size.set(list, list.size() - to + from);
} catch (Exception e) {
throw new RuntimeException(e);
}
}