List子接口LinkedList
LinkedList引入
ArrayList : 底层是数组,通过下标就能查询,它的查询比较快,但是删除比较慢 LinkedList:底层是双向链表,要一个一个的遍历,没有下标,所以数据比较多的时候查询比较 慢,但是它比较适合添加和删除操作。
对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末 元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。
LinkeList前置基础知识
单项链表
element:用来存放元素
next:用来指向下一个节点 通过每个节点的指针指向下一个节点从而连接起来的结构,最后一个节点的next指向null
单向循环链表
在单向链表的最后一个节点的next会指向头节点,而不是指向null,这样存成一个环。
双向链表
element:用来存放元素
pre:用来指向上一个节点
next:用来指向下一个节点 双向链表包含两个指针,pre指向前一个节点, next 指向后一个指针,但是第一个节点head(头节点) 的pre指向null,最后一个节点的tail指向null。
双向循环链表
lement、pre、next 跟前面的一样
第一个节点的pre指向最后一个节点,最后一个节点的next指向第一个节点,也形成一个“环”。
LinkedList常用方法
方法名 | 说明 |
---|---|
void addFirst(Object o) | 在链表的头部添加元素 |
void addLast(Object o) | 在链表的尾部添加元素 |
Object getFirst() | 获取链表中的第一个元素 |
Object getLast() | 获取链表中的最后一个元素 |
Object removeFirst | 删除中的第一个元素 |
Object removeLast() | 删除链表中的最后一个元素 |
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add("aaa");//添加到数组
linkedList.add("bbb");
linkedList.add("ccc");
linkedList.add("ddd");
linkedList.add("eee");
System.out.println(linkedList);//打印出添加后的数组
//linkedList.clear();
linkedList.offerFirst("eee");//在首位添加元素
System.out.println(linkedList);
linkedList.addFirst("qqq");//继续在首位添加元素
System.out.println(linkedList);
linkedList.removeFirst();//移除首位元素
System.out.println(linkedList);
linkedList.pollFirst();
//方法检索并删除此列表的第一个元素,如果此列表为空,则返回 null
System.out.println(linkedList);
}
链表的原理及实现
什么是链表?
链表是由n个子节点组合起来的一种线性数据结构 链表的特性:
- 链表是以节点(Node)的方式来存储,所以又叫链式存储
- 节点可以连续存储,也可以不连续存储
- 节点的逻辑顺序与物理顺序可以不一致
- 链表可以扩充
单链表
两个部分:
- data域:数据域,用来存储元素数据
- next域:用于指向下一节点
单链表的操作
单链表的所有操作都是从head开始,head本身不存储元素,其next指向第一个节点,然后顺着next链表 进行一步步操作。其尾部节点的next指向为空,这也是判断尾部节点的依据。
插入节点
向单链表中插入一个新节点,可以通过调整两次next指向来完成。
x为新节点,将其next指向A2,再将A1的next指向X即可。
如果是从尾节点插入,直接将尾节点的next指向新节点就可以了。
删除操作
从单链表中删除一节点,通过修改next指向来实现。
将A1的next 指向为A2,这样就能删除 X, X的内存空间会自动被垃圾回收。
代码实现
public class Node {
public String data;
public Node next;
public Node(String data) {
this.data = data;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"data='" + data + '\'' +
", next=" + next +
'}';
}
}
private Node head = new Node(null);//得有head节点,是单链表的开始
//添加节点
public void add(Node node){
Node temp = head;//temp存储头节点
while (true) {//判断node是否为空
if (temp.next == null){
temp.next = node;
break;
}
temp = temp.next;
}
}
singleLinkedList.add(aaa);
singleLinkedList.add(bbb);
//指定节点插入元素
public void addOfOrder(Node node, String name) {
Node temp = head;
while (true) {
if (temp.next == null){
temp.next = node;
break;
}else if (name == temp.getData()) {
node.next = temp.next;
temp.next = node;
break;
}
temp = temp.next;
}
}
singleLinkedList.addOfOrder(eee,"bbb");
//查找获取数据
public Node get(String key) {
if (head.next == null) {
return null;
}
Node temp = head.next;
while (temp != null){
if (temp.data == key) {
return temp;
}
temp = temp.next;
}
return null;
}
//移除数据
public void remove(Node node) {
Node temp = head.next;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.data == node.data) {
temp.next = temp.next.next;//断开连接
break;
}
temp = temp.next;
}
}
//修改叫这个node节点的名字name
public void update(Node node, String name) {
Node temp = head.next;//这句话一般都不变,都要用
while (true) {
if (temp == null) {
break;
}
if (temp.data == name) {
temp.data = node.data;
break;
}
temp = temp.next;
}
}
//测试
public static void main(String[] args) {
Node aaa = new Node("aaa");
Node bbb = new Node("bbb");
Node ccc = new Node("ccc");
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.add(aaa);
singleLinkedList.add(bbb);
singleLinkedList.add(ccc);
singleLinkedList.print();
System.out.println("=====================");
Node eee = new Node("eee");
singleLinkedList.addOfOrder(eee,"bbb");
singleLinkedList.print();
System.out.println("=====================");
System.out.println(singleLinkedList.get("eee"));
System.out.println(singleLinkedList.get("qqq"));
System.out.println("=====================");
singleLinkedList.remove(ccc);
singleLinkedList.print();
System.out.println("=====================");
singleLinkedList.update(new Node("www"), "eee");
singleLinkedList.print();
双向链表
双向链表其节点由三部分构成:
-
prev域:用于指向上一节点
-
data域:数据域,用来存储元素数据
-
next域:用于指向下一节点
双向链表的操作
双向链表的操作可以从两端开始,从第一个节点通过next指向可以一步步操作到尾部,从最后一个节点 通过prev指向可以一步步操作到头部。
插入节点
像双向链表中插入一个节点,需要调整两次 pre指向 和 两次 next 指向。
X为新节点,将A1的next指向X,将x的next指向A2,将A2的pre指向X,将X 的pre指向A1。
删除节点
从双向链表中删除一个节点,需要调整一次 pre 指向 和 一次 next 指向就可以了。
将A1的 next指向 A2, 将A2的pre 指向 A1。
代码实现
public class DoubleNode {
public final int key;
public String value;
public DoubleNode pre;
public DoubleNode next;
public DoubleNode(int key, String value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "DoubleNode{" +
"key=" + key +
", value='" + value + '\'' +
'}';
}
}
public class DoubleLinkedList {
public DoubleNode first = null; //双链表的头部,也就是第一个节点
public DoubleNode last = null; //双链表的尾部,也就是最后一个节点
/**
* @Description //TODO 从尾部添加节点
*/
public DoubleLinkedList addToTail(DoubleNode node) {
if (last == null) {
first = node;
} else {
last.next = node;
node.pre = last;
}
last = node;
return this;
}
/**
* @Description //TODO 从头部开始添加
*/
public DoubleLinkedList addToHead(DoubleNode node) {
if (first == null) {
last = node;
} else {
node.next = first;
first.pre = node;
}
first = node;
return this;
}
/**
* @Description //TODO 按照key key顺序添加
*/
public DoubleLinkedList addOfOrder(DoubleNode node) {
if (first == null) {
first = node;
last = node;
return this;
}
//node 比头节点小,将 node 设为头肩
if (first.key > node.key) {
first.pre = node;
node.next = first;
first = node;
return this;
}
//node 比尾节点大,将node变为 尾节点
if (node.key > last.key) {
last.next = node;
node.pre = last;
last = node;
return this;
}
DoubleNode temp = first.next;
while (true) {
if (temp.key > node.key) {
node.next = temp;
node.pre = temp.pre;
temp.pre.next = node;
temp.pre = node;
break;
}
temp = temp.next;
}
return this;
}
/**
* @Description //TODO 获取节点
*/
public DoubleNode get(int key) {
if (first == null) {
return null;
}
DoubleNode temp = first;
while (temp != null) {
if (temp.key == key) {
return temp;
}
temp = temp.next;
}
return null;
}
/**
* @Description //TODO 删除
*/
public DoubleLinkedList remove(DoubleNode node) {
if (first == null) {
return this;
}
//如果删除的是头节点
if (first == node) {
first.next.pre = null;
first = first.next;
return this;
}
//如果删除的是为节点
if (last == node) {
last.pre.next = null;
last = last.pre;
return this;
}
DoubleNode temp = first.next;
while (temp != null) {
if (temp.key == node.key) {
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
break;
}
temp = temp.next;
}
return this;
}
/**
* @Description //TODO
*/
public DoubleLinkedList update(DoubleNode node) {
if (first == null) {
return this;
}
DoubleNode temp = first;
while (temp != null) {
if (temp.key == node.key) {
temp.value = node.value;
break;
}
temp = temp.next;
}
return this;
}
public void print() {
if (first == null) {
return;
}
DoubleNode temp = first;
while (temp != null) {
System.out.println(temp);
temp = temp.next;
}
}
public static void main(String[] args) {
DoubleNode aaa = new DoubleNode(1, "aaa");
DoubleNode bbb = new DoubleNode(2, "bbb");
DoubleNode ccc = new DoubleNode(6, "ccc");
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.addToTail(aaa);
doubleLinkedList.addToTail(bbb);
doubleLinkedList.addToTail(ccc);
doubleLinkedList.print();
System.out.println("===================");
DoubleNode ddd = new DoubleNode(4, "ddd");
doubleLinkedList.addToHead(ddd);
doubleLinkedList.print();
System.out.println("===================");
DoubleNode eee = new DoubleNode(3, "eee");
doubleLinkedList.addOfOrder(eee);
doubleLinkedList.print();
System.out.println("===================");
System.out.println(doubleLinkedList.get(7));
System.out.println("===================");
doubleLinkedList.remove(new DoubleNode(1, "aaa"));
doubleLinkedList.print();
System.out.println("===================");
doubleLinkedList.update(new DoubleNode(2, "herb"));
doubleLinkedList.print();
}
}
源码分析
LinkedList特性
- LinkedList 集合底层实现的数据结构为双向链表
- LinkedList 集合中元素允许为 null
- LinkedList 允许存入重复的数据
- LinkedList 中元素存放顺序为存入顺序。
- LinkedList 是非线程安全的
- 如果在频繁的插入,或者删除数据时,就用linkedList性能会更好。
继承结果及层次关系
类的属性
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
//LinkedList 中的节点个数
transient int size = 0;
//LinkedList 链表的头节点
transient Node<E> first;
//LinkedList 链表的 尾节点
transient Node<E> last;
}
构造方法
- 空构造器
//空构造器生成一个空链表 ==> frist = last = null
public LinkedList() {
}
- 有参构造器
// 将集合 c 中的 各个元素构建成 LinkedList 链表
public LinkedList(Collection<? extends E> c) {
//调用无参构造器
this();
//添加集合中的所有元素
addAll(c);
}
内部类(Node)
private static class Node<E> {
E item; //节点的值
Node<E> next; //用于指向下一节点(后继)
Node<E> prev;//用于指向上一节点(前驱)
//构造函数,赋值 前驱后继
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
说明:内部类 Node 就是实际的节点,用于存放实际元素的地方
add()
public boolean add(E e) {
//将元素添加到链表的末尾
linkLast(e);
return true;
}
说明:add函数用于向LinkedList中添加一个元素,并且添加到链表尾部。具体添加到尾部的逻辑是由 linkLast函数完成的。
void linkLast(E e) {
//临时节点 l(L的小写)保存last,也就是 l 指向最后一个节点
final Node<E> l = last;
//将e(值)封装为节点,并且e.perv指向了最后一个节点
final Node<E> newNode = new Node<>(l, e, null);
//newNode 成为最后一个节点
last = newNode;
// 判断之前的链表是否为空,如果为空,那么 newNode 就成为了头节点
if (l == null)
first = newNode;
else //否则将之前的尾节点 的 next 指向 newNode(新节点)
l.next = newNode;
size++;///添加了节点,size要自增
modCount++;
}
的末尾
linkLast(e);
return true;
}
说明:add函数用于向LinkedList中添加一个元素,并且添加到链表尾部。具体添加到尾部的逻辑是由 linkLast函数完成的。
void linkLast(E e) {
//临时节点 l(L的小写)保存last,也就是 l 指向最后一个节点
final Node<E> l = last;
//将e(值)封装为节点,并且e.perv指向了最后一个节点
final Node<E> newNode = new Node<>(l, e, null);
//newNode 成为最后一个节点
last = newNode;
// 判断之前的链表是否为空,如果为空,那么 newNode 就成为了头节点
if (l == null)
first = newNode;
else //否则将之前的尾节点 的 next 指向 newNode(新节点)
l.next = newNode;
size++;///添加了节点,size要自增
modCount++;
}