Map:
1、HashMap
2、LinkedHashMap
3、IdentityHashMap
4、WeakHashMap
5、TreeMap
6、EnumMap
7、ConcurrentHashMap
8、ConcurrentSkipListMap
今天主要学习的是LinkedHashMap。
1、LinkedHashMap
LinkedHashMap是继承HashMap的一个子类,所有存储的结构也是Entry[],同样是以key的hashCode值来计算key-value所在的Entry[]桶所在的下标。来实现快速的存放和查询。但是LinkedHashMap除了继承父类HashMap的功能以外,解决了HashMap无序的问题。
LinkedHashMap遍历元素的时候,遍历的顺序是以数据的存入的顺序。
因为LinkedHashMap在HashMap的Entry[]对象中,继承Entry对象扩充了自己的Entry对象能力,增加了before和after属性,使得数据存入以后,成为一个双向链表的形式。因为双向链表的处理,所以LinkedHashMap的数据写入性能没有HashMap高。
LinkedHashMap的数据写入也是调用父类的put方法(已经在HashMap里面介绍了put方法了),而重写了addEntry方法。对于Entry对象的处理,主要在于建立双向链表,使得由header对象开始,与每次加入的对象成为双向链表。
/**
* This override alters behavior of superclass put method. It causes newly
* allocated entry to get inserted at the end of the linked list and
* removes the eldest entry if appropriate.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex);
// Remove eldest entry if instructed, else grow capacity if appropriate
Entry eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
if (size >= threshold)
resize(2 * table.length);
}
}
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry old = table[bucketIndex];
Entry e = new Entry(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}
/**
* Inserts this entry before the specified existing entry in the list.
*/
private void addBefore(Entry existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
2、LinkedHashMap的数据获取
LinkedHashMap的get方法其实和HashMap的get方法实现是一致的,但是HashMap的get方法,在null值的判断上处理了两次,一次是get方法中,一次在底层调用getEntry方法的时候也判断了一次,而LinkedHashMap对这里进行了优化,只在getEntry中去判断就可以了。
LinkedHashMap中key值为null的情况,仍然和HashMap一样放在Entry[0]中,也就是桶的首位。
特别之处在于accessOrder为true的情况,后面再讲。
3、LinkedHashMap的数据遍历
LinkedHashMap也是一般通过keySet方法进行key值的遍历。而LinkedHashMap的迭代器并不是像HashMap一样先遍历桶(Entry[])的,而是由header元素开始慢慢的next读取after对象。所以读取的顺序和写入的顺序一致。
private abstract class LinkedHashIterator implements Iterator {
Entry nextEntry = header.after;
Entry lastReturned = null;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
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 nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (nextEntry == header)
throw new NoSuchElementException();
Entry e = lastReturned = nextEntry;
nextEntry = e.after;
return e;
}
}
3、LinkedHashMap的特别属性
LinkedHashMap虽然继承了HashMap的结构存储数据,但是除了HashMap的相关属性以外,还特别添加了:
3.1、Header属性,来存储双向链表的关系。
3.2、accessOrder属性,这个属性主要是控制链表的顺序的,如果accessOrder为false(默认为false),遍历LinkedHashMap的时候,返回的数据顺序为写入的顺序,如果为true,则返回数据的顺序为读取的顺序。
注意:如果accessOrder为true,get方法中也在修改LinkedHashMap,所以不能一边遍历LinkedHashMap,一边从LinkedHashMap中get获取对象,否则会抛出ConcurrentModificationException
下面的例子主要是针对accessOrder为true的证实
public class TestLinkedHashMap {
/**
* @param args
*/
public static void main(String[] args) {
LinkedHashMap hm1 = new LinkedHashMap(16, 0.75f,true);
hm1.put(1,"1");
hm1.put(2,"2");
hm1.put(3,"3");
hm1.put(4,"4");
hm1.put(5,"5");
hm1.get(2);
hm1.get(4);
hm1.get(3);
hm1.get(5);
hm1.get(1);
Iterator iter1 = hm1.keySet().iterator();
while(iter1.hasNext()){
System.out.println(iter1.next());
}
}
}