HashMap介绍

 1.哈希表:

 

(1)说哈希表之前不得说一下算法,我们都知道评价一个好的算法主要要从时间空间复杂度来进行衡量。

时间复杂度就是执行算法所需要的时间,而空间付再度就是该算法所占的内存空间。两者均体现了计算机的资源的概念。毋庸置疑,对于一个数组我们都清楚,查找一个元素只要知道索引位置就可以了,因此,时间复杂度为O(1),然而对于一个链表其时间复杂度为O(n)。

 

(2)理想的情况就是不需要比较一次就能得到所查的记录,换言之,在记录的存储位置与他的关键字k之间建立一个确定的对应关系f,使得每一个关键字和结构中的一个位置唯一的对应,即f(k)。此时,称f为哈希函数。用该函数建的表为哈希表。

 

(3)几个基本概念:

冲突:不同关键字得到相同而的哈希值。

 

散列:散列函数:将数据的hashCode映射到表中的位置,这一映射过程叫做哈希造表或散列,此过程不需给出冲突解决方案。

 

好的散列函数的2个必备条件:1,快捷,在O(1)时间内运行;2,均匀的分布hashCode,填充概率相同。

 

哈希地址:根据关键字所得到的记录的存储位置。

 

冲突解决方案:当一个新项散列到已经被占据的散列表中的位置时,被告之发生冲突,解决方案用于确定新项可以插入散列表中未被占据的位置。

    解决冲突主要的主要方法:开放寻址方法(寻找另外的空位);封闭寻址方法(吊挂另一种数据结构)

一般采用后者挂链表的方式。

 

再散列:当数据的容量大于散列表的容量的容量时,那么创建一张指定新容量的表,再将原来表中的数据映射到新表中。

 

2.关于HashMap的简单介绍:

 

HashMap 的实例有两个参数影响其性能:初始容量加载因子容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

 

3.HashMap存储

HashMap<String ,int> hp = new HashMap<String,int>();

hp.put(“张三",20);

hp.put("李四",21);

 

4.HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置。
当程序执行 hp.put("张三" , 20); 时,系统将调用"张三"的 hashCode() 方法得到其 hashCode 值。每个对象都有 hashCode() 方法,都可通过该方法获得它的 hashCode 值。得到这个对象的 hashCode 值之后,系统会根据该 hashCode 值来决定该元素的存储位置。

 

 

参考put();方法的源码:

 

 HashMap 的每个 bucket 里存储的 Entry 只是单个 Entry ——也就是没有通过指针产生 Entry 链时,此时的 HashMap 具有最好的性能:当程序通过 key 取出对应 value 时,系统只要先计算出该 key 的 hashCode() 返回值,在根据该 hashCode 返回值找出该 key 在 table 数组中的索引,然后取出该索引处的 Entry,最后返回该 key 对应的 value 即可.

 

public V put(K key, V value){
   
// 如果 key 为 null,调用 putForNullKey 方法进行处理  
 if (key == null)   
    return putForNullKey(value);
  
//每个key.hashCode()返回一个hash值  
int hash = hash(key.hashCode());
  
// 根据key.hashCode()的值来找记录的位置  
  int i = indexFor(hash, table.length);
 
// 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素  
 for (Entry<K,V> e = table[i]; e != null; e = e.next)   
 {   
     Object k;
   
     // 找到指定 key 与需要将要存入的 key 相等 ,将   
     if (e.hash == hash && ((k = e.key) == key   
         || key.equals(k)))   
     {   
         V oldValue = e.value;   
         e.value = value;   
         e.recordAccess(this);   
         return oldValue;   
     }   
 }   
 // 如果 i 索引处的 Entry 为 null,表明此处还没有 Entry   
 modCount++;   
 // 将 key、value 添加到 i 索引处  
 addEntry(hash, key, value, i);   
 return null;
}   
  (1)  Entry就是HashMap存储数据所用的类,相当于链表的节点;每个 Map.Entry 其实就是一个 key-value 对;我们完全可以把 Map 集合中的 value 当成 key 的附属,当系统决定了 key 的存储位置之后,value 随之保存在那里即可。


(2)&按位取并的操作符,前提是length为2的n次幂。

 

public  void addEntry(int hash, K key, V value, int bucketIndex){
  
    // 获取指定 bucketIndex 索引处的 Entry   
    Entry<K,V> e = table[bucketIndex];
 
    // 将新创建的 Entry 放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry   
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
   
    // 如果 Map 中的 key-value 对的数量超过了极限
if (size++ >= threshold) 

  
        //  table 对象的长度扩充到 2 倍。  
        resize(2 * table.length);


}


系统总是将新添加的 Entry 对象放入 table 数组的 bucketIndex 索引处,如果 bucketIndex 索引处已经有了一个 Entry 对象,那新添加的 Entry 对象指向原有的 Entry 对象(产生一个 Entry 链)。

 

当 HashMap 的每个 bucket 里存储的 Entry 只是单个 Entry ——也就是没有通过指针产生 Entry 链时,此时的 HashMap 具有最好的性能:当程序通过 key 取出对应 value 时,系统只要先计算出该 key 的 hashCode() 返回值,在根据该 hashCode 返回值找出该 key 在 table 数组中的索引,然后取出该索引处的 Entry,最后返回该 key 对应的 value。

 

参考一下get()方法的源码:

public V get(Object key){
   
// 如果 key 是 null,调用 getForNullKey 取出对应的 value   
if (key == null)   
     return getForNullKey();
   
 // 根据该 key 的 hashCode 值计算它的 hash 码  
 int hash = hash(key.hashCode());
  
 // 直接取出 table 数组中指定索引处的值,  
 for (Entry<K,V> e = table[indexFor(hash, table.length)];   
     e != null;   
     // 搜索该 Entry 链的下一个 Entr   
     e = e.next)           
 {   
     Object k;
   
     // 如果该 Entry 的 key 与被搜索 key 相同
      if (e.hash == hash && ((k = e.key) == key   
         || key.equals(k)))   
         return e.value;   
 }   
 return null;

 

 (1)HashMap 的每个 bucket 里只有一个 Entry 时,HashMap 可以根据索引、快速地取出该 bucket 里的 Entry;在发生“Hash 冲突”的情况下,单个 bucket 里存储的不是一个 Entry,而是一个 Entry 链,系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止,如果恰好要搜索的 Entry 位于该 Entry 链的最末端(该 Entry 是最早放入该 bucket 中),那系统必须循环到最后才能找到该元素。

 

(2)如果 HashMap 中 Entry 的数量一直不会超过极限容量(capacity * load factor),HashMap 就无需调用 resize() 方法重新分配 table 数组,从而保证较好的性能。当然,开始就将初始容量设置太高可能会浪费空间(系统需要创建一个长度为 capacity 的 Entry 数组)。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值