什么是HashMap

HashMap是Map接口的实现类
一、存储方式
采用KV键值对方式存储,基于哈希表(Hash Table)设计:

  • JDK1.7 : 底层数据结构基于“数组”+“链表”
  • JDK1.8 : 底层数据结构基于“数组”+“链表”+“红黑树”

当链表长度大于阈值(默认为8)+ 数组长度大于64时,将链表转化为红黑树,以减少搜索时间

二、扩容机制(扩容方法是resize()方法)

  • 初始容量为16
  • 加载因子为0.75:当 元素个数超过容量长度的0.75倍 时,进行扩容
  • 按原有容量的2倍进行扩容

●初始容量用来规定哈希表数组的长度
默认为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()添加方法

  • 添加指定的键值对到 Map 中,如果已经存在,就替换 public V put(K key, V value)
    逻辑如下
  • 判断数组tab是否为空,如果为空进行初始化;
  • 如果不为空,使用hash方法计算key的hashCode ,通过(n -1) & hash计算应当存放在数组中的下标index ;
  • 查看tab[index]是否存在数据;
  • 如果没有数据,就构造个Node<K,V>节点 ,存放在tab[index]中;
  • 如果存在数据,说明发生哈希冲突,继续判断key是否相等;
  • 如果相等,用新的value替换原数据;
  • 如果不相等,判断当前节点类型是不是TreeNode<K,V>树型节点
  • 如果是树型节点,创造树型节点插入红黑树中;
  • 如果不是树型节点,创建普通Node<K,V>加入链表尾部;
  • 判断链表长度大于阈值(默认为8 )并且数组长度大于64,如果满足,链表转换为红黑树;如果不满足,数组扩容;
  • 最后,插入完成之后,判断当前节点数是否大于实际存储空间大小;
  • 如果大于,调用resize() ,按原数组的长度,扩容一倍。
  • JDK1.8HashMap的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?

特别希望本文可以对你有所帮助,感谢你留个赞和关注,道阻且长,我们并肩前行!

转载请注明出处。感谢大家留言讨论交流。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值