hashmap的使用

(1)为什么要学习hashmap?

hashmap的增删改查时间复杂度都能低至O(1)

(2) hashmap原理

hashmap通过数组和链表,实现对元素的存储,从而可以高速的访问元素值

(4)什么是哈希表

哈希表也叫散列表,哈希表是一种数据结构,提供了快速的插入操作和查找操作,

无论哈希表有多少条数据,他的增删改查的时间复杂度都是O(1),但是哈希表也有缺点,他是基于数组实现的,所以他的扩容效率很慢,

哈希表采用的是一种转换思想,其中一个重要的概念就是如何将键或者关键字

转换为数组下标,这个过程由哈希函数完成,并不是所有的键或者关键字都需要转换,对于int类型的键,则可以直接使用

(5)什么是哈希函数?

哈希函数的作用是帮我们把非int的「键」或者「关键字」转化成int,可以用来做数组的下标。比如我们上面说的将学生的姓名作为「键」或者「关键字」,这是就需要哈希函数来完成。

ex:

  static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

「HashMap」中利用了「hashCode」来完成这个转换。哈希函数不管怎么实现,都应该满足下面三个基本条件:

  • 散列函数计算得到的散列值是一个非负整数
  • 如果 key1 = key2,那 hash(key1) == hash(key2)
  • 如果 key1 ≠ key2,那 hash(key1) ≠ hash(key2)

结论:任意一个键值通过哈希函数,有且生成唯一的一个index值,并且大于或等于0 ,但是实际上第三条很难保证,因为我们是把最后生成的索引值放在一个较小的数组空间,在这种情况下,很容易出现哈希冲突;

(6)哈希冲突

哈希冲突,不同的键值通过哈希函数生成了相同的索引值

常用的解决哈希冲突的方法:

  • 开放地址法

        1、线性探测法:当发生冲突时,就顺序查看下一个存储位置,如果写一个位置还有冲突,继续往后找,知道最后一个位置还是冲突的话,就从头开始找。

        2、平方探测法:平方探测法的规定是以1^2,-1^2, 2^2, -2^2,...,探测新的存储位置是否能存储元素。

        3、再散列法:利用两个散列函数,当通过第一个散列函数得到关键字的存储地址发生重冲突时,再利用第二个散列函数计算出地址增量,地址计算方式如下:Hi = (Hash(key)+                                 i*Hash2(key) % p;

        4、伪随机数法: 当发生地址冲突时,加入一个随机数作为地址增量寻找新的存储地址,地址计算方式如下 Hi = (Hash(key)+ di) p;

  • 线性拉链法

将通过哈希函数生成相同索引值的元素连成一个单链表,m个索引值就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。

拉链结构如下:

(7)C++ hashmap开发

1、导入头文件 #include<unordered_map>

2、哈希表的声明和初始化

unordered_map var_name;

unordered_map hmap{ {1,10}, {2, 20}, {3, 30}};

unordered_map hmap{ {{1,10}, {2, 20}, {3, 30}}, 3};

3、添加元素

hmap[4] = 40;

hmap[5] = 50;

hmap.insert({6,60}); //insert可以避免覆盖问题,同一个键值只能插入一次,第二次无效

unordered_map hmap2(hmap);

4、常用函数

  • begin()函数, 该函数返回有一个指向哈希表开始位置的迭代器
  • end()函数,指向哈希表结尾位置的下一个元素的迭代器
  • cbegin()函数和cend()函数:这两个函数和begin()函数和 end()函数功能相同,唯一的区别是cbegin()和cend()函数是面向不可变的哈希表,也就是const 声明的哈希表。
  • empty()函数:判断哈希表是否为空,空则返回true,非空返回false
  • size()函数: 返回哈希表的大小
  • erase()函数:删除某个位置的元素,或者删除某个位置到某个位置这一范围的元素,或者传入key值,删除键值对
  • at()函数:根据key查找哈希表中的元素对应的值
  • clear()函数:清空哈希表中的元素
  • find()函数,以key作为参数寻找哈希表中的元素,如果哈希表中存在该key值,则返回该位置上的迭代器,否则返回end迭代器。
  • bucket()函数,以key寻找哈希表中该元素储存的bucket编号(unordered_map的源码是基于拉链式的哈希表,所以是通过一个bucket存储元素)
  • bucket_count()函数:该函数返回哈希表中存在的存储桶总数(一个存储桶可以用来存放多个元素,也可以不存放元素,一般bucket的个数大于等于元素个数)
  • count()函数: 统计某个key值对应的元素个数,因为unordered_map不允许重复元素,所以count()函数返回的结果是0或者1
  • 哈希表的遍历,通过迭代器遍历。

(7)hashmap是线程不安全的,因为它的操作函数都没有加锁,多线程操作时容易抛出异常;为了解决这个问题,推出了concurrenthashmap, 他是线程安全的hashmap,专门用于多线程环境。相比于hashtable在put方法上加上锁,保证插入时阻塞其他线程的插入操作,concurrenthashmap是通过原子操作和局部加锁的方法保证了多线程的线程安全,尽可能减少了性能损耗。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值