LinkedHashMap是为了解决遍历Hash表的无序问题,它内部维护了一个链表用于记录你插入元素(或你访问元素的顺序)的位置,遍历时直接遍历链表,元素的顺序即为你插入的顺序,但是Entry对象要多加两个成员变量before和after用于记录链表的前驱和后继。所以LinkedHashMap的的存储效率要低于HashMap,但是遍历效率要高于HashMap。
java.util.LinkedHashMap
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* LinkedHashMap内部不仅存储了一个散列表,
* 也维护了一个链表,
* 根据链表中元素的顺序可以分为:按插入顺序的链表,和按访问顺序(调用get方法)的链表。
* 默认是按插入顺序排序,如果指定按访问顺序排序,那么调用get方法后,会将这次访问的元素
* 移至链表尾部,不断访问可以形成按访问顺序排序的链表。
* 可以重写removeEldestEntry方法返回true值指定插入元素时移除最老的元素
*/
public class LinkedHashMap<K,V> extends HashMap<K,V>
implements Map<K,V>
{
private static final long serialVersionUID = 3801124242820219131L;
/**
* 此Map中维护的一个双向循环链表的头节点
*/
private transient Entry<K,V> header;
/**
*
* 遍历Map的顺序的变量,
* true为按访问顺序遍历
* false为按插入顺序遍历
*/
private final boolean accessOrder;
/**
*
* 构造一个空的,按插入顺序遍历的LinkedHashMap实例,
* 初始容量与加载因子由自己指定
*/
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
/**
* 构造一个空的,按插入顺序遍历的LinkedHashMap实例,
* 初始容量由自己指定
*/
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
/**
*
* 构造一个空的,按插入顺序遍历的LinkedHashMap实例
* 初始容量与加载因子按HashMap的默认值16与0.75
*/
public LinkedHashMap() {
super();
accessOrder = false;
}
/**
* 构造一个非空,按插入顺序遍历的LinkedHashMap实例
*/
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(m);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
/**
*
* 在HashMap每个构造方法中都会调用一次此init()方法
* 这里是初始化一个双向循环链表的头节点
*/
void init() {
header = new Entry<K,V>(-1, null, null, null);
header.before = header.after = header;
}
/**
*
* 在父类HashMap进行容量扩充时会调用此方法
* 把原来Entry数组中的对象经过重新计算索引转移到此newTable中
* 这里与HashMap中的transfer方法的实现不再相同,而且比它更快。
*
* HashMap是遍历Entry数组,需要检查是否为null,如果不为空的话则就需
* 要遍历数组中的链表,而这里所有的元素都链接成了一个链表,直接遍历此
* 链表就可以遍历出LinkedHashMap中的所有元素。
*/
void transfer(HashMap.Entry[] newTable) {
int newCapacity = newTable.length;
for (Entry<K,V> e = header.after; e != header; e = e.after) {
int index = indexFor(e.hash, newCapacity); //根据新容量重新计算index
e.next = newTable[index]; //最后添加的节点放最前面
newTable[index] = e;
}
}
/**
*
* 查找Map中是否含有本值,这里重写了HashMap的containsValue方法
* 因为在LinkedHashMap中只需要遍历链表查找,这比HashMap遍历table查找更加
* 高效和快速
*/
public boolean containsValue(Object value) {
// Overridden to take advantage of faster iterator
if (value==null) {
for (Entry e = header.after; e != header; e = e.after)
if (e.value==null)
return true;
} else {
for (Entry e = header.after; e != header; e = e.after)
if (value.equals(e.value))
return true;
}
return false;
}
/**
*
* 通过key值获得value,如果没有找到此key则返回null。
* 不过返回null也可能是其value为null
* 通过contain方法可判断Map中是否含有此键
*
* 重写此方法的原因是:
* 如果map中链表是按照访问顺序进行排序,
* 就需要把本次访问的元素移到链表尾部,表示
* 这是你最新访问的元素,以后可能会继续访问它
*
*/
public V get(Object key) {
Entry<K,V> e = (Entry<K,V>)getEntry(key);
if (e == null)
return null;
e.recordAccess(this);
return e.value;
}
/**
* 移除元素,清空链表
*/
public void clear() {
super.clear();
header.before = header.after = header;
}
/**
* LinkedHashMap 的Entry对象.
*/
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// 包含其前驱和后继的信息.
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
/**
*
* 在链表中移除此对象只需要修改前驱和后继的指针
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
*
* 把元素插入到指定的存在的元素之前
* 链表的插入也是修改指针
*/
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
/**
*
* 当调用父类的put或putAll方法,发现要插入的键已经存在时会调用此方法,
* 当调用LinkedHashMap的get方法时会调用此方法。
* 如果LinkedHashMap是按访问顺序遍历的,就移动此Entry
* 到链表的最后位置,否则do nothing
*/
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove(); //移除它
addBefore(lm.header); //再把她添加到链表尾部
}
}
/**
* 在移除元素时会调用此方法,即调用
* 父类的remove(key)方法时调用它
* 这里是改变链表指针,移除链表中的此元素
*/
void recordRemoval(HashMap<K,V> m) {
remove();
}
}
private abstract class LinkedHashIterator<T> implements Iterator<T> {
Entry<K,V> nextEntry = header.after; //初始为第一个元素
Entry<K,V> lastReturned = null;
/**
* 检测同步
*/
int expectedModCount = modCount;
public boolean hasNext() {
return nextEntry != header;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
LinkedHashMap.this.remove(lastReturned.key);
lastReturned = null;
expectedModCount = modCount;
}
Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (nextEntry == header)
throw new NoSuchElementException();
Entry<K,V> e = lastReturned = nextEntry;
nextEntry = e.after;
return e;
}
}
//依次重写next方法,返回不同的迭代器。
private class KeyIterator extends LinkedHashIterator<K> {
public K next() { return nextEntry().getKey(); }
}
private class ValueIterator extends LinkedHashIterator<V> {
public V next() { return nextEntry().value; }
}
private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() { return nextEntry(); }
}
// These Overrides alter the behavior of superclass view iterator() methods
Iterator<K> newKeyIterator() { return new KeyIterator(); }
Iterator<V> newValueIterator() { return new ValueIterator(); }
Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }
/**
*
* 重写父类addEntry方法
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex);
/*如果子类重写了removeEldestEntry方法并返回true,
将在map中移除链表中的第一个元素,否则检测是否需要扩充容量。
eldest元素即为链表中的第一个元素*/
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
if (size >= threshold)
resize(2 * table.length); //扩充至原来的2倍
}
}
/**
*
* 重写父类的createEntry方法,此方法不需要检测是否要扩充容量,
* 与HashMap中此方法的唯一区别时,这里不仅把元素插入到散列表中,
* 而且将元素插入到了链表尾
*/
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}
/**
*
* 此方法会在调用put()或putAll()方法时调用,此方法告诉linkedHashMap
* 是否在添加一个元素后移除最老的元素
* 比如下面代码会让LinedHashMap的大小始终保持在100
* private static final int MAX_ENTRIES = 100;
*
* protected boolean removeEldestEntry(Map.Entry eldest) {
* return size() > MAX_ENTRIES;
* }
* 此方法默认返回false,需要子类去复写此方法
*
* 什么是eldest元素?
* 如果按插入问顺序来说,是map中最先插入的元素
* 如果按访问顺序来说,是map中最久未被访问的元素
*/
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
}
关于LinkedHashSet
/**
* LinkedHashSet实际上是基于LinkedHashMap的基础上实现的,
* LinkedHashSet继承自HashSet,在HashSet中有一构造方法
* HashSet(int initialCapacity, float loadFactor, boolean dummy)
* 第三个参数dummy为true时new出了一个LinkedHashMap实例,以Set中的
* 元素为键,以一个Object的虚假的对象为值,所以HashSet中的元素不可能重复。
* 以下构造函数dummy都为true
*/
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
private static final long serialVersionUID = -2851667679971038690L;
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
public LinkedHashSet() {
super(16, .75f, true);
}
public LinkedHashSet(Collection<? extends E> c) {
super(Math.max(2*c.size(), 11), .75f, true);
addAll(c);
}
}
分享到:
相关推荐
这个"深入理解Java集合框架.zip"文件很可能是包含了一系列关于Java集合框架的详细资料,比如源码分析、设计模式以及最佳实践。下面将详细探讨Java集合框架的关键知识点。 1. **集合接口**:Java集合框架的核心接口...
Java集合框架是Java编程语言中的一个核心部分,它为数据结构和对象的存储、管理和操作提供了统一的接口和实现。这个框架包括了多种类型的集合,如List、Set、Queue和Map,以及它们的各种实现类,如ArrayList、...
Java集合框架是Java编程语言中的核心部分,它提供了一组高效的数据结构,使得开发者能够方便地存储和操作数据。在面试中,对于Java集合的深入理解往往被视为衡量开发者能力的重要指标。本文将深入剖析Java集合的源码...
9. **源码分析**:深入理解Java集合框架的源码可以帮助开发者优化代码,了解其内部实现,比如哈希算法、链表结构或红黑树的工作原理。 综上所述,Java集合框架是Java编程中的基础,它提供的接口和类覆盖了日常开发...
总的来说,深入学习Java集合框架源码,不仅可以提升对Java语言的理解,还能增强解决实际问题的能力,是每个Java开发者必备的技能之一。在实践中不断探索,结合开源系统的源码分析,将使你对Java集合框架有更全面和...
3. **源码分析**: - 对于集合框架中的实现类,深入理解其源码是非常有价值的。例如,`ArrayList`的扩容机制,`HashMap`的哈希冲突解决方法,以及`LinkedList`的链表操作效率等,都是面试和实际开发中常见的问题。 ...
在深入学习集合框架的基础上,我们还可以进一步探讨它们的应用,如如何使用LinkedHashMap实现一个LRU(最近最少使用)缓存。LRU是一种用于管理缓存淘汰策略的算法,它保证了最先被使用的数据最不容易被淘汰。 最后...
Java集合框架是Java编程中非常重要的部分,它提供了一种高效、灵活的数据组织方式。本文主要探讨了几个关键...通过对源码的深入分析,我们可以更好地掌握Java集合框架的工作原理,并根据实际需求选择最适合的数据结构。
总的来说,Java集合框架源码分析可以帮助我们掌握集合操作的底层原理,提高代码性能,增强对并发编程的理解,并且有助于我们设计出更高效、更安全的Java程序。通过对Collections类的深入学习,我们可以更好地利用...
在面试中,不仅要知道这些概念,还要理解其内部工作原理,如ArrayList和Vector的扩容策略,HashMap和Hashtable的同步性及对null的处理,以及集合类的源码分析。熟练掌握这些知识点能够帮助你在面试中表现出色。同时...
### HashSet源码分析 1. **构造器**:HashSet提供了多个构造器,可以创建默认容量的HashSet,指定容量和负载因子的HashSet,或者带有一个初始集合的HashSet。 2. **add(E e)**:向HashSet添加元素时,实际上是将...
源码分析:ArrayList、Vector、LinkedList、HashMap、ConcurrentHashMap、HashSet、LinkedHashSet and LinkedHashMap Java 并发编程 > 线程机制、线程通信、J.U.C组件、JMM、线程安全、锁优化 Java I/O 磁盘...
Java集合框架提供了多种用于存储和操作数据的接口与类。这些集合类主要分为两大类:`Collection` 和 `Map`。 #### Collection `Collection` 是一组对象的容器,它允许我们对这些对象执行一些通用操作,如添加、删除...
在Java编程领域,题库测试是开发者提升技能和准备面试的重要资源。这个“java题库测试”可能包含一系列与Java语言、...同时,结合源码分析,可以更直观地了解Java底层的工作原理,对于提升编程技巧和代码质量大有裨益。