http://blog.chinabyte.com/blog.php?do-showone-uid-135325-itemid-454704-type-blog.html
总结下,LinkedList的两个remove方法,remove(Object)和remove(int)的时间复杂度都是O(n),在链表元素很多
并且没有索引可用的情况下,LinkedList也并不适合做随机增删元素。在对性能特别敏感的场景下,还是需要自己实现专用的双向链表结构,真正实现
O(1)级别的随机增删。更进一步,jdk5引入的ConcurrentLinkedQueue是一个非阻塞的线程安全的双向队列实现,同样有本文提到的
问题,有兴趣可以测试一下在大量元素情况下的并发随机增删,效率跟自己实现的特定类型的线程安全的链表差距是惊人的。确保PHP安全 不能违反的四条安全规则
理论上说,双向链表的删除的时间复杂度是O(1),你只需要将要删除的节点的前节点和后节点相连,然后将要删除的节点的前节点和后节点置为null即可,
//伪代码
node.prev.next=node.next;
node.next.prev=node.prev;
node.prev=node.next=null;
这个操作的时间复杂度可以认为是O(1)级别的。但是LinkedList的实现是一个通用的数据结构,因此没有暴露内部的节点Entry对象,remove(Object)传入的Object其实是节点存储的value,这里还需要一个查找过程:
public boolean remove(Object o) {
if (o==null) {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
//查找节点Entry
for (Entry<E> e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) {
//删除节点
remove(e);
return true;
}
}
}
return false;
}
java.util.LinkedList是双向链表,这个大家都知道,比如Java的基础面试题喜欢问ArrayList和LinkedList的区
别,在什么场景下用。大家都会说LinkedList随机增删多的场景比较合适,而ArrayList的随机访问多的场景比较合适。更进一步,我有时候会
问,LinkedList.remove(Object)方法的时间复杂度是什么?有的人回答对了,有的人回答错了。回答错的应该是没有读过源码。
删除节点的操作就是刚才伪代码描述的:
private E remove(Entry<E> e) {
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}
因此,显然,LinkedList.remove(Object)方法的时间复杂度是O(n)+O(1),结果仍然是O(n)的时间复杂度,而非推测的O(1)复杂度。最坏情况下要删除的元素是最后一个,你都要比较N-1次才能找到要删除的元素。
既然如此,说LinkedList适合随机删减有个前提,链表的大小不能太大,如果链表元素非常多,调用remove(Object)去删除一个元素的效率肯定有影响,一个简单测试,插入100万数据,随机删除1000个元素:
final List<Integer> list = new LinkedList<Integer>();
final int count = 1000000;
for (int i = 0; i < count; i++) {
list.add(i);
}
final Random rand=new Random();
long start=System.nanoTime();
for(int i=0;i<1000;i++){
//这里要强制转型为Integer,否则调用的是remove(int)
list.remove((Integer)rand.nextInt(count));
}
System.out.println((System.nanoTime()-start)/Math.pow(10, 9));
在我的机器上耗时近9.5秒,删除1000个元素耗时9.5秒,是不是很恐怖?注意到上面的注释,产生的随机数强制转为Integer对象,否则调用的是
remove(int)方法,而非remove(Object)。如果我们调用remove(int)根据索引来删除:
for(int i=0;i<1000;i++){
list.remove(rand.nextInt(list.size()-1));
}
随机数范围要递减,防止数组越界,换成remove(int)效率提高不少,但是仍然需要2.2秒左右(包括了随机数产生开销)。这是因为
remove(int)的实现很有技巧,它首先判断索引位置在链表的前半部分还是后半部分,如果是前半部分则从head往前查找,如果在后半部分,则从
head往后查找(LinkedList的实现是一个环):
Entry<E> e = header;
if (index < (size >> 1)) {
//前一半,往前找
for (int i = 0; i <= index; i++)
e = e.next;
} else {
//后一半,往后找
for (int i = size; i > index; i--)
e = e.previous;
}
最坏情况下要删除的节点在中点左右,查找的次数仍然达到n/2次,但是注意到这里没有比较的开销,并且比remove(Object)最坏情况下n次查找还是好很多。
题外,ArrayList比LinkedList更不适合随机增删的原因是多了一个数组移动的动作,假设你删除的元素在m,那么除了要查找m次之外,还需要往前移动n-m-1个元素。
分享到:
相关推荐
本文将详细讲解如何使用Java中的LinkedList类来模拟这两种数据结构,并实现其基本操作。 堆栈(Stack)是一种后进先出(LIFO, Last In First Out)的数据结构,它遵循“先进后出”的原则。常见的堆栈操作有压栈...
本文将深入探讨LinkedList的原理和使用方法。 首先,LinkedList是一个双向链表,意味着每个元素(节点)都有指向前后元素的引用。这与ArrayList不同,ArrayList是基于数组实现的,对元素的添加和删除操作在数组末尾...
在Java编程语言中,LinkedList是一个实现List接口的类,它以双向链表的形式存储元素。这个数据结构允许我们在列表的任何位置进行插入和删除操作,具有O(1)的时间复杂度,这使得LinkedList在需要频繁进行这些操作时比...
总结来说,LinkedList是Java中一个灵活且功能丰富的数据结构,通过合理利用其方法,我们可以构建出栈和队列,满足不同场景下的需求。不过,在追求性能时,应考虑使用专门为此设计的类。在阅读和分析给定的Queue.java...
实现一个链表LinkedList,要求使用链表机构实现,并提供相应的add(Object o),remove(Object o)这两个方法.
更多的情况下我们使用 ArrayList 访问列表中的随机元素更加高效,但以下几种情况 LinkedList 提供了更高效的方法。 在列表开头添加元素: ```java import java.util.LinkedList; public class RunoobTest { ...
在Java编程中,LinkedList是一个非常重要的数据结构,它实现了List接口,允许我们在列表的任何位置进行插入和删除操作。LinkedList内部使用双向链表实现,因此它的遍历速度比ArrayList快,但随机访问性能较差。本...
在Java中,我们可以利用LinkedList的addFirst()方法来模拟压栈操作,将新元素添加到链表头部,使用removeFirst()方法来模拟弹栈操作,删除并返回链表头部的元素。以下是一个简单的栈实现: ```java public class ...
Java LinkedList集合是Java集合框架中的一种重要实现,基于链表数据结构,提供了许多有用的方法来操作集合元素。下面对Java LinkedList集合的功能进行详细介绍。 LinkedList集合的基本操作 LinkedList集合继承自...
在Java中,我们可以使用LinkedList的`push()`方法来模拟栈的压栈操作,即向栈顶添加元素;使用`pop()`方法来模拟栈的弹栈操作,即移除并返回栈顶元素。为了确保线程安全,我们需要在多线程环境下对这些操作进行同步...
通过上述介绍,我们可以了解到`LinkedList`在Java中的基本使用方法,包括如何增加、删除和获取元素,以及如何使用`ListIterator`和`Iterator`来进行遍历。对于开发人员来说,了解这些基础知识是非常重要的,它可以...
Java中的LinkedList是一个实现双向链表的数据结构,它继承自AbstractList接口,并实现...对于初学者,理解LinkedList的底层工作原理和正确使用其方法是至关重要的,这样才能避免常见的编程错误并有效利用这种数据结构。
在LinkedList中,添加元素可以使用add()方法,该方法可以在中间添加元素,也可以在集合的头部或尾部添加元素。例如: * `add("H")`:在中间添加元素 * `addFirst("F")`:在集合最前面添加元素 * `addLast("L")`:在...
"Java集合框架LinkedList使用方法" Java集合框架中的LinkedList是一个双向链表,具有链表底层实现的特点,查询速度慢,增删速度快。LinkedList是Java集合框架中的一个重要组件,广泛应用于各种软件系统中。本文将...
**Java系列LinkedList** 链表是一种基础数据结构,它在计算机科学中扮演着重要角色,特别是在Java编程中。链表不按照线性顺序存储数据,而是每个...了解`LinkedList`的工作原理和用法对于优化Java程序性能至关重要。
### LinkedList的用法详解 #### 一、简介 在Java集合框架中,`LinkedList`类是一种基于链表实现的线性数据结构,继承自`AbstractSequentialList`抽象类,并实现了`List`接口与`Deque`接口。由于其内部是通过双向...
Java编程语言中的`Map`, `List`, `ArrayList` 和 `LinkedList` 是四个核心的数据结构,它们在实际开发中被广泛使用。了解它们的源码对于深入理解Java集合框架的内部工作原理至关重要,尤其是对初学者而言,这有助于...
这个简单的示例展示了LinkedList的基本用法,但LinkedList还提供了许多其他方法,如`peek`(查看但不移除第一个或最后一个元素)、`offer`(在列表的开头或末尾添加元素,返回是否成功)等,这些方法在实际开发中...
Java LinkedList 的实现原理图文详解 Java LinkedList 是一个通过双向链表实现的 List 和 Deque 接口,它允许插入所有元素,包括 null,...通过了解 LinkedList 的实现原理,我们可以更好地理解和使用 LinkedList。