在上一篇文章中我们介绍了ArrayList使用方法,并进行了简单的模拟实现,但通过分析我们发现其不方便进行插入和删除操作,因为要移动数组元素,最坏情况下时间复杂度会达到O(n)效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。
一、什么是链表
概念:链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
注意: 链式结构是在逻辑上连续,但在物理上不一定连续。
实际中链表的结构非常多样,以下情况组合起来就有 8 种链表结构:
- 单向或者双向
- 带头或者不带头
3. 循环或者非循环
虽然有这么多的链表的结构,但是我们重点掌握两种 :
无头单向非循环链表 : 结构简单 ,一般不会单独用来存数据。实际中更多是作为 其他数据结构的子结构 ,如 哈希桶、图的邻接表等等。
无头双向链表 :在 Java 的集合框架库中 LinkedList 底层实现就是无头双向循环链表。
二、MyLinkedList实现
1、IList接口
java
public interface IList {
//头插法
void addFirst(int data);
//尾插法
void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
void addIndex(int index,int data);
//查找是否包含关键字key是否在单链表当中
boolean contains(int key);
//删除第一次出现关键字为key的节点
void remove(int key);
//删除所有值为key的节点
void removeAllKey(int key);
//得到单链表的长度
int size();
void clear();
void display();
}
2、MyLinkList实现
1、基础结构
java
public class MyLinkList implements IList{
//内部类
class Node{
public int val;
public Node next;//下一个结点
public Node head;//头结点
public Node(int val){
this.val=val;
}
}
}
2、插入数据
(1)头插法
java
public void addFirst(int data) {
Node node=new Node(data);
node.next=head;
head=node;
}
(2)尾插法
java
public void addLast(int data) {
Node node=new Node(data);
Node end=head;
while (end.next!=null){
end=end.next;
}
end.next=node;
}
(3)在指定位置插入一个元素
java
public void addIndex(int index, int data) {
if(index<0||index>size()){
System.out.println("位置不合法");
return;
}
if(index==0){
addFirst(data);
return;
}
if(index==size()){
addLast(data);
return;
}
Node node=new Node(data);
Node pre=getNode(index);
node.next=pre.next;
pre.next=node;
}
public Node getNode(int index){
Node node=head;
while (index-1!=0){
node=node.next;
index--;
}
return node;
}
注意:
一定要检查插入数据的合法性,避免程序运行出现问题
数据结构是一门十分严谨的学科,所以我们一定要注意好这些小细节
3、是否包含某一个元素
java
public boolean contains(int key) {
Node node=head;
while (node!=null){
if(node.val==key){
return true;
}
node=node.next;
}
return false;
}
4、删除指定数据
(1)删除第一个为key的元素
java
public void remove(int key) {
if(head.val==key){
head=head.next;
return;
}
int index=getindex(key);
Node node=head;
while (index-1!=0){
node=node.next;
index--;
}
node.next=node.next.next;
}
(2)删除所有为key的元素
java
public void removeAllKey(int key) {
if (head==null){
return;
}
Node node=head;
Node cur=head.next;
while (cur!=null){
if(cur.val==key){
node.next=cur.next;
cur=cur.next;
}else {
node=node.next;
cur=cur.next;
}
}
if(head.val==key){
head=head.next;
}
}
5、其它操作
java
//根据key返回它的前一个坐标
public int getindex(int key){
int count=0;
Node node=head;
while (node!=null){
if(node.val==key){
return count;
}
node=node.next;
count++;
}
return -1;
}
//求链表大小
public int size() {
Node node=head;
int count=0;
while (node!=null){
count++;
node=node.next;
}
return count;
}
//清空链表
public void clear() {
Node node=head;
while (node!=null){
Node next=node.next;
node.next=null;
node=next;
}
head=null;
}
//打印链表
public void display() {
Node node=head;
while (node!=null){
System.out.print(node.val+" ");
node=node.next;
}
}
三、LinkedList
1、LinkedList是什么
LinkedList的底层是双向链表结构 ( 链表后面介绍 ) ,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。
LinedList源码:
可以看出:
- LinkedList 实现了 List 接口
- LinkedList 的底层使用了双向链表
- LinkedList 没有实现 RandomAccess 接口,因此 LinkedList 不支持随机访问
- LinkedList 的任意位置插入和删除元素时效率比较高,时间复杂度为 O(1)
- LinkedList 比较适合任意位置插入的场景
2、LinkedList的使用
(1)构造
java
public static void main(String[] args) {
// 构造一个空的LinkedList
List<Integer> list1 = new LinkedList<>();
List<String> list2 = new java.util.ArrayList<>();
list2.add("JavaSE");
list2.add("JavaWeb");
list2.add("JavaEE");
// 使用ArrayList构造LinkedList
List<String> list3 = new LinkedList<>(list2);
}
其他常用方法介绍:
(2)遍历
java
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
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());
// foreach遍历
for (int e:list) {
System.out.print(e + " ");
}
System.out.println();
// 使用迭代器遍历---正向遍历
ListIterator<Integer> it = list.listIterator();
while(it.hasNext()){
System.out.print(it.next()+ " ");
}
System.out.println();
// 使用反向迭代器---反向遍历
ListIterator<Integer> rit = list.listIterator(list.size());
while (rit.hasPrevious()){
System.out.print(rit.previous() +" ");
}
System.out.println();
}
3、ArrayList和LinkedList****的区别
总结
链表的优点:
- 适合频繁进行任意位置插入的场景
缺点:
- 不支持随机访问,访问随机数据时,最坏情况下时间复杂度会达到O(n)
我们在实际应用中要对比着使用ArrayList和LinkedList,根据应用场景选择合适的数据结构,要记住,没有最好的数据结构,只有最合适的数据结构
那么本篇文章就到此为止了,如果觉得这篇文章对你有帮助的话,可以点一下关注和点赞来支持作者哦。作者还是一个萌新,如果有什么讲的不对的地方欢迎在评论区指出,希望能够和你们一起进步✊