首页 > 编程知识 正文

linklist类型变量,linkedlist数据结构

时间:2023-05-04 00:08:37 阅读:44348 作者:2442

本文基于JDK1.8。

读了这篇文章,你会学到以下内容。

LinkedList可以插入null值的原因LinkedList可以接受重复原因LinkedList插入快和查询慢的原因

上一篇文章讨论了ArrayList,但本文主要讨论链接列表的实现。 这两个集合类在我们学习和工作中很常用。 在某种程度上,随便搜索一下,出现的是两个不同点。 我们知道ArrayList基于动态数组,LinkedList基于链表。 按层次分析链接列表为什么是插入快,查询慢。 但在此之前,让我们先看看什么是链表。 让我们来看看链表的定义。 链表是物理存储单元上的非连续、非顺序存储结构,数据元素的逻辑顺序通过链表中指针的链接顺序来实现。 链表由一组节点组成,链表中的每个元素都称为节点,节点可以在运行时动态生成。 每个节点有两个部分:存储数据元素的数据域和存储以下节点地址的指针域。

虽然上面可能很复杂,但让我们画个图来理解一下:

在上图中,每个节点有两个部分。 也就是说,第一部分用于保存自己的数据,第二部分保存指向下一个节点的指针。

双向链表双向链表和单向链表的最大区别是每个节点保留指向下一个节点的指针,同时也保留指向上一个节点的指针。

单向链表中只有后者的节点指针。 节点被删除并移动时,必须临时保存上一个节点,删除时必须连接上一个节点和下一个节点。 由于只保留一个比双向链表早的节点,并且只在删除时临时保存,所以与单向链表相比可以节约资源,但增加了操作的复杂性。

双向链表中有前后两个节点指针,可以跟踪指针以方便节点的删除和移动。 执行删除操作时,只需要连接索引节点前后两个节点,但与单向链表相比需要额外的资源。

总之,双向链表是在空间里改变时间。 接下来要分析的LinkedList基于此。

节点类这里的节点类是链接列表的静态内部类,也是上述双向链表节点的具体实现。

私有静态类节点{ e item; //当前节点保存的数据NodeE next; //对下一个节点的引用NodeE prev; //对上一个节点的引用node(nodeeprev,E element,NodeE next ) { this.item=element; this.next=next; this.prev=prev; }结构非常简单,如上所述,验证了每个节点不仅维护自身的数据,还维护前一个节点和后一个节点的引用。 用图表示,如下。

LinkedList成员变量很简单,主要包括:

//记录链表节点数的transient int size=0; 记录//链表的第一个节点transient NodeE first记录链表的最后一个节点transient NodeE last; 由于链接的列表是从Deque继承的,因此必须支持一系列操作,如removeFirst和removeLast,因此必须记录链表的一致节点。

要添加元素,需要以下代码:

publicstaticvoidmain (字符串[ ] args ) liststringlist=newlinkedliststring ); //1list.add(Hello ); //2 )让我们逐步分析上述两处代码。 首先是第一个地方。 让我们看看源代码:

可以看到公共链接列表初始化时没有特殊操作。 接下来,我们来看第二个源代码。

调用publicbooleanadd(e ) {//Linklast ) }; 返回真; }voidLinklast(e )//首先获取最后一个节点final NodeE l=last; //这里,上一个节点指向l,是包含要保存的数据的数据,下一个节点为nullfinalnodeenewnode=new node (l,e,null ); //然后将此新节点另存为链表中的最后一个节点的last=newNode; //如果第一个最后一个节点为null,则是第一次添加if(l==null ) first=newNode )。 else //否则,在启动最后一个节点后立即引用下一个节点,新创建的节点l.next=newNode; size; 模具计数; }其实上面的注释已经写得很清楚了,现在总结一下:

保存当前链接列表中的最后一个节点以创建新节点

节点,并将要添加的数据赋值给新节点的e属性,那么这个时候新节点的上一个节点就应该是一开始保存的最后一个节点,即l接着将LinkedList的last引用指向新创建的节点,last保存的是链表的最后一个节点,因为这个时候新的节点成为了最后一个节点,所以需要重新指向。我们在第一次添加时,l肯定是null,这个时候链表中只有一个节点,那么就是新创建出来的节点,所以同时将first引用指向新节点(如果不是第一次添加,那么则l是不为空的,则需要将l的下一个节点的引用指向新节点)。

第一次添加后,链表结构:

第二次添加后,链表结构:

查看元素

这里以最简单的get(int index)方法为例:

public E get(int index) {//参数合法检验checkElementIndex(index);//node方法返回节点,获取该节点保存的值并返回 return node(index).item;}Node<E> node(int index) {//size >> 1 相当于size / 2 位运算比较高效,不需要转换为10进制计算//index如果小于链表大小的一半,则从头遍历 if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { //index如果大于等于链表大小的一半,则从尾部遍历 Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; }}

这段代码就体现出了双向链表的好处了。双向链表增加了一点点的空间消耗(每个Node里面还要维护它的前置Node的引用,相对于单链表来说空间消耗增加),同时也增加了一定的编程复杂度,却大大提升了效率。
举例:假设LinkedList中有10000个元素,如果我要找到第10000的元素,则直接从尾部开始遍历,只需要一次就能找到想要的元素。但最坏情况下如果查询第5000个元素,那么效率大打折扣。

删除元素

看完查看元素后,我们看一下如何删除一个元素,这里以按下标删除举个例子好了,下面先用图示解释一下如何删除元素。
假设现在链表中存在三个节点:

现在需要删除中间的节点,即将第一个节点的next引用指向第三个节点,再将最后一个节点的pre引用指向第一个节点:
最终结果:

那么接下来看看应用到LinkedList具体是怎么实现的:

public E remove(int index) { checkElementIndex(index); //node方法和查看元素时相同 return unlink(node(index));}E unlink(Node<E> x) {//分别获取当前要删除节点的值、前置节点、后置节点final E element = x.item;final Node<E> next = x.next;final Node<E> prev = x.prev;//前置节点为null,说明当前节点为首节点if (prev == null) { first = next;} else {//前置节点不为null,将前置节点的next指向后置节点 prev.next = next; //1 x.prev = null;}//后置节点为null,说明当前节点为尾节点if (next == null) { last = prev;} else {//后置节点不为null,将后置节点的prev指向前置节点 next.prev = prev; //2 x.next = null;}//3x.item = null;size--;modCount++;return element;}

这里注意一点:上面源码中的1、2、3步骤都设置为了null,目的是为了GC。

插入元素

插入元素其实和上面讲的几种是一个道理,如果读者理解了上面的逻辑,插入元素也就能想通怎么回事了。

LinkedList和ArrayList的区别

这个问题不管是在平时面向搜索编程还是在基础面试过程中都算是老生常谈了,在这里我们逐个分析一下这两个的优缺点:

插入速度比较。网上大部分说LinkedList插入比ArrayLst快。这种说法是不准确的。LinkedList做插入、删除的时候,慢在寻址,快在只需要改变前后Node的引用地址,而ArrayList做插入、删除的时候,慢在数组元素的批量copy,快在寻址。
所以如果待插入的元素位置在数据结构的前半段尤其是非常靠前时,ArrayList需要拷贝大量的元素,势必LinkedList会更快;如果带插入元素位置在数据结构后半段尤其是非常靠后,ArrayList需要拷贝的元素个数会越来越少,所以速度也会提升,甚至超过LinkedList。ArrayLst基于动态数组,所以内存上是连续的,而LinkedList基于链表,内存上不需要保持连续。一般遍历LinkedList时最好不要使用普通for循环,而是使用迭代器代替。

我们在实际工作中,还是要根据实际情况来确定选用哪种数据结构存储数据,最好是根据需求,经过理论支撑和实际测试,最终选择合适的数据结构。

总结

本篇文章介绍了LinkedList增加元素、查看元素、移除元素、插入元素等,以图示和源码结合的方式掌握了LinkedList实现原理,其内部实现就是一个双向链表,通过以空间换时间的方式提高查询的效率。

微信搜索Java成神之路或扫描下方二维码,发现更多Java有趣知识,让我们一起成长!

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。