HashMap:
开发中用得更多的当然是HashMap,在Map中插入、删除和获取元素,HashMap是比较好的选择。它根据key的hashcode值进行数据存储,根据key可以直接获取对应的value,在效率上也是非常快的。HashMap允许一个为null的key,至于value可以有多个null。但是如果需要进行多线程的并发写HashMap,就可能造成数据的不一致情况,这个时候可以使用Collections.synchronizedMap的方式,或者使用Hashtable,或者更高级的ConcurrentHashMap,这个是jdk1.5出现的并发包下的,主要用来解决并发的Map操作。
注:ConcurrentHashMap的性能是优于Hashtable和Collections.synchronizedMap的。
LinkedHashMap:
顾名思义多了一层链表操作,既然有了链表,那必然就能找到插入数据的顺序了,也就是插入的数据,在遍历取出时,是于插入的顺序一致的,其他也具有map的特性。原因如下:
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
{
private static final long serialVersionUID = 3801124242820219131L;
private transient Entry<K,V> header;
private final boolean accessOrder;
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
}
它本身就是从HashMap继承而来,并且实现了Map接口,LinkedHashMap的实现基本和HashMap一致,但是他多一个header属性,这个属性就是用来额外存储顺序的。
linkedHashMap部分源码
void init() {
header = new Entry<>(-1, null, null, null);
header.before = header.after = header;
}
@Override
void transfer(HashMap.Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e = header.after; e != header; e = e.after) {
if (rehash)
e.hash = (e.key == null) ? 0 : hash(e.key);
int index = indexFor(e.hash, newCapacity);
e.next = newTable[index];
newTable[index] = e;
}
}
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;
}
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;
}
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// These fields comprise the doubly linked list used for iteration.
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;
}
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
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;
}
}
从源码上可以看出,之所有会存在插入和取出顺序,还是由于链表的左右,before,after.
在工作中,使用得linkedHashMap更多的是为了保持与某个数组或者集合的顺序一致,同时又构建比较复杂key-value型的数据结构,此时一般就会用到这个了。
他其实与HashMap没有什么区别,不同的是他定义了一个Entry<K,V> header,这个header并没有放在table里面,而是单独出来的。
LinkedHashMap通过继承HashMap的Entry并添加了两个属性Entry<K,V> before, after和header集合起来组成一个双向链表,来实现插入顺序排序和访问顺序排序。