只写一些比较常用的方法
首先要明确一个基础知识,LinkedList是链表结构,是通过地址相连的,举个栗子: 链表中有A B C三个元素 他们每一个都有一个相应的节点对象 节点对象有 prev 和 next两个属性 A元素的 prev中存储的为null 因为是第一个元素 next中存储的是B 元素的节点对象 这样这两个元素就连接在一起了,同理可证,B元素中的 prev含有 A元素的节点对象 next含有C元素的节点对象等等 以此类推。
获取第一个或者最后一个元素
//Node是节点对象,先判断节点对象是否为空,如果为空说明这个链表就是空的,因为没有第一个元素。如果不为空就返回 item , item是节点对象的一个属性,存储着元素的值
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
//与上面同理
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
删除第一个或者最后一个元素
//删除第一个元素
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
//调用unlinkFirst方法 下面我们一起看一下这个方法
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
//将第二个元素的节点对象赋值给firest 直接断开与第一个对象的连接(firest为LinkedList的一个属性,代表第一个元素)
first = next;
//如果等于NULL说明集合已经为空
if (next == null)
last = null;
else
//如果不为空就将第二个元素与第一个元素的连接断开
next.prev = null;
//长度减一
size--;
modCount++;
return element;
}
//删除最后一个元素 与上面的原理相同
将元素添加到开头或者结尾
//主要调用linkFirst 方法 下面我们一起看一下
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {
//先获取第一个元素节点对象
final Node<E> f = first;
//为当前对象创建节点对象 参数为 1. prev 与上一个节点对象的连接(因为是第一个所以为Null)
//2 参数本身,会成为节点对象的一个属性 3.next 与下个节点连接的节点对象(将原来第一个的元素变成第二个,所以参数为f)
final Node<E> newNode = new Node<>(null, e, f);
//将新节点赋值到firest
first = newNode;
//判断f是否为空,如果为空说明集合就只有一个元素
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
//添加到末尾的原理与上面的相同
集合中是否包含某元素
//这个方法调用的是indexOf 方法 下面我们一起看一下
public boolean contains(Object o) {
return indexOf(o) != -1;
}
//查找目标对象索引
public int indexOf(Object o) {
int index = 0;
//判断查找对象是否为null
if (o == null) {
//先拿到第一个节点对象,然后依次循环,直到找到目标对象
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
//与上面的循环同理
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
//找不到返回-1
return -1;
}
删除元素
public boolean remove(Object o) {
if (o == null) {
//循环,先创建一个节点对象,并赋值为第一节点。结束循环的条件为 节点对象为Null 一次循环后就获取下一个节点继续循环
for (Node<E> x = first; x != null; x = x.next) {
//判断是否找到了目标对象
if (x.item == null) {
//找到了就调用unlink 方法删除
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;
}
//上面调用的unlink 方法
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;
//如果prev为null说明是第一个元素
if (prev == null) {
//直接将下一个节点对象赋值给first 这样链表和要删除的节点就断开连接了
first = next;
} else {
//将前一个节点的指针直接指向要删除元素的下一个节点对象
prev.next = next;
//删除X的前节点对象
x.prev = null;
}
//与上面同理
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
//清空数据
x.item = null;
size--;
modCount++;
return element;
}
将一个集合中的元素添加到链表中
//调用addAll方法 下面一起看一下
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//在指定位置添加元素
public boolean addAll(int index, Collection<? extends E> c) {
//先判断索引是否越界
checkPositionIndex(index);
//将集合转成数组
Object[] a = c.toArray();
//获取数组长度,并判断是否为空
int numNew = a.length;
if (numNew == 0)
return false;
//Node<E>是一个节点对象,主要是存储跟他相连上一个和下一个节点的索引信息的(链表)
Node<E> pred, succ;
//如果添加位置在链表最后 则让pred等于原链表的最后一个元素的节点对象
if (index == size) {
succ = null;
pred = last;
} else {
//如果不是在最后,就获取index索引位置和其前面的节点对象
succ = node(index);
pred = succ.prev;
}
//循环主要是进行元素的插入操作,体现在pred的变化上。循环开始前 pred是目标index的前一个元素的节点对象。
//循环结束后pred是要插入集合的最后一个元素的节点对象
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//为当前对象创建节点对象
Node<E> newNode = new Node<>(pred, e, null);
//先判断pred 节点对象是否为空,如果为空证明是第一个元素
if (pred == null)
first = newNode;
else
//如果不为空就将上一个节点对象(也就是index位置的上一个元素)的指针只向待插入对象
pred.next = newNode;
//将待插入对象的节点对象赋值给pred。这一步就完成了pred的转换。
//由index的上一个元素的节点对象变成了index对象,并作为下一次循环中新节点对象的前一个对象。
pred = newNode;
}
//如果满足条件说明是最后一个对象,就将pred赋值给最后一个节点对象
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
清空集合
//清空集合
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
//创建节点对象X 并赋值为第一个节点对象,直到X 等于null说明已经到最后一个对象了
for (Node<E> x = first; x != null; ) {
//先取出该节点的下一个节点对象,并将所有属性赋值为null
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
在指定位置添加元素
public void add(int index, E element) {
checkPositionIndex(index);
//如果将元素添加到最后
if (index == size)
linkLast(element);
else
//如果不是就调用linkBefore方法
linkBefore(element, node(index));
}
//linkLast 方法
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++;
}
//add方法调用
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节点对象的指针指向newNode对象
succ.prev = newNode;
//主要是判断集合是否为空
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
获取链表第一个元素
public E peek() {
//获取第一个元素的数据
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E element() {
//返回第一个元素,与上面的不同是 这个方法不能返回Null
return getFirst();
}
//比较简单就不介绍了
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
获取迭代器
//获得迭代器
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
集合转数组
//跟ArrayList一样,也是将集合转成数组 这个方法只能返回Object类型的数组
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
//跟ArrayList一样,也是将集合转成数组 这个方法可以返回指定泛型
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
if (a.length > size)
a[size] = null;
return a;
}