1.双向链表比单向链表优势
对于插入操作,单链表与双链表都是O(1)时间复杂度。
对于删除操作,我们经常看到的说法是单链表的删除时间复杂度是O(1),其实这种说法不准确。因为单从删除这个动作来说,确实是O(1),只要把next指针的指向做改变即可。但是,在删除操作之前,我们需要找到前驱节点,这个操作就不是O(1),而是O(n)。
具体来说,链表删除数据一般是两种情况:
1.删除节点中值等于某个特定值的节点。
2.删除给定指针指向的节点。
对于第一种情况,不管单链表双链表,都需要从头节点开始挨个遍历,找到值等于给定值的节点,然后删除,这个时间复杂度都为O(n)。
第二种情况下,因为已经找到了要删除的节点,但是我们删除该节点之前需要找到其前驱节点。对于单链表,前面我们已经分析了这个时间复杂度为O(n)。而双向链表因为有前后两个指针,能够在O(1)时间复杂度的情况下直接定位到前驱节点。
同理,如果需要在指定的节点前面插入一个节点,双向链表的时间复杂度同样是O(n),而单链表是O(1)。
2.LinkedHashMap基本功能
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
JDK1.8中,LinkedHashMap声明代码如上。可以看出来,LinkedHashMap继承自HashMap,同时实现了Map接口。
LinkedHashMap与HashMap的最大区别在于,HashMap是无序的,我们对HashMap遍历的时候是无序的。但是我们有时候也需要一个有序的Map,遍历的时候能按照插入的顺序,对应的数据结构就是LinkedHashMap。
/**
* HashMap.Node subclass for normal LinkedHashMap entries.
*/
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
JDK1.8里,LinkedHashMap中的内部类Entry定义。由上面代码不难看出,相比HashMap中的Node类,LinkedHashMap中的Entry多了before, after两个性质,before, after实际上维护的就是LinkedHashMap中的双向链表。
HashMap中的Node类部分代码如下
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
.......
上述图片来自网络
结合上面的代码与图示,重点分析一下里面的指针
Node里面有next指针,该指针是用于维护单链表的next属性的,目的是解决Hash桶中冲突key。
而before,after指针,则是维护双向链表,访问时候有序靠的就是这个双向链表。
3.LinkedHashMap与HashMap遍历对比
写了一小段代码,简单对比一下LinkedHashMap与HashMap遍历的效果。
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapVisit {
public static void visit() {
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("b", 2);
map.put("a", 1);
map.put("d", 4);
map.put("c", 3);
for(Map.Entry<String, Integer> entry: map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public static void visit2() {
Map<String, Integer> map = new HashMap<>();
map.put("b", 2);
map.put("a", 1);
map.put("d", 4);
map.put("c", 3);
for(Map.Entry<String, Integer> entry: map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public static void main(String[] args) {
visit();
System.out.println();
visit2();
}
}
以上代码运行的结果:
b: 2
a: 1
d: 4
c: 3
a: 1
b: 2
c: 3
d: 4
通过上面例子可以发现,LinkedHashMap遍历的时候,是按照插入顺序的。
4.accessOrder参数
LinkedHashMap的构造函数中有一个accessOrder属性,默认为false。LinkedHashMap默认的顺序是插入序,如果accessOrder属性为true,实现的是访问序。在访问序下,最近访问的节点会是尾节点,遍历的时候会是在最后的位置。
public static void visit3() {
LinkedHashMap<String, Integer> map = new LinkedHashMap<>(8, 0.75f,true);
map.put("b", 2);
map.put("a", 1);
map.put("d", 4);
map.put("c", 3);
for(Map.Entry<String, Integer> entry: map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
int num = map.get("a");
System.out.println();
for(Map.Entry<String, Integer> entry: map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
上面代码的输出为
b: 2
a: 1
d: 4
c: 3
b: 2
d: 4
c: 3
a: 1