LinkedList
LinkedList
是一个双向链表,其实现了 List
和Deque
接口。下图为其结构:
LinkedList
实现了所有双向链表的所有操作,可以从头节点或尾节点双向遍历链表,当查找指定index
的元素时,会从头或尾较近方向遍历查找。
LinkedList
的实现是非同步的,当多线程操作时,需要在外部实现同步。
1 成员变量
LinkedList
类中主要有三个成员变量:
transient int size = 0; // 元素的数量
/**
* Pointer to first node.
*/
transient Node<E> first; // 链表的头节点
/**
* Pointer to last node. // 链表的尾节点
*/
transient Node<E> last;
这里的关键字transient
与序列化有关,将不需要序列化的成员变量前加关键字transient
,即这些成员变量不会被持久化到磁盘中,其生命周期只能在内存中。具体可参考:https://baijiahao.baidu.com/s?id=1636557218432721275&wfr=spider&for=pc
这里的size
属性指,LinkedList
中元素的数量。first
和last
属性的类型为Node
,是LinkedList
中实现的私有类,其结构如上如所示,代码如下:
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 构造方法
LinkedList
有两个构造方法:默认构造方法和创建包含集合c中元素的构造方法
/**
* Constructs an empty list.
*/
public LinkedList() {
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this(); // 调用默认构造方法
addAll(c);
}
由于LinkedList
是通过Node
节点实现的,所以在构造方法中不需要像ArrayList
中做一些数组初始化的操作。这里主要看一下第二个构造方法,其主要操作就是通过addAll(c)
方法将集合c中的元素都添加至LinkedList
对象中,根据集合c元素迭代的顺序建链表。
3 成员方法
3.1 get()
LinkedList
中有2个get
相关方法:
Modifier and Type | Method | Description |
---|---|---|
E | get(int index) | Returns the element at the specified position in this list. |
E | getFirst() | Returns the first element in this list. |
E | getLast() | Returns the last element in this list. |
下面为get()
方法的代码:
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
其中调用了node()
方法:
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { // 右移,index与链表长度的一半比较,如果小于,则从头遍历
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { // 如果index大于链表长度的一半,则从尾遍历
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
get()
方法实现了从头或尾较近方向遍历查找指定index
元素。getFirst()
和getLast()
直接返回成员变量first
和last
。
这里需要注意node(index)
方法,LinkedList
类中的很多方法都是通过该方法来定位元素在链表的位置
3.2 add()
remove()
set()
等
LinkedList
中实现了很多增删改的方法,这些方法当需要定位到具体节点都是通过node(index)
方法实现,这些方法的具体实现都差不多,这里只以add()
方法为例。
LinkedList
中有2个add()
方法:
Modifier and Type | Method | Description |
---|---|---|
void | add(int index, E element) | 在链表的制定位置插入指定元素 |
boolean | add(E e) | 在链表的尾部添加指定元素 |
其代码如下:
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size) // 判断插入点是否为链表尾部
linkLast(element);
else
linkBefore(element, node(index));
}
public boolean add(E e) {
linkLast(e);
return true;
}
可以看到这两个方法都调用了linkLast()
方法,对于add(int index, E element)
首先判断插入点是否为链表尾部,如果是,则直接调用linkLast()
方法,否则调用linkBefore()
方法。
下面首先看一下linkLast()
方法:
void linkLast(E e) {
final Node<E> l = last; // 获取链表的尾节点
final Node<E> newNode = new Node<>(l, e, null); // 创建一个元素数据为e的新节点
last = newNode; // 新节点作为尾节点
if (l == null) // 判断链表是否为空
first = newNode; // 若为空则表明新节点既是尾节点也是头节点
else
l.next = newNode; // 连接旧链表和新节点
size++; // 链表元素数量更新
modCount++; // 链表修改次数更新
}
当对于add(int index, E element)
首先判断插入点不是尾节点,则linkBefore()
方法,其传入参数为元素值和位置为index
的节点,即插入元素节点的后继节点(通过node()
方法实现):
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.3 indexOf()
Modifier and Type | Method | Description |
---|---|---|
int | indexOf(Object o) | 返回指定元素第一次出现的index,如果不存在则返回-1 |
int | lastIndexOf(Object o) | 返回指定元素最后次出现的index,如果不存在则返回-1 |
public int indexOf(Object o) {
int index = 0;
if (o == null) { // 如果查找元素为null,则通过“==”来比较
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else { // 如果查找元素不是null,则通过equals方法来比较
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
可以看到,indexOf(Object o)
从头遍历元素直到第一次出现指定元素(或没有找到)
3.4 toArray()
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;
}
总结
-
LinkedList
通过双向链表实现 -
类中方法需要实现查找指定位置(index)的元素通过
node()
方法实现