简单介绍
1)LinkedList底层实现了双向链表和双端队列特点;
2)可以添加任意元素(元素可重复),包括null;
3)线程不安全,没有实现同步。
底层操作机制
1)LinkedList底层维护了一个双向链表;
2)LinkedList中维护了两个属性first和last分别指向首节点和尾节点;
3)每个节点(Node对象),里面维护了prev、next、item三个属性,其中通过prev指向前一个节点,通过next指向后一个节点,实现双链表;
4)LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高
模拟双链表机制:
package com.pero.list_;
/**
* @author Pero
* @version 1.0
*/
public class LinkedList_ {
public static void main(String[] args) {
//创建节点内容
Node jake = new Node("jake");
Node tom = new Node("tom");
Node pero = new Node("pero");
//建立三个节点的双向链表
Node first = jake;
jake.next = tom;
tom.next = pero;
Node last = pero;
pero.prev = tom;
tom.prev = jake;
//正向遍历(从前往后,依次输出链表内容)
System.out.println("正向遍历");
while (true){
if (first==null){
first = jake;
break;
}
System.out.println(first);
first = first.next;
}
//反向遍历(从后往前,依次输出链表内容)
System.out.println("反向遍历");
while (true){
if (last == null){
last = pero;
break;
}
System.out.println(last);
last = last.prev;
}
//增添元素
Node lucy = new Node("lucy");
//增添到tom后面pero前面
tom.next = lucy;
lucy.next = pero;
pero.prev = lucy;
lucy.prev = tom;
//遍历增添后的链表
System.out.println("增加lucy后的链表");
while (true){
if (first == null){
first = jake;
break;
}
System.out.println(first);
first = first.next;
}
}
}
class Node{
public Object item; //存放数据
public Node prev; //前一个节点
public Node next; //后一个节点
//构造器
public Node(Object item){
this.item = item;
}
//toString方法重写
public String toString(){
return "Node item ="+item;
}
}
增删改查底层执行流程
测试代码
package com.pero.list_;
import java.util.Iterator;
import java.util.LinkedList;
/**
* @author Pero
* @version 1.0
*/
public class LinkedListCRUD {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
for (int i = 1; i <= 5; i++) {
linkedList.add(i);
}
System.out.println("linkedList="+linkedList);
//指定位置添加元素
linkedList.add(1,"jake");
//删除元素
linkedList.remove(); //默认删除第一个节点
linkedList.remove("jake");//删除指定元素
linkedList.remove(2);//删除指定位置元素
System.out.println("linkedList="+linkedList);
//修改某个节点对象
linkedList.set(2,998);
//得到某个对象
linkedList.get(3);
//遍历链表
//迭代器遍历
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//增强for
for (Object o :linkedList) {
System.out.println(o);
}
//普通for
for (int i = 0; i < linkedList.size(); i++) {
linkedList.get(i);
}
}
}
1.依次添加元素的方法add();
1)执行构造器(先调用无参构造器,定义size=0);
public LinkedList() {
}
transient int size = 0;
2)执行add()方法(第一,进行装箱,将int类型数据转换成INtegerl类型;第二,执行public boolean add(E e),方法开始添加元素;第三,执行void linkLast(E e) 方法,首先将尾部节点last赋值给l,然后创建一个新的Node对象newNode(Node类为LinkedList类的内部类,一个Node对象代表一个节点,在Node(Node<E> prev, E element, Node<E> next)中prev表示当前节点指向前一个节点,element表示当前节点存储的元素内容,next表示当前节点指向后一个节点,在Node<>(l, e, null)中,l表示指向上一个节点,e表示添加的元素,null表示该节点没有向后指向的节点),新节点创建好后(同时与前一个节点已连接好),尾节点指向新添加的节点,然后进行if (l == null)判断,如果条件成立意味着新创建的节点为首个节点,之前没有节点,那么首部指针first也指向新创建的节点,如果条件不成立则意味着当前节点不是首个创建的节点前面还有节点,则前一个节点l的属性l.next指向当前节点,判断结束后,执行size++语句意味着当前链表长度增加一位,然后执行modCount++语句,表示当前对链表所做的修改记录次数增加一次,节点添加成功并返回。第一个节点添加成功,后续节点的添加依次执行当前操作流程。)
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
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;
}
}
2.在指定位置添加元素add(2,"jake")
第一,执行private void checkPositionIndex(int index)方法;第二,执行private void checkPositionIndex(int index) 方法,进行if (!isPositionIndex(index))判断;第三,执行private boolean isPositionIndex(int index)方法,return index >= 0 && index <= size表示判断指定位置的值是否在链表范围中,如果不满足条件抛出异常执行outOfBoundsMsg(index)方法,返回 "Index: "+index+", Size: "+size;如果满足条件则进行if (index == size)判断,如果满足条件则证明所添加的位置为最末端位置,后续执行void linkLast(E e)方法,如果不满足条件则执行void linkBefore(E e, Node<E> succ);第四,在执行linkBefore方法之前,首先执行Node<E> node(int index)方法,判断index是在链表的前半段还是后半段,如果是在前半段则将首部指针first赋值给x,开始遍历直至满足条件返回x.next为即将添加的位置,如果是在后半段则将尾部指针last赋值给x,开始遍历直至满足条件返回x.prev为即将添加的位置;第五,执行void linkBefore(E e, Node<E> succ),首先将succ.prev赋值给pred表示目标位置前一个节点,而后创建新节点newNode,在new Node<>(pred, e, succ)中,pred表示指向前一个节点,e为当前节点存储的对象,succ表示之前在目标位置的节点现在在新添加节点的后面;然后执行succ.prev = newNode语句表示后一节点的prev指向新节点;执行判断语句if (pred == null),如果满足条件则表示当前节点前面没有节点为首个节点,则将首部指针指向新创建的节点,如果不满足条件则表示当前节点之前有节点,则将前一节点的next指向当前位置节点;最后对链表的长度+1,对链表的操作计数+1;新添加的节点添加完毕。
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
3.remove()方法默认删除第一个节点
第一,执行public E remove()方法;第二,执行public E removeFirst() 方法,将首部指针first赋值给f,此时f亦是第一个节点,然后执行if (f == null)判断如果满足条件抛出异常,如果不满足条件则执行unlinkFirst(f)方法;第三,执行private E unlinkFirst(Node<E> f)方法,记录首个节点的对象信息到element,将后一个节点记录到next中,将删除目标节点的内容和指向下一节点的信息置null,将首部指针first指向下一个节点,判断if (next == null),如果满足条件则说明当前链表删除目标节点后就变成空链表,则尾部指针last也要置null,如果不满足条件则新的第一节点的prev要置null,链表长度size-1,对链表修改次数记录modCount+1,返回被删除节点信息。
public E remove() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
4.remove("jake")方法删除指定元素
第一,执行public boolean remove(Object o)方法,首先判断删除目标结点的对象信息是否是null,如果满足条件,则从头依次对链表进行搜索,直至节点为空,执行判断if (x.item == null),执行unlink(x)方法;如果不满足条件,则从头依次对链表进行搜索,直至节点为空,执行判断if (o.equals(x.item)),执行unlink(x)方法;执行E unlink(Node<E> x)方法,首先将当前节点的信息记录到element,将目标结点的prev和next分别记录到新建的prev和next中,执行判断if (prev == null) ,如果满足条件,说明当前节点为第一个节点,则首部指针first指向目标结点的后一个节点,如果不满足条件说明当前节点之前还有节点,则将目标结点的前一个节点的next(prev.next)只想目标结点的后一个节点,并将目标节点的prev(x.prev)置null,执行判断if (next == null),如果满足条件则说明当前节点为最后一个节点,则将尾部指针指向当前节点的前一个节点,如果不满足条件说明当前节点后面还有节点,则将后一个节点的prev(next.prev)指向前一个节点,然后将目标结点的next(x.next)置null,然后将目标结点的存储对象置null,链表长度size-1,对链表执行修改操作的纪录次数+1,返回被删除的目标信息。
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
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) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
5.linkedList.remove(2)方法删除指定位置元素
第一,执行public E remove(int index)方法;第二,执行private void checkElementIndex(int index)方法,判断index索引是否异常,如果没有异常执行unlink(node(index));第三,执行Node<E> node(int index)方法,判断index是在链表的前半段还是后半段,如果是在前半段则将首部指针first赋值给x,开始遍历直至满足条件返回x.next为即将添加的位置,如果是在后半段则将尾部指针last赋值给x,开始遍历直至满足条件返回x.prev为即将添加的位置;第四,执行E unlink(Node<E> x) 方法,首先将当前节点的信息记录到element,将目标结点的prev和next分别记录到新建的prev和next中,执行判断if (prev == null) ,如果满足条件,说明当前节点为第一个节点,则首部指针first指向目标结点的后一个节点,如果不满足条件说明当前节点之前还有节点,则将目标结点的前一个节点的next(prev.next)只想目标结点的后一个节点,并将目标节点的prev(x.prev)置null,执行判断if (next == null),如果满足条件则说明当前节点为最后一个节点,则将尾部指针指向当前节点的前一个节点,如果不满足条件说明当前节点后面还有节点,则将后一个节点的prev(next.prev)指向前一个节点,然后将目标结点的next(x.next)置null,然后将目标结点的存储对象置null,链表长度size-1,对链表执行修改操作的纪录次数+1,返回被删除的目标信息。
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
ArrayList和LinkedList的比较
底层结构 | 增删效率 | 改查效率 | |
ArrayList | 可变数组 | 较低,数组扩容 | 较高,索引定位 |
LinkedList | 双向链表 | 较高,链表追加 | 较低,首部指针遍历 |
项目编程中的选择:
1)如果对存储数据的改查操作较多,则选择ArrayList;
2)如果对存储数据的增删操作较多,则选择LinkedList;
3)一般来说,在程序中大部分都是查询操作,选择ArrayList的较多;
4)也可根据业务具体选择,一个模块选择ArrayList,另外一个模块选择LinkedList。