包路径:package java.util;
一、基本属性
1、transient int size = 0; 表示当前集合存储的元素个数
2、 transient Node<E> first; 表示第一个结点
3、 transient Node<E> last; 表示最后一个结点
二、构造函数
1、有参构造如下:传入的参数是集合类型的对象,首先调用自身的无参构造方法,构造一个空的链表,其次采用了addAll()方法,将集合类型的对象一一添加进去。
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
2、无参构造如下:
public LinkedList() {
}
三、继承关系
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
继承了AbstractSequentialList抽象类
实现了List接口(提供List接口中所有方法的实现)
实现了Dequq接口,实现了Dequq的所有可选操作
实现了Cloneable接口,它支持克隆(浅拷贝),底层实现:LinkedList节点并没有被克隆,只是通过Object的clone()方法得到的Object对象强制转化成LinkedList,然后把它内部的实例域全部置成空,然后把被拷贝的LinkedList节点中的每一个值都拷贝到clone中。
实现了java.io.Serializable接口,表示支持可序列化(底层提供两个方法:readObject()、writeObject()都可用于实现序列化)
四、方法
1、LinkedList中真正用来存储元素是通过一个内部类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;
}
}
2、添加元素:
addFirst(E e)基于 头插法:linkFirst(E e)参数中的元素作为链表的第一个元素、
addLast(E e)基于尾插法:linkLast(e)参数中的元素作为链表中的最后一个元素、
add(E e)基于尾插法:linkLast(e)、
addAll(Collection<? extends E> c)基于:addAll(size, c)、
add(int index, E element) 基于尾插法:linkLast(e)或者基于:linkBefore(E e, Node<E> succ)在非空节点succ之前插入元素e 、(不同情况采用方法不同)
addAll(int index, Collection<? extends E> c)
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//判断传进来的位置index是否合法
Object[] a = c.toArray();//将传进来的集合转化为数组,为Object[] a
int numNew = a.length;//新建一个变量存储数组的长度
if (numNew == 0)//如果添加元素的长度是0的话,直接返回,无须进行后面的操作
return false;
//定义两个节点pred、succ
//Node<E> pred:表示待添加节点的位置
//Node<E> succ:表示待添加节点的前一个位置
Node<E> pred, succ;
if (index == size) {//添加节点的位置在LinkedList最后一个元素的后面
succ = null; //将succ置成空的
pred = last; //pred指向尾结点
} else { //添加节点的位置在LinkedList中
succ = node(index);//返回对应索引位置的节点
pred = succ.prev; //pred指向succ节点的前一个节点
}
//接着遍历数组中的每一个元素,在每一次遍历的时候,都新建一个节点,该节点存储数组a中遍历
//的值,该节点的prev用来存储pred节点,next置为空,接着判断该节点的前一个节点是否为空,
//如果为空的话,则把当前节点设置为头结点,否则的话把当前节点的前一个节点的next值设置为当
//前节点,最后把pred设置为当前节点,以便后续新节点的插入
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
//这里仍然分为两种情况
//情况一:succ==null,也就是新添加的节点位于LinkedList最后一个元素的后面,通过遍历a中
//的所有元素,此时pred指向LinkedList中的最后一个元素,所以把last指向pred指向的节点。
//情况二:succ不等于空的时候,也就是表明在LinkedList中添加的元素,需要把pred的next指
//向succ上,succ的prev指向pred。
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew; //集合的大小设置为新的大小
modCount++; //modCount(修改的次数)自增
return true;
}
3、删除元素:
removeFirst() :基于头部删除:unlinkFirst() 删除LinkedList中的第一个节点;
removeLast():基于尾部删除: unlinkLast() 删除LinkedList中的最后一个节点;
remove(Object o) :基于:unlink() 删除指定的元素;
remove(int index) :基于:unlink(node(index)) 按照位置进行删除;
removeFirstOccurrence(Object o): 基于:remove(o);移除第一次出现的元素。(从前向后遍历集合) 底层调用remove()方法,通过从前向后遍历集合。
removeLastOccurrence(Object o):基于:unlink();移除第一次出现的元素。(从后向前遍历集合)
remove() :基于:removeFirst();
4、实现双端队列接口的方法:
peek() :返回头结点的值(即使头结点为空,也直接返回)
element():返回头结点的值(和上面的实现方式不一样,如头结点为空,抛出异常)
poll():移除头结点,并返回移除的头结点的值
remove():移除元素(移除头结点的元素,如果为空,则抛出异常)
offer(E e):在链表的尾部添加元素
offerFirst(E e):在链表的头部添加元素
offerLast(E e):在链表的尾部添加元素
peekFirst() :弹出链表的头结点的元素,如果为空的话,则返回null
peekLast() :弹出链表的尾结点的元素,如果为空的话,则返回null
pollFirst():取出链表头节点的值,并删除该头节点。如果头结点为空,则返回null
pollLast():取出链表尾节点的值,并删除该头节点。如果头结点为空,则返回null
push(E e):增加一个新的元素:(新添加的元素位于LinkedList的头结点)
pop():弹出头结点的元素(删除头结点的值,并返回删除的值)
五、总结
1、LinkedList底层采用双向链表的存储结构。
2、LinkedList既可以从前向后遍历,又可以从后向前遍历。
3、LinkedList可以存储重复数据
4、LinkedList可以存储null值
5、LinkedList插入数据有序