[英]Java LinkedList : remove from to to
我有一个java.util.LinkedList
包含逻辑上的数据
1 > 2 > 3 > 4 > 5 > null
我想删除2到4之间的元素,并使LinkedList
像这样
1 > 5 > null
实际上,我们应该能够在O(n)复杂度下实现这一点,考虑到你必须将链断开为2并在一次操作中将其连接到5。
在Java LinkedList中,我无法找到任何允许在单个O(n)操作中使用from和to从链表中删除链的函数。
它只为我提供了单独删除元素的选项(使每个操作O(n))。
无论如何,我只需一次操作即可实现这一目标(无需编写自己的列表)?
这里提供的一个解决方案使用单行代码解决了问题,但不是单个操作。 list.subList(1, 4).clear();
问题更多的是算法和性能。 当我检查性能时,这实际上比逐个删除元素慢。 我猜这个解决方案实际上并没有删除o(n)中的整个子列表,而是为每个元素逐个删除(每次删除O(n))。 还要添加额外的计算来获取子列表。
以ms为单位的平均1000000次计算:
没有sublist = 1414
使用提供的子列表解决方案:= 1846 **
一步到位的方法是
list.subList(1, 4).clear();
正如Javadoc中为java.util.LinkedList#subList(int,int)所记录的那样 。
检查完源代码后,我发现最终会逐个删除元素。 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);
}
}
要在单个操作(方法调用)中删除中间元素,您可以继承java.util.LinkedList
,然后公开对List.removeRange(int,int)的调用:
list.removeRange(1, 4);
(相信发布此答案的人然后删除它。:))但是,即使这个方法调用ListIterator.remove()
n次。
我不相信有一种方法可以从java.util.LinkedList
删除n个连续的条目,而无需在引擎盖下执行n个操作。
通常,从任何链表中删除n个连续项似乎需要O(n)操作,因为必须一次从一个项目从起始索引遍历到结束索引 - 固有地 - 以便在修改后的列表中查找下一个列表条目。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.