/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.github.ggrandes.kvstore.structures.hash;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
import com.github.ggrandes.kvstore.utils.GenericFactory;
import com.github.ggrandes.kvstore.utils.PrimeFinder;
import org.apache.log4j.Logger;
/**
* Native Integer LinkedHashMap
* This class is NOT Thread-Safe
* @param <V> type of values
*
* @author Guillermo Grandes / guillermo.grandes[at]gmail.com
*/
public class IntLinkedHashMap<V> implements Iterable<V> {
private static final Logger log = Logger.getLogger(IntLinkedHashMap.class);
private int elementCount;
private IntLinkedEntry<V>[] elementData;
private final float loadFactor;
private int threshold;
private int defaultSize = 17;
private GenericFactory<V> factory;
/**
* The head of the doubly linked list. 双向链表的头指针
*/
private transient IntLinkedEntry<V> header;
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* 访问顺序: true表示按照访问的顺序, false表示插入的顺序
*/
private final boolean accessOrder;
/**
* Constructs a new {@code IntLinkedHashMap} instance with the specified capacity.
*
* @param capacity the initial capacity of this hash map.
* @throws IllegalArgumentException when the capacity is less than zero.
*/
public IntLinkedHashMap(final Class<V> type) {
this(17, type, false);
}
public IntLinkedHashMap(final int capacity, final Class<V> type) {
this(capacity, type, false);
}
public IntLinkedHashMap(final int capacity, final Class<V> type, final boolean accessOrder) {
this.accessOrder = accessOrder;
//
factory = new GenericFactory<V>(type);
defaultSize = primeSize(capacity);
if (capacity >= 0) {
elementCount = 0;
elementData = newElementArray(defaultSize);
loadFactor = 0.75f; // Default load factor of 0.75
initCache(elementData.length);
computeMaxSize();
} else {
throw new IllegalArgumentException();
}
// Initializes the chain. 初始化链表
initChain();
}
@SuppressWarnings("unchecked")
private IntLinkedEntry<V>[] newElementArray(int s) {
return new IntLinkedEntry[s];
}
/**
* Removes all mappings from this hash map, leaving it empty.
*
* @see #isEmpty
* @see #size
*/
public void clear() {
clear(true);
}
public void clear(final boolean shrink) {
clearCache();
if (elementCount > 0) {
elementCount = 0;
}
if (shrink && (elementData.length > 1024) && (elementData.length > defaultSize)) {
elementData = newElementArray(defaultSize);
}
else {
Arrays.fill(elementData, null);
}
computeMaxSize();
initChain();
}
private void initChain() {
// Initializes the chain.
//双向链表的头指针不存储条目. 初始化时, 头指针,它的before,after都指向自己
header = new IntLinkedEntry<V>(-1);
header.before = header.after = header;
}
private void computeMaxSize() {
threshold = (int) (elementData.length * loadFactor);
}
/**
* Returns the value of the mapping with the specified key.
*
* @param key the key.
* @return the value of the mapping with the specified key, or {@code null}
* if no mapping for the specified key is found.
*/
public V get(final int key) {
final int index = (key & 0x7FFFFFFF) % elementData.length;
IntLinkedEntry<V> m = elementData[index];
while (m != null) {
if (key == m.key) {
//如果按照访问的顺序迭代map的元素. 则访问一次,这个元素会被加入到链表的尾部.
//注意: 链表的header之后的是最近不常访问的条目, 链表的尾部是最近访问的元素.
if (accessOrder) {
//if (log.isDebugEnabled()) log.debug("reliking " + this.key);
//先把这个元素删除掉
m.remove();
//然后加入到链表尾部. 这样这个元素就变成了最近访问的元素了
m.addBefore(header); //使当前元素m成为header的before. 即before.after=m. m is the last element of LHM
}
return m.value;
}
//访问时,还是通过数组, 而不是通过双向链表. 双向链表用在迭代获取所有条目时使用:从头指针遍历链表
//因为数组可以直接定位到key对应的index, 然后再从数组索引的链表(不是双向链表)里再循环找出key对应的条目.
m = m.nextInSlot;
}
return null;
}
/**
* Returns whether this map is empty.
*
* @return {@code true} if this map has no elements, {@code false} otherwise.
* @see #size()
*/
public boolean isEmpty() {
return (elementCount == 0);
}
/**
* Maps the specified key to the specified value.
*
* @param key the key.
* @param value the value.
* @return the value of any previous mapping with the specified key or
* {@code null} if there was no such mapping.
*/
public V put(final int key, final V value) {
int index = (key & 0x7FFFFFFF) % elementData.length;
IntLinkedEntry<V> entry = elementData[index];
while (entry != null && key != entry.key) {
//遍历数组索引上的链表
entry = entry.nextInSlot;
}
if (entry == null) {
// Remove eldest entry if instructed, else grow capacity if appropriate
// header.before是最近的条目, header.after是最旧的条目
IntLinkedEntry<V> eldest = header.after; //最旧的条目
++elementCount;
if (removeEldestEntry(eldest)) { //如果我们的策略要求删除最旧的条目
remove(eldest.key);
} else {
if (elementCount > threshold) {
rehash();
index = (key & 0x7FFFFFFF) % elementData.length;
}
}
entry = createHashedEntry(key, index);
}
V result = entry.value;
entry.value = value;
return result;
}
IntLinkedEntry<V> createHashedEntry(final int key, final int index) {
IntLinkedEntry<V> entry = reuseAfterDelete();
if (entry == null) {
entry = new IntLinkedEntry<V>(key);
}
else {
entry.key = key;
entry.value = null;
}
//创建的新的条目作为数组索引的链表表头, 同时也要加到双向链表的表尾
entry.nextInSlot = elementData[index]; //当前条目的下一个条目是原先数组索引的第一个条目
elementData[index] = entry; //现在当前条目作为数组索引的第一个条目了
entry.addBefore(header); // LinkedList 加入到双向链表的表尾
return entry;
}
void rehash(final int capacity) {
final int length = primeSize(capacity == 0 ? 1 : capacity << 1);
if (log.isDebugEnabled()) log.debug(this.getClass().getName() + "::rehash() old=" + elementData.length + " new=" + length);
IntLinkedEntry<V>[] newData = newElementArray(length);
for (int i = 0; i < elementData.length; i++) {
IntLinkedEntry<V> entry = elementData[i];
while (entry != null) {
int index = (entry.key & 0x7FFFFFFF) % length;
IntLinkedEntry<V> next = entry.nextInSlot;
entry.nextInSlot = newData[index];
newData[index] = entry;
entry = next;
}
}
elementData = newData;
computeMaxSize();
}
void rehash() {
rehash(elementData.length);
}
/**
* Removes the mapping with the specified key from this map.
*
* @param key the key of the mapping to remove.
* @return the value of the removed mapping or {@code null} if no mapping
* for the specified key was found.
*/
public V remove(final int key) {
//key对应的条目
IntLinkedEntry<V> entry = removeEntry(key);
if (entry == null) return null;
V ret = entry.value;
reuseAfterDelete(entry);
return ret;
}
public V removeEldest() {
final IntLinkedEntry<V> eldest = header.after;
V ret = eldest.value;
remove(eldest.key);
return ret;
}
IntLinkedEntry<V> removeEntry(final int key) {
IntLinkedEntry<V> last = null;
final int index = (key & 0x7FFFFFFF) % elementData.length;
IntLinkedEntry<V> entry = elementData[index];
while (true) {
if (entry == null) return null;
//找到了要删除的条目entry
if (key == entry.key) {
if (last == null) {
elementData[index] = entry.nextInSlot;
} else {
last.nextInSlot = entry.nextInSlot;
}
--elementCount;
//从双向链表中删除条目
entry.remove();
return entry;
}
//数组索引的链表,如果没有找到,则使用next指针右移
//同时要保存last, 因为找到要删除的条目后,last要指向删除条目的下一个条目.
last = entry;
entry = entry.nextInSlot;
}
}
/**
* Returns the number of elements in this map.
*
* @return the number of elements in this map.
*/
public int size() {
return elementCount;
}
// ========== Entry Cache
/*
private transient Entry<V> reuseAfterDelete = null;
private void initCache(int size) {}
private void clearCache() {}
private Entry<V> reuseAfterDelete() {
final Entry<V> ret = reuseAfterDelete;
reuseAfterDelete = null;
return ret;
}
private void reuseAfterDelete(final Entry<V> entry) {
entry.clean();
reuseAfterDelete = entry;
}
*/
private ArrayDeque<IntLinkedEntry<V>> cache;
private void initCache(final int size) {
cache = new ArrayDeque<IntLinkedEntry<V>>(size);
}
public void clearCache() {
cache.clear();
}
//添加条目时, 如果有空闲空间,则优先使用空闲空间, 而不用重新创建新的条目
private IntLinkedEntry<V> reuseAfterDelete() {
final IntLinkedEntry<V> reuse = cache.pollLast();
if (reuse != null) {
if (log.isDebugEnabled()) log.debug("reusing IntLinkedEntry<V>=" + reuse.hashCode() + " cacheSize=" + cache.size());
}
return reuse;
}
//删除后, 条目占用的空间会被加入到缓存列表中
private void reuseAfterDelete(final IntLinkedEntry<V> entry) {
entry.clean();
cache.offerLast(entry);
}
// ========== Internal Entry
protected static final class IntLinkedEntry<V> {
// These fields comprise the doubly linked list used for iteration.
//双向链表的前一个,后一个条目的指针
private IntLinkedEntry<V> before, after;
//数组索引中的链表的下一个条目的指针
private IntLinkedEntry<V> nextInSlot;
protected int key;
protected V value;
IntLinkedEntry(final int theKey) {
this.key = theKey;
this.value = null;
}
private void clean() {
value = null;
key = Integer.MIN_VALUE;
nextInSlot = null;
before = null;
after = null;
}
/**
* Removes this entry from the linked list.
* ____ ____ ____
* | | <--> | | <--> | |
* before entry after
*
* __________________________
* _|__ ____ _ |_
* | | | | | |
* before entry after
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
* Inserts this entry before the specified existing entry in the list.
* 将当前条目插入到指定的条目之前. 即指定条目的before指针指向当前条目
*
* 在put和get时, 会将当前条目(this)加入到双向链表的尾部, 即header(existingEntry)的前面
* ____________________________
* _|__ ____ __|_ ____
* | | <--> | | <--> | | | |
* header 1st 2nd entry
* after before
* entry.after entry.before
*
* _________after.before=this______________
* _|__ ____ ____ __|_
* | | <--> | | <--> | | --> | |
* header 1st 2nd entry
* before.after=this
*
* 添加一个条目, 一共有4个指针.
* 1. 当前条目到前一个条目 before
* 2. 前一个条目到当前条目 before.after=this
* 3. 当前条目到下一个条目 after
* 4. 下一个条目到当前条目 after.before=this
*/
private void addBefore(IntLinkedEntry<V> existingEntry) {
//设置当前条目的after, before. 即从当前条目触发的指针
after = existingEntry; //③
before = existingEntry.before; //①
//设置其他条目到当前条目的指针.
before.after = this; //②
after.before = this; //④
}
/**
* Returns the key corresponding to this entry.
*
* @return the key corresponding to this entry
*/
public int getKey() {
return key;
}
/**
* Returns the value corresponding to this entry.
*
* @return the value corresponding to this entry
*/
public V getValue() {
return value;
}
}
// ========== Linked List
/**
* Returns <tt>true</tt> if this map should remove its eldest entry.
* This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
* inserting a new entry into the map. It provides the implementor
* with the opportunity to remove the eldest entry each time a new one
* is added. This is useful if the map represents a cache: it allows
* the map to reduce memory consumption by deleting stale entries.
*
* <p>Sample use: this override will allow the map to grow up to 100
* entries and then delete the eldest entry each time a new entry is
* added, maintaining a steady state of 100 entries.
* <pre>
* private static final int MAX_ENTRIES = 100;
*
* protected boolean removeEldestEntry(IntLinkedEntry eldest) {
* return size() > MAX_ENTRIES;
* }
* </pre>
*
* <p>This method typically does not modify the map in any way,
* instead allowing the map to modify itself as directed by its
* return value. It <i>is</i> permitted for this method to modify
* the map directly, but if it does so, it <i>must</i> return
* <tt>false</tt> (indicating that the map should not attempt any
* further modification). The effects of returning <tt>true</tt>
* after modifying the map from within this method are unspecified.
*
* <p>This implementation merely returns <tt>false</tt> (so that this
* map acts like a normal map - the eldest element is never removed).
*
* @param eldest The least recently inserted entry in the map, or if
* this is an access-ordered map, the least recently accessed
* entry. This is the entry that will be removed it this
* method returns <tt>true</tt>. If the map was empty prior
* to the <tt>put</tt> or <tt>putAll</tt> invocation resulting
* in this invocation, this will be the entry that was just
* inserted; in other words, if the map contains a single
* entry, the eldest entry is also the newest.
* @return <tt>true</tt> if the eldest entry should be removed
* from the map; <tt>false</tt> if it should be retained.
*/
protected boolean removeEldestEntry(IntLinkedEntry<V> eldest) {
return false;
}
// ========== Prime Finder
private static final int primeSize(final int capacity) {
//return java.math.BigInteger.valueOf((long)capacity).nextProbablePrime().intValue();
return PrimeFinder.nextPrime(capacity);
}
// ========== Iterator
/**
* @returns iterator over values in map
*/
public Iterator<V> iterator() {
return new IntLinkedHashMapIterator<V>(this);
}
static class IntLinkedHashMapIterator<V> implements Iterator<V> {
final IntLinkedHashMap<V> associatedMap;
IntLinkedEntry<V> nextEntry = null; //下一个条目
IntLinkedEntry<V> lastReturned = null;
public IntLinkedHashMapIterator(final IntLinkedHashMap<V> associatedMap) {
this.associatedMap = associatedMap;
//初始化时,指向第一个条目. 即header的after
nextEntry = associatedMap.header.after;
}
//从header开始遍历链表, 通过after指针右移. 链表的最后一个条目的after是head
//只要下一个条目不是head, 就说明还有下一个条目!
public boolean hasNext() {
return nextEntry != associatedMap.header;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
associatedMap.remove(lastReturned.key);
lastReturned = null;
}
IntLinkedEntry<V> nextEntry() {
if (nextEntry == associatedMap.header)
throw new NoSuchElementException();
IntLinkedEntry<V> e = lastReturned = nextEntry;
//通过after指针指向下一个条目
nextEntry = e.after;
return e;
}
public V next() { return nextEntry().value; }
}
// =========================================
public V[] getValues() {
final V[] array = factory.newArray(elementCount);
int i = 0;
for (final V v : this) {
array[i++] = v;
}
return array;
}
// =========================================
public static void main(String[] args) {
IntLinkedHashMap<Integer> hash = new IntLinkedHashMap<Integer>(16, Integer.class, true) {
/*protected boolean removeEldestEntry(IntLinkedEntry<Integer> eldest) {
System.out.println("---- begin");
for (Integer i : this) {
System.out.println(i);
}
System.out.println("---- end");
return (size() > 3);
}*/
};
for (int i = 1; i < 6; i++) { // 1...4
hash.put(i, i);
}
hash.put(3, 3);
hash.put(3, 3);
hash.put(3, 3);
hash.put(3, 3);
hash.get(3);
//hash.remove(3);
for (Integer i : hash) {
System.out.println(i);
}
System.out.println("---");
while (hash.size() > 0) {
System.out.println("remove value=" + hash.removeEldest());
}
System.out.println("---");
for (Integer i : hash) {
System.out.println(i);
}
}
}