一、LinkedList详解
特点:
- 元素可以重复
- 元素可以为null
- 数据插入有序
JDK1.8源码分析:
-
继承关系
继承AbstractSequentialList类,实现了List,Deque,Cloneable,Serializable接口。
public class LinkedList<E>extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
-
底层数据结构
双向链表(源码中有表示)↓↓
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;
}
}
- 基本属性
transient int size = 0; //集合中元素个数
transient Node<E> first; //头结点
transient Node<E> last; //尾节点
- 默认值
无 - 构造函数(2个)
public LinkedList() { //无参构造
}
public LinkedList(Collection<? extends E> c) { //有参构造函数
this();
addAll(c);
}
-
增长方式
无限增长(因为底层是双向链表,所以它的容量不受限制)
-
CRUD方法研究
1、添加元素:add(E e)(默认尾插方式)
public boolean add(E e) {
linkLast(e); //调用linklast()
return true;
}
void linkLast(E e) { //尾插法
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);//调用节点类的构造函数实例化一个新的节点
last = newNode; //将newNode重置为尾节点
if (l == null) //如果链表为空,直接把新节点置为头结点
first = newNode;
else
l.next = newNode;//链表不为空,新节点插在尾节点之后(尾插)
size++; //容量+1
modCount++; //版本号+1
}
2、删除元素:remove(Object o)
public boolean remove(Object o) {
if (o == null) { //判断删除的对象是否为空
for (Node<E> x = first; x != null; x = x.next) {
//删除元素是从前往后删,重复元素只删除第一个出现的
if (x.item == null) {
unlink(x); //调用unlink()方法 ↓↓↓
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
//前驱分为两种情况
if (prev == null) { //头结点first
first = next; //令first代表的是删除的节点的下一个节点
} else { //非头结点
prev.next = next;
x.prev = null;
}
//后继分为两种情况
if (next == null) { //尾节点
last = prev; //令last代表删除的节点的上一个节点
} else { //非尾节点
next.prev = prev;
x.next = null;
}
x.item = null; //把当前要删除的节点置为空
size--; //容量-1
modCount++;//版本号+1
return element; //返回删除的节点
}
手动实现LinkedList
//我只是简单的谢了LinkedList的添加、获取和删除元素的三个方法,
//其他方法通过理解源码也可以很容易的用双向链表来实现
public class LinkedListSelf2202<T> {
class Entry{ //内部类表示及节点
private int data; //节点个数
private Entry next;
public Entry(){
this.next = null;
}
public Entry(Integer val){
this.data = val;
this.next = null;
}
}
int size = 0; //链表的节点个数
private Entry head = null;
public LinkedListSelf2202(){
this.head = new Entry(); //得到头节点的引用
}
//添加
public void add(int val){
Entry cur = this.head;
while (cur.next != null) {
cur = cur.next; //cur++
}
Entry entry = new Entry(val);
cur.next = entry;
size ++;
}
//查找
public Entry search(Integer val){
Entry cur = this.head;
while(cur.next != null){
if(cur.next.data == val) {
return cur;
}cur = cur.next;
}return null;
}
//获取
public int get(int index){
Entry entry = this.head;
if(index>=0 && index<size){
for (int i = 0; i <= index; i++) {
entry = entry.next;
}
}
return entry.data;
}
//删除
public boolean remove(int key){
Entry cur = search(key); //先查找要删除的元素是否存在
if(cur == null){
//待删除元素在集合中不存在
return false;
}
Entry del = cur.next;
cur.next = del.next;
size--;
return true;
}
//打印
public void show() {
Entry cur = this.head.next;
while (cur != null) {
System.out.print(cur.data + " ");
cur = cur.next;
}
System.out.println();
}
public static void main(String[] args) {
LinkedListSelf2202 linkedListSelf2202 = new LinkedListSelf2202();
linkedListSelf2202.add(22);
linkedListSelf2202.add(33);
linkedListSelf2202.add(44);
linkedListSelf2202.show();
Integer integer = linkedListSelf2202.get(0);
System.out.println(integer);
linkedListSelf2202.remove(22);
linkedListSelf2202.show();
}
}
执行结果:
ArrayList和LinkedList异同点,各自的优势(应用场景)?
-
相同点:
继承关系:都是List接口下的实现类具有List提供的方法
有序性:数据都是插入有序
重复性:集合中元素是可以重复的
null值:都可以存储null值
安全性:都是非线程安全的集合(都可能会出现并发性异常ConcurrentModificationException()) -
不同点:
数据结构:ArrayList底层是数组。LinkedList底层是双向链表
特有方法:LinkedList因为实现了Deque双向队列接口,具有特有的方法:例如addFirst() 、 addLast()
效率: ArrayList访问效率高,移动、删除效率低, LinkedList 插入、删除效率高,访问效率低
(ArrayList查询时间复杂度O(1)、LinkedList插入时间复杂度O(1))
3.应用场景:
根据两者的效率来看:
ArrayList在查询较高的业务场景优先考虑,
LinkedList在修改、添加等操作较多的场景下优先考虑