发布时间:2025-09-18 14:13:33 点击量:
HASH GAME - Online Skill Game GET 300
Map是一个集合,一种依照键(key)存储元素的容器,键(key)很像下标,在List中下标是整数。 在Map中键(key)可以使任意类型的对象。Map中不能有重复的键(Key),每个键(key)都有一个对应的值(value)。 一个键(key)和它对应的值构成map集合中的一个元素。 Map中的元素是两个对象,一个对象作为键,一个对象作为值。键不可以重复,但是值可以重复。 Map本身是一个接口,要使用Map需要通过子类进行对象实例化。 Map接口的常用子类有:HashMap、HashTable、TreeMap、ConcurrentHashMap。 (1)key值不允许重复,如果重复,则会把对应value值更新; (2)key和value都允许为null,key为null有且只有一个。
链地址在处理的流程如下: 添加一个元素的时候,首先计算元素key的hash值,确定插入数组中的位置。如果当前位置下没有重复数据,则直接添加到当前位置。当遇 到冲突的时候,添加到同一个hash值的元素后面,行成一个链表。这个链表的特点是同一个链表上的Hash值相同。java的数据结构 HashMap使用的就是这种方法来处理冲突,JDK1.8中,针对链表上的数据超过8条的时候,使用了红黑树进行优化。 ②开放地址法 开放地址法是指大小为 M 的数组保存 N 个键值对,其中 M N。我们需要依靠数组中的空位解决碰撞冲突。基于这种策略的所有方法被统 称为“开放地址”哈希表。线性探测法,就是比较常用的一种“开放地址”哈希表的一种实现方式。线性探测法的核心思想是当冲突发生时,顺序 查看表中下一单元,直到找出一个空单元或查遍全表。简单来说就是:一旦发生冲突,就去寻找下 一个空的散列表地址,只要散列表足够 大,空的散列地址总能找到。
4.什么是HashMap 1.HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry。 这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干。 HashMap由数组链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前 entry的next指向null),那么查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为 O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以, 性能考虑,HashMap中的链表出现越少,性能才会越好。
线性探测法的数学描述是:h(k, i) = (h(k, 0) i) mod m,i表示当前进行的是第几轮探查。i=1时,即是探查h(k, 0)的下一个;i=2,即是再下一 个。这个方法是简单地向下探查。mod m表示:到达了表的底下之后,回到顶端从头开始。 对于开放寻址冲突解决方法,除了线性探测方法之外,还有另外两种比较经典的探测方法,二次探测(Quadratic probing)和双重散列 (Double hashing)。但是不管采用哪种探测方法,当散列表中空闲位置不多的时候,散列冲突的概率就会大大提高。为了尽可能保证散列 表的操作效率,一般情况下,我们会尽可能保证散列表中有一定比例的空闲槽位。我们用装载因子(load factor)来表示空位的多少。 散列表的装载因子=填入表中的元素个数/散列表的长度。装载因子越大,说明冲突越多,性能越差。
5.HashMap的实现原理或者工作原理? HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方 法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。 (1)当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞 了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。 当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。 (2)当两个对象的hashcode相同会发生什么? 答:因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的 Map.Entry对象)会存储在链表中。” 解析:虽然有很多种处理碰撞的方法,这种方法是最简单的,也正是HashMap的处理方法。 (3)如果两个键的hashcode相同,你如何获取值对象? 答:当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,找到bucket位置之后,会调用keys.equals()方法去找到链 表中正确的节点,最终找到要找的值对象。
解析:HashMap在链表中存储的是键值对 (4)如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办? 答:将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。 解析:这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置 (5)你了解重新调整HashMap大小存在什么问题吗? 答:当重新调整HashMap大小的时候,会存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大 小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾 部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。 解析:跳出问题来看,为什么要在多线程的环境下使用HashMap呢?(多线程下不建议用HashMap) 一句话总结就是,并发环境下HashMap的rehash过程可能会带来循环链表,导致死循环致使线程挂掉。 因此并发环境下,建议使用current包中的ConcurrentHashMap以保证线程安全。 至于HashTable,它并未使用分段锁,而是锁住整个数组,高并发环境下效率非常的低,会导致大量线程等待。 同样的,Synchronized关键字、Lock性能都不如分段锁实现的