☰
JAVA-LinkedList代码实现与使用总结
2026/10/4 4:28:09 网站建设 项目流程

LinkedList 详解:核心特性、常用接口与总结(附图解)

本文聚焦 Java 集合框架中的LinkedList,从「什么是链表」讲起,用图解方式直观展示 LinkedList 的底层结构,系统梳理所有常用接口的作用与返回值,最后给出模拟代码实现并且对比 ArrayList 给出整体总结。适合作为学习笔记或面试速查。


目录

  • 一、从 ArrayList 的缺陷说起
  • 二、链表:LinkedList 的底层原理(图解)
  • 三、LinkedList 简介
  • 四、LinkedList 的构造方式
  • 五、LinkedList 常用接口(作用 + 返回值)
  • 六、LinkedList 的三种遍历方式
  • 七、ArrayList 和 LinkedList 的区别
  • 八、模拟代码实现
  • 九、总结

一、从 ArrayList 的缺陷说起

ArrayList 底层使用数组存储元素。由于其底层是一段连续空间,在任意位置插入或删除元素时,需要将后序元素整体往前或往后搬移,时间复杂度为O(n),效率较低。

因此:ArrayList 不适合做任意位置插入和删除较多的场景。为此,Java 集合中又引入了LinkedList,即链表结构。


二、链表:LinkedList 的底层原理(图解)

2.1 什么是链表

链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的——元素存储在一个个独立的节点(Node)中,节点之间靠引用串起来。

与顺序表对比着看更直观:

一句话总结:顺序表是「物理连续、逻辑也连续」;链表是「物理不连续、逻辑连续」。

2.2 链表的 8 种结构

实际中链表的结构非常多样,以下三种情况组合起来共有2 × 2 × 2 = 8 种链表结构:


虽然结构很多,但重点掌握两种:

  • 无头单向非循环链表:结构简单,一般不会单独用来存数据,实际中更多是作为其他数据结构的子结构(如哈希桶、图的邻接表),并且是笔试面试的高频考点;
  • 无头双向链表:Java 集合框架中 LinkedList 的底层实现就是无头双向循环链表。

2.3 LinkedList 的底层结构:无头双向循环链表

每个节点(Node)包含三个部分:prev(前驱引用)| item(数据)| next(后继引用),同时额外维护first和last两个引用。由于链表没有将元素存储在连续的空间中,因此在任意位置插入或删除元素时,不需要搬移元素,只需修改引用的指向,效率较高。


三、LinkedList 简介

在集合框架中,LinkedList 也实现了List 接口。它的核心特性如下:

特性说明
实现了 List 接口拥有 List 的全部通用方法
底层是双向链表元素存储在独立节点中,通过引用连接
不支持随机访问没有实现 RandomAccess 接口,按下标访问只能从头遍历,效率 O(n)
插入删除效率高任意位置插入和删除元素时效率比较高(改引用即可,时间复杂度为 O(1))¹
适合频繁插删的场景与 ArrayList 互补

¹ 精确地说:「改引用」这一步是 O(1),但定位到目标位置仍需 O(n) 遍历;不过相比 ArrayList 插入时还要整体搬移元素,LinkedList 依然优势明显。

此外,LinkedList 还实现了Deque(双端队列)接口,所以它既能当列表用,也能当栈、队列用:


四、LinkedList 的构造方式

publicstaticvoidmain(String[]args){// 1. 构造一个空的 LinkedListList<Integer>list1=newLinkedList<>();// 2. 使用其他集合构造 LinkedList(拷贝构造)List<String>list2=newjava.util.ArrayList<>();list2.add("JavaSE");list2.add("JavaWeb");list2.add("JavaEE");// list3 构造好之后,与 list2 中的元素一致List<String>list3=newLinkedList<>(list2);}
构造方法作用
LinkedList()构造一个空的LinkedList
LinkedList(Collection<? extends E> c)使用其他集合中的元素构造 LinkedList(拷贝构造)

注意:与 ArrayList 不同,LinkedList没有「指定初始容量」的构造方法——链表按需创建节点,不存在预分配容量的概念。


五、LinkedList 常用接口(作用 + 返回值)

5.1 List 通用接口

方法作用返回值
boolean add(E e)尾插(在链表末尾添加元素)添加成功返回true
void add(int index, E e)在index位置插入元素无返回值
E remove(int index)删除index位置的元素返回被删除的元素
boolean remove(Object o)删除第一次出现的指定元素删除成功返回true,不存在返回false
E get(int index)获取index位置的元素返回该位置的元素
E set(int index, E e)将index位置的元素设置为e返回被替换的旧元素
boolean contains(Object o)检测元素o是否存在存在返回true,否则返回false
int indexOf(Object o)从前往后找o第一次出现的位置找到返回下标,未找到返回 -1
int lastIndexOf(Object o)从后往前找o第一次出现的位置找到返回下标,未找到返回 -1
int size()获取链表中有效元素个数返回元素个数
void clear()清空链表无返回值
List<E> subList(int from, int to)用 list 中[from, to)之间的元素构造一个新的 List 返回返回子列表

5.2 LinkedList 特有的首尾操作(来自 Deque 双端队列接口)

这是 LinkedList 区别于 ArrayList 的最大亮点——头尾操作全部 O(1):

方法作用返回值
void addFirst(E e)头插,将元素插到链表头部无返回值
void addLast(E e)尾插,将元素插到链表尾部无返回值
E remove()删除第一个元素(内部调用的就是removeFirst())返回被删除的元素;链表为空时抛异常
E removeFirst()删除第一个元素返回被删除的元素;链表为空时抛异常
E removeLast()删除最后一个元素返回被删除的元素;链表为空时抛异常
E getFirst()获取第一个元素(不删除)返回首个元素;链表为空时抛异常
E getLast()获取最后一个元素(不删除)返回末尾元素;链表为空时抛异常
boolean offer(E e)尾插(等价add(e),队列风格)成功返回true
boolean offerFirst(E e)头插(队列风格)成功返回true
boolean offerLast(E e)尾插(队列风格)成功返回true
E poll()删除第一个元素(队列风格)返回被删除元素;链表为空返回null而不抛异常
E pollFirst()/E pollLast()删除首 / 尾元素同上,空链表返回null
E peek()获取第一个元素(不删除)空链表返回null,不抛异常
E peekFirst()/E peekLast()获取首 / 尾元素(不删除)空链表返回null
void push(E e)头插(栈风格,等价addFirst(e))无返回值
E pop()删除第一个元素(栈风格,等价removeFirst())返回被删除元素;空链表抛异常

⚠️两组方法的区别务必记牢:

  1. remove(int index)返回被删除的元素,remove(Object o)返回boolean——重载语义不同;
  2. 「抛异常组」(removeFirst/getFirst/pop…)与「返回特殊值组」(pollFirst/peekFirst/offer…)功能相同,区别仅在于链表为空时前者抛异常、后者返回null或false。

5.3 接口使用示例

publicstaticvoidmain(String[]args){LinkedList<Integer>list=newLinkedList<>();list.add(1);// add(elem): 表示尾插list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);System.out.println(list.size());System.out.println(list);// 在 index 位置插入元素 elem:在起始位置插入 0list.add(0,0);System.out.println(list);list.remove();// remove(): 删除第一个元素,内部调用的是 removeFirst()list.removeFirst();// removeFirst(): 删除第一个元素list.removeLast();// removeLast(): 删除最后一个元素list.remove(1);// remove(index): 删除 index 位置的元素System.out.println(list);// contains(elem): 检测 elem 元素是否存在,如果存在返回 true,否则返回 falseif(!list.contains(1)){list.add(0,1);}list.add(1);System.out.println(list);System.out.println(list.indexOf(1));// indexOf(elem): 从前往后找到第一个 1 的位置System.out.println(list.lastIndexOf(1));// lastIndexOf(elem): 从后往前找第一个 1 的位置intelem=list.get(0);// get(index): 获取指定位置元素list.set(0,100);// set(index, elem): 将 index 位置的元素设置为 elemSystem.out.println(list);// subList(from, to): 用 list 中 [from, to) 之间的元素构造一个新的 List 返回List<Integer>copy=list.subList(0,3);System.out.println(list);System.out.println(copy);list.clear();// 将 list 中元素清空System.out.println(list.size());}

六、LinkedList 的三种遍历方式

publicstaticvoidmain(String[]args){LinkedList<Integer>list=newLinkedList<>();list.add(1);// add(elem): 表示尾插list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);System.out.println(list.size());// 1. foreach 遍历for(inte:list){System.out.print(e+" ");}System.out.println();// 2. 使用迭代器遍历 --- 正向遍历ListIterator<Integer>it=list.listIterator();while(it.hasNext()){System.out.print(it.next()+" ");}System.out.println();// 3. 使用反向迭代器 --- 反向遍历ListIterator<Integer>rit=list.listIterator(list.size());while(rit.hasPrevious()){System.out.print(rit.previous()+" ");}System.out.println();}

💡 由于 LinkedList没有实现 RandomAccess 接口,用「for 循环 + 下标get(i)」遍历的代价是每次get都从头找,整体为O(n²),不推荐。推荐foreach或迭代器遍历。


七、ArrayList 和 LinkedList 的区别

对比维度ArrayListLinkedList
底层结构动态顺序表(连续数组)无头双向循环链表
随机访问支持(实现了 RandomAccess),get(i)为O(1)不支持,get(i)需从头遍历,为O(n)
任意位置插入 / 删除需整体搬移元素,O(n)只需修改引用,O(1)¹
首尾操作尾插均摊 O(1),头插 O(n)头插、尾插都是O(1)
内存占用连续空间,有一定冗余容量每个节点额外维护 prev / next 两个引用,占用更大
扩容需要申请新空间 + 拷贝数据无需扩容,按需创建节点
适用场景查询多、增删少任意位置增删多、查询少

¹ 同第三节的说明:定位节点本身是 O(n),此处指节点间的插入 / 删除操作。

一句话选型:以「查」为主选 ArrayList,以「增删」为主选 LinkedList。


八、模拟代码实现

packagelinkedlist;classNode{publicStringvalue;publicNodenext;publicNode(Stringv){value=v;}}publicclassMyLinkedList{privateNodehead=null;publicvoidaddFirst(Stringv){NodenewNode=newNode(v);//需要完成的两个步骤newNode.next=head;head=newNode;}publicvoidaddLast(Stringv){if(head==null){addFirst(v);return;}NodenewNode=newNode(v);Nodecur=head;while(cur.next!=null){cur=cur.next;}cur.next=newNode;}publicStringtoString(){StringBuilderstr=newStringBuilder();str.append("[");Nodecur=head;//打印的话要遍历所有 而不是走到最后就结束了while(cur!=null){str.append(cur.value);if(cur.next==null)break;str.append(",");cur=cur.next;}str.append("]");returnstr.toString();}publicintsize(){intsize=0;Nodecur=head;while(cur!=null){cur=cur.next;size++;}returnsize;}publicvoidadd(intindex,Stringv){if(index<0||index>size()){thrownewIndexOutOfBoundsException();}NodenewNode=newNode(v);Nodeprev=null;Nodecur=head;while(index-->0){prev=cur;cur=cur.next;}if(prev==null){addFirst(v);}else{prev.next=newNode;newNode.next=cur;}}publicbooleancontains(Stringv){if(size()==0)returnfalse;Nodecur=head;while(cur!=null){if(cur.value.equals(v))returntrue;cur=cur.next;}returnfalse;}publicvoidremove(intindex){if(index<0||index>size()){thrownewIndexOutOfBoundsException();}Nodeprev=null;Nodecur=head;for(inti=0;i<index;i++){prev=cur;cur=cur.next;}if(prev==null){head=head.next;return;}prev.next=cur.next;return;}publicvoidremove(Stringv){if(size()==0)return;Nodecur=head;Nodeprev=null;while(cur!=null){if(cur.value.equals(v)){if(prev==null){head=head.next;return;}prev.next=cur.next;return;}prev=cur;cur=cur.next;}}publicintindexOf(Stringv){Nodecur=head;intindex=0;while(cur!=null){if(cur.value.equals(v)){returnindex;}cur=cur.next;index++;}return-1;}publicvoidremoveAllKey(Stringkey){Nodeprev=null;Nodecur=head;while(cur!=null){if(cur.value.equals(key)){if(prev==null){head=head.next;cur=head;continue;}prev.next=cur.next;cur=prev.next;continue;}prev=cur;cur=cur.next;}}publicvoidclear(){//垃圾回收机制 只要没引用指向 就会自动释放内存head=null;}publicstaticvoidmain(String[]args){MyLinkedListlist=newMyLinkedList();list.addFirst("Hello");list.addFirst("World");list.addLast("Hello");list.addLast("Hello");list.addLast("World");list.add(2,"gogogo");list.add(0,"hel");System.out.println(list);list.removeAllKey("Hello");System.out.println(list);}}

九、总结

  1. LinkedList 本质:底层是无头双向循环链表的 List 实现,同时实现了List、Deque等接口;
  2. 核心特性:物理存储不连续、不支持随机访问(未实现 RandomAccess)、头尾插入删除效率极高(O(1))、无需扩容;
  3. 关键接口:add/remove/get/set/contains/indexOf/lastIndexOf/size/clear/subList,以及特有的一整套首尾操作(addFirst/addLast/removeFirst/removeLast/getFirst/getLast/poll/peek/push/pop),需牢记各自作用与返回值;
  4. 返回值易错点:
    • remove(int index)返回被删除元素,remove(Object o)返回 boolean;
    • removeFirst()/getFirst()/pop()空链表抛异常,pollFirst()/peekFirst()空链表返回 null;
  5. 遍历建议:用foreach或迭代器,不要用「for + get(i)」(O(n²));
  6. 与 ArrayList 的选型:查询多选 ArrayList,任意位置增删多选 LinkedList;两者是互补关系而非替代关系。

如果这篇文章对你有帮助,欢迎点赞 👍 + 收藏 ⭐ + 关注,后续会继续更新 Java 集合框架系列文章!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询