039:LinkedList集合源码深度解析
1 LinkedList底层链表数据结构分析
课程内容:
1、LinkedList与ArrayList区别
2、数据结构之链表与数组区别
3、完全白话文分析LinkedList
4、纯手写LinkeList链表数据结构
算法的时间复杂度和空间复杂度
时间复杂度指的就是一个算法执行所耗费的时间;
空间复杂度指的就是一个算法所耗费的存储空间;
数组与链表数据结构区别
数组查询效率比较高(使用下标定位元素),但是增删效率比较低(多个元素移动);
链表查询效率比较低(范围查询),但是增删效率比较高(改变节点头尾指向);
2 手写简单版本LinkedList实现基本功能
public interface MayiktList<E> {
/**
* 集合大小
* @return
*/
int size();
/**
* 添加元素
* @param e
* @return
*/
boolean add(E e);
/**
* 使用下标查询元素
* @param index
* @return
*/
E get(int index);
/**
* 使用下标删除元素
* @param index
* @return
*/
public E remove(int index);
}
public class MayiktLinkedList<E> implements MayiktList<E> {
/**
* 集合大小
*/
transient int size = 0;
transient MayiktLinkedList.Node<E> first;
transient MayiktLinkedList.Node<E> last;
public int size() {
return size;
}
public boolean add(E e) {
linkLast(e);
return true;
}
public void linkLast(E e) {
// 获取当前最后的元素节点
final MayiktLinkedList.Node<E> l = last;
// 设置新增的node节点元素
final MayiktLinkedList.Node<E> newNode = new MayiktLinkedList.Node<E>(l, e, null);
// 将新增的节点设为最后节点
last = newNode;
if (l == null) {
// 说明当前链表第一次添加元素
first = newNode;
} else {
l.next = newNode;
}
size++;
}
public E get(int index) {
// 检查数组是否越界
checkElementIndex(index);
return (E) node(index).item;
}
MayiktLinkedList.Node<E> node(int index) {
// 折半查找
if (index < (size >> 1)) {
// 获取第一个节点
MayiktLinkedList.Node<E> x = first;
// 从0开始到当前下标位置
for (int i = 0; i < index; i++) {
// 获取下一个节点
x = x.next;
}
return x;
} else {
// 获取最后一个节点
MayiktLinkedList.Node<E> x = last;
// 从最后一个元素开始向前遍历
for (int i = size - 1; i > index; i--) {
x = x.prev;
}
return x;
}
}
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
E unlink(MayiktLinkedList.Node<E> x) {
// 获取当前删除节点的内容
E item = x.item;
// 获取当前删除节点的下一个节点
Node<E> next = x.next;
// 获取当前删除节点的上一个节点
Node<E> prev = x.prev;
if (prev == null) {
// 删除节点为头结点
first = next;
} else {
// 上一个节点的next 指向 当前删除节点的next
prev.next = next;
// 删除节点的上节点变为空,告诉gc回收
x.prev = null;
}
if (next == null) {
// 当前删除节点为尾节点
last = prev;
} else {
// 下一个节点的prev 指向 当前节点的prev
next.prev = prev;
// 删除节点的下节点变为空,告诉gc回收
x.next = null;
}
x.item = null;
size--;
return item;
}
// 链表中的节点
private static class Node<E> {
/**
* 节点元素值
*/
E item;
/**
* 当前节点的下一个Node
*/
MayiktLinkedList.Node<E> next;
/**
* 当前节点上一个Node
*/
MayiktLinkedList.Node<E> prev;
// 使用构造函数传递参数
Node(MayiktLinkedList.Node<E> prev, E element, MayiktLinkedList.Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
private void checkElementIndex(int index) {
if (!isElementIndex(index)) {
throw new IndexOutOfBoundsException("index越界...");
}
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
}
public class Test {
public static void main(String[] args) {
MayiktLinkedList<String> stringMayiktLinkedList = new MayiktLinkedList<String>();
stringMayiktLinkedList.add("mayikt1");
stringMayiktLinkedList.add("mayikt2");
stringMayiktLinkedList.add("mayikt3");
System.out.println(stringMayiktLinkedList.get(1)); //mayikt2
stringMayiktLinkedList.remove(1);
System.out.println(stringMayiktLinkedList.get(1)); //mayikt3
}
}
3 LinkedList底层源码分析完全总结
链表数据底层原理实现:双向链表头尾相接
- 在底层中使用静态内部类Node节点存放节点元素
三个属性:prev 上一个节点、item 当前的值、next 下一个节点 - Add原理:一直在链表之后新增;
- Get原理:链表查询效率非常低, linkeList中采用折半查找,范围查询定位node节点;
- Remove原理:改变头尾相结合,改变指向;