目录
一:ArrayList的缺陷
二:链表
2.1 :链表的概念及结构
2.2:链表的实现
三:LinkedList模拟实现
四:总结
一:ArrayList的缺陷
由于其底层是⼀段连续空间,当在ArrayList任意位置插⼊或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率⽐较低,因此ArrayList不适合做任意位置插⼊和删除⽐较多的场景。因此:java集合中⼜引⼊了LinkedList,即链表结构。
二:链表
2.1 :链表的概念及结构
链表是⼀种物理存储结构上⾮连续存储结构,数据元素的逻辑顺序是通过链表中的引⽤链接次序实现的 。
白话一点:
链表不要求连续的内存空间(离散的结构)
元素和元素之间,内存是不连续的,并且这些元素的空间是没啥规律的(顺序上没有要求,内存上也没有要求)
如何知道链表中包含哪些元素、如何遍历链表中所以元素?
此处就把每个节点上面都引入一个引用变量,称为next
使用直观引用保存下一个元素对应的内存空间。
就是在a中存放一个next如何存放b的地址,以此类推,到d中就存放一个null,后面没有元素了。
链表的特点:
1.链表的元素在离散的内存空间上
2.每个元素中记录下一个元素地址(引用)
需要知道第一个元素是谁,后续整个链表就能拿到了
实际中链表的结构⾮常多样,以下情况组合起来就有8种链表结构:
1:单向或者双向
单链表只能指知道下一个,不知道上一个
双向链表,每个节点包含两个引用,prev、next(前一个元素地址、后一个元素地址)功能多了,但是消耗的空间更多了
2.带头或者不带头(是否带有“傀儡节点”)
头节点:应该是链表第一个节点
傀儡节点:这个节点不存储数据,而是占位置,来简化后续代码
其实带头也有傀儡节点,只是不显示,如图带头,其实head的next指向傀儡节点,但是傀儡节点不存储数据,那么head的next就指向d1
3.循环或者⾮循环
重点关注:
⽆头单向⾮循环链表:结构简单,⼀般不会单独⽤来存数据。实际中更多是作为其他数据结构的⼦结构,如哈希桶、图的邻接表等等。另外这种结构在笔试⾯试中出现很多。
⽆头双向链表:在Java的集合框架库中LinkedList底层实现就是⽆头双向循环链表
在java标准库中,LinkedList就是现成的实现,都是实现List的接口。
2.2:链表的实现
package linkedlist; import java.util.LinkedList; public class Test1 {public static void main(String[] args) {LinkedListlist = new LinkedList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(0,5);list.addFirst(6);System.out.println(list);//[6, 5, 1, 2, 3, 4]list.remove(2);// 删除索引为2的元素 1 System.out.println(list);//[6, 5, 2, 3, 4]list.remove(Integer.valueOf(2));//删除值为2的元素 2 System.out.println(list);//[6, 5, 3, 4]//头删list.removeFirst();System.out.println(list);//[5, 3, 4]//尾删list.removeLast();System.out.println(list);//[5, 3]list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);//通过get方法获取元素System.out.println(list.get(2));//3//通过set方法修改元素list.set(2, 10);System.out.println(list);//[1, 2, 10, 4, 1, 2, 3, 4, 5]//通过contians方法判断是否包含元素list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);System.out.println(list.contains(3));//trueSystem.out.println(list.contains(6));//false} }
三:LinkedList模拟实现
以下代码模拟实现都有注释,如果有不理解的可以评论O~~~
package linkedlist; //表示链表的节点 class Node {// 节点保存值public String value;// 指向下一个节点public Node next;public Node(String value) {this.value = value;this.next = null;} } public class MyLinkedList {//把链表的头节点表示出来,此时整个链表就能获取了//此时不包含傀儡节点,head==null的时候表示空链表private Node head=null;//不像顺序表使用size表示区间的长度,链表使用length表示链表的长度//但也可以使用size表示个数。//插入元素//1:尾插(先找到尾巴,再插入)public void addlast(String value){//如果链表为空if(head==null){//新建一个节点Node newNode=new Node(value);//把新节点放到头部head=newNode;return; }//先找道尾巴,把新的节点加道尾巴后面//先创建一个头节点Node tail=head;for(;tail.next!=null;tail=tail.next){if(tail.next==null){break;}}//找到尾巴//新建一个节点Node newNode=new Node(value);tail.next=newNode;newNode.next=null;}//2:头插(public void addfirst(String value) {//新建一个节点Node newNode = new Node(value);//把新节点放到头部//1:就要将newNode的next指向head指向的节点newNode.next = head;//2:然后我们的head在指向(新节点)newNodehead = newNode;}//3:指定位置插入//在链表中没有下标的概念//链表需要遍历,找位置。//但是java标准库,LinkedList同样引入下标这个概念,使用List统一作为ArrayList和LinkedList的接口public void add(int index,String value) {//先判断index是否合法if (index < 0 || index > size()) {//index==size等于尾插,没必要判断throw new IndexOutOfBoundsException("Index is out of bounds");}//针对头插出现的情况if(index==0){addfirst(value);return; }//根据当前value值,创建新的节点Node cur = new Node(value);//找到index位置的前一个节点//由于当前链表是单向链表,每个节点稚只能找到next节点//需要修改前一个节点next的值,让它指向当前节点//插入新节点,需要找到index-1位置的节点Node prev = head;for(int i=0;i= size()){throw new IndexOutOfBoundsException("Index is out of bounds");}//特殊处理index为0的情况if(index==0){head=head.next;return; }//找到被删除元素前一个节点位置Node prev=head;for(int i=0;i
关于clear:
一旦head==null,此时1就没有无人指向
1这个节点,就会被GC给释放掉了
1被释放之后,2就没有人指向了,2也会被GC释放,后面也差不多,依此类推。
四:总结
本篇博客讲述了链表的概念以及使用方法,最后模拟实现了链表。
如果有不明白的可以评论哦。




