HashMap是Map接口的实现类
一、存储方式
采用KV键值对方式存储,基于哈希表(Hash Table)设计:
当链表长度大于阈值(默认为8)+ 数组长度大于64时,将链表转化为红黑树,以减少搜索时间
二、扩容机制(扩容方法是resize()方法)
●初始容量用来规定哈希表数组的长度
默认为16,因为16是2的整数次幂的原因,在小数据量的情况下,能减少哈希冲突,提高性能。在存储大容量数据的时候,最好预先判断数据量,按照2的幂次方,提前预设初始容量;●加载因子用来表示哈希表中元素的填满程度,默认为0.75
越大则表示允许填满的元素就越多,哈希表的空间利用率就越高,但是冲突的机会增加。反之越小则冲突的机会就会越少,但是空间很多就浪费。
1.线程不安全
JDK1.7,当并发执行扩容操作时,会造成环形链,导致死循环和数据丢失
JDK1.8,解决了环形链问题,但是在执行put操作时,会发生数据覆盖
2.性能高
3.允许Null key和Null value,Null Key只允许有1个,Null value 可以有多个
四、遍历方法
1.可以采用keySet()+for循环的方法来遍历,keySet()返回的是一个Key值的集合
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
for(String key:map.keySet()){
system.out.println("key:"+key);
system.out.println("value:"+map.get(key));
}
2.采用EntrySet()+Iterator进行遍历,EntrySet()返回的是一个Map.Entry的一个集合,它提供getKey(),getValue()方法来获取键值对。
Iterator< Map.Entry<String,Integer>> it=map.EntrySet().iterator();
while(it.hasNext()){
Map.Entry<String,Integer> entry=it.next();
system.out.println("key:"+entry.getKey());
system.out.println("value:"+entry.getValue());
}
3.采用EntrySet()+for循环进行遍历,EntrySet()返回的是一个Map.Entry的一个集合,它提供getKey(),getValue()方法来获取键值对。
Set<Entry<String, Integer>> entrySet = map.entrySet();
for (Entry<String, Integer> entry : entrySet) {
System.out.println("key"+ entry.getKey());
System.out.println("value"+entry.getValue());
}
简写方式:
for (Entry<String, Integer> entry : map.entrySet()) {
System.out.println("key" + entry.getKey());
System.out.printf("value"+entry.getValue());
System.out.println();
}
五、构造方法
//创建一个空的哈希表,初始容量为 16,加载因子为 0.75
public HashMap()
//创建一个空的哈希表,指定容量,使用默认的加载因子
public HashMap(int initialCapacity)
//创建一个空的哈希表,指定容量和加载因子
public HashMap(int initialCapacity, float loadFactor)
//创建一个内容为参数 m 的内容的哈希表
public HashMap(Map<? extends K, ? extends V> m)
六、put()添加方法
public V put(K key, V value) {
// 对key的hashCode()做hash
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//tab为空则创建
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//计算index,并对null做处理
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//节点key存在,直接覆盖value
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//判断该链为红黑树
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
//该链为链表
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key,value,null);
//链表长度大于8+数组长度大于64时转换为红黑树进行处理
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// key已经存在直接覆盖value
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) break;
p = e;
}
}
if (e != null) {
// existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
// 超过最大容量,就调用resiza()方法进行扩容
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
部分转载
图片来自:https://blog.csdn.net/qq_36711757/article/details/80394272?
特别希望本文可以对你有所帮助,感谢你留个赞和关注,道阻且长,我们并肩前行!
转载请注明出处。感谢大家留言讨论交流。