前言:之前写了HashMap 和 ArrayList,还差 LinkedList 和 HashTable 没有学习,这篇学习下LinkedList。
PS: 源码分析针对于Android O 的jdk,即jdk1.8
同事推荐的流程图画图网站:https://www.processon.com/
demo地址:jiatai的demo
1. LinkedList简单介绍
LinkedList从实现方面用的数据结构是双向链表,它也可以被当作堆栈、队列或双端队列进行操作,不需要考虑扩容。
2. LinkedList简单使用
简单的往LinkedList里面塞数字0-9:
package com.example.demo_27_linkedlist;
import java.util.LinkedList;
public class MyClass {
public static void main(String[] args){
LinkedList linkedList = new LinkedList<Integer>();
linkedList.add(0);
for (int i = 1; i < 10; i++) {
linkedList.add(i);
}
System.out.print(linkedList);
}
}
对应log打印:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3. 简单使用所对应的内在
上面的使用主要涉及了三个方法
- 构造方法
- add()
- toString()
下面来简单地看下内在=-=
3.1 无参构造方法
很光棍,没啥看的。
/**
* Constructs an empty list.
*/
public LinkedList() {
}
3.2 add(E e)
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
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(E e)往LinkedList里添加元素的时候,会缓存一个last成员变量,实时记录上一次添加的元素,即链表的尾巴,添加完成后,更新链表的尾巴为新添加的元素。当链表为空的时候,last自然是null,所以第一个添加的元素即为first也为last。
这里再关注一下Node的构造方法,传入了last/E/null,分别对应prev、element和next。
来看下源码,一个简单的双向链表静态内部类:
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;
}
}
这里可以看出LinkedList的每个元素都具有索引它前后元素的能力,即linkedList是个双向链表。
3.3 toString()
和ArrayList 一样的,使用了继承自父类AbstractCollection的toString方法。
/**
* Returns a string representation of this collection. The string
* representation consists of a list of the collection's elements in the
* order they are returned by its iterator, enclosed in square brackets
* (<tt>"[]"</tt>). Adjacent elements are separated by the characters
* <tt>", "</tt> (comma and space). Elements are converted to strings as
* by {@link String#valueOf(Object)}.
*
* @return a string representation of this collection
*/
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
这里贴一下自己画的继承关系图
3.4 get(int dex)
// Positional Access Operations
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
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;
}
}
linkedList的get方法相比ArrayList效率肯定是低不少的,从上面可以看出linkedList是做了优化的,如果是前一半就从first开始遍历,如果是后一半就从last开始往前遍历,而ArrayList不需要这么遍历,直接就可以获取对应index的元素了。
3.5 add(int index, E element)
/**
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
* Inserts element e before non-null Node succ.
*/
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++;
}
add的过程其实包含了get和重新链接的过程,先找到对应index的链表元素,然后将新插入元素放在该元素前面,重新处理前后的链接关系。比ArrayList的劣势在于get的劣势,而插入就很容易了,第一不要考虑扩容,第二不需要挪一下后面所有元素。在元素很多的情况下显然LinkedList效率会更高。
4. 总结
就如我们一直所知LinkedList是个双向链表,与ArrayList相比,获取指定位置的元素效率较低,但是插入和移除元素的效率是更高的。