您的位置:首页 > 其它

Map集合及HashMap源码分析

2017-05-18 16:24 549 查看




Map主要学习HashMap,TreeMap,HashTable

HashMap

因为HashMap是基于hash表实现的,所以不会hash数据结构的可以参考如下链接,hashMap是采用的链地址法来解决冲突

点击打开链接

以下面一段代码来分析hashMap源码

public void Test(){
HashMap<String,String> hashMap = new HashMap<String, String>();
hashMap.put("1", "m");
hashMap.put("2", "a");
hashMap.put("3", "p");
System.out.println(hashMap.get("1"));
}


构造函数:

public HashMap(int initialCapacity, float loadFactor) {
//检测参数,loadFactor表示装载因子,因为HashMap采用的链地址法,所以装载因子可以是1,
// 但是会影响性能,默认选择为0.75
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);

// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;

this.loadFactor = loadFactor;
//该map中装载的最大数量
threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
//创建数组
table = new Entry[capacity];
useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
init();
}


put(K key, V value)

首先查找map中有没该元素,如果有,则直接替换其值,如果没有找到,则通过addEntry(int hash, K key, V value, int bucketIndex)插入数据,把这个值插入到链表的头部

public V put(K key, V value) {
if (key == null)//插入key为null的值
return putForNullKey(value);
int hash = hash(key);//获得hash值
int i = indexFor(hash, table.length);//获得在table中的索引
//迭代查找该元素在链表中要插入的位置
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如果该map中已经有该key存在,那么替换其值即可
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}

modCount++;
//如果没有此值,则已经找到位置,插入元素
addEntry(hash, key, value, i);
return null;
}


get(Object key)

public V get(Object key) {
if (key == null)
return getForNullKey();
//通过getEntry来查找
Entry<K,V> entry = getEntry(key);

return null == entry ? null : entry.getValue();
}

final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key);//得到hash值
//遍历此节点位置的链表
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}


remove(Object key)

public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}

final Entry<K,V> removeEntryForKey(Object key) {
//获取hash值
int hash = (key == null) ? 0 : hash(key);
//得到其索引
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
//遍历整个链表
while (e != null) {
Entry<K,V> next = e.next;
Object k;
//找到要删除的值
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)//表名删除的是链表的头
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}

return e;
}

与HashMap相对应的还有HashTable,HashMap和HashTable有什么区别

看一下HashTable源码:

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
可以看到,HashTable和HashMap一样,都是实现Map来实现的

来看put方法:

/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
*
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one
* @exception NullPointerException if the key or value is
* <code>null</code>
* @see Object#equals(Object)
* @see #get(Object)
*/
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}

// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}

modCount++;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();

tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
}

// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
return null;
}

从上面可以看到,增加了synchronized关键字,所以是线程安全的,还有注释说到,其key和value不能为空

所以HashMap和HashTable的区别如下:

1.HashTable是线程安全的,HashMap不是线程安全的

2.HashMap可以存储key和value为null的值,HashTable不能

3.还有一个区别HashMap的Iterator是fail-fast机制的,所以HashMap在迭代过程中,如果有集合有被修改,那么将会抛出ConcurrentModificationException异常,

而Hashtable的enumerator迭代器不是fail-fast.

TreeMap

TreeMap使用红黑树来实现,是一种有序的数据结构,因为总是处于一种平衡的状态,所以没有调优因子。

TreeMap和HashMap的区别

HashMap是hash表来实现,TreeMap使用红黑树来实现

TreeMap是有序的,HashMap是无序的

怎么选择使用HashMap还是TreeMap了

如果插入和更新都比较频繁的话,那么保证元素的有序可以提高快速和频繁查找的性能。如果对于排序操作(例如产生一个报表合作者运行一个批处理程序)的要求不是很频繁的话,那么把数据以无序的方式存储,然后在需要排序的时候用Collections.sort(…)来进行排序,会比用有序的方式来存储可能会更加高效
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: