版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
文档来源网络侵权联系删除PAGEPAGE1仅供参考集合详解之Map+面试题集合有两个大接口:Collection和Map,本文重点来讲解集合中另一个常用的集合类型Map。以下是Map的继承关系图:11Map简介Map常用的实现类如下:Hashtable:Java早期提供的一个哈希表实现,它是线程安全的,不支持null键和值,因为它的性能不如ConcurrentHashMap,所以很少被推荐使用。HashMap:最常用的哈希表实现,如果程序中没有多线程的需求,HashMap是一个很好的选择,支持null键和值,如果在多线程中可用ConcurrentHashMap替代。TreeMap:基于红黑树的一种提供顺序访问的Map,自身实现了key的自然排序,也可以指定Comparator来自定义排序。LinkedHashMap:HashMap的一个子类,保存了记录的插入顺序,可在遍历时保持与插入一样的顺序。Map常用方法常用方法包括:put、remove、get、size等,所有方法如下图:enterimagedescriptionhere使用示例,请参考以下代码:MaphashMap=newHashMap();
//增加元素
hashMap.put("name","老王");
hashMap.put("age","30");
hashMap.put("sex","你猜");
//删除元素
hashMap.remove("age");
//查找单个元素
System.out.println(hashMap.get("age"));
//循环所有的key
for(Objectk:hashMap.keySet()){
System.out.println(k);
}
//循环所有的值
for(Objectv:hashMap.values()){
System.out.println(v);
}以上为HashMap的使用示例,其他类的使用也是类似。HashMap数据结构HashMap底层的数据是数组被成为哈希桶,每个桶存放的是链表,链表中的每个节点,就是HashMap中的每个元素。在JDK8当链表长度大于等于8时,就会转成红黑树的数据结构,以提升查询和插入的效率。HashMap数据结构,如下图:enterimagedescriptionhereHashMap重要方法1)添加方法:put(Objectkey,Objectvalue)执行流程如下:对key进行hash操作,计算存储index;判断是否有哈希碰撞,如果没碰撞直接放到哈希桶里,如果有碰撞则以链表的形式存储;判断已有元素的类型,决定是追加树还是追加链表,当链表大于等于8时,把链表转换成红黑树;如果节点已经存在就替换旧值;判断是否超过阀值,如果超过就要扩容。源码及说明:publicVput(Kkey,Vvalue){
//对key进行hash()
returnputVal(hash(key),key,value,false,true);
}
staticfinalinthash(Objectkey){
inth;
//对key进行hash()的具体实现
return(key==null)?0:(h=key.hashCode())^(h>>>16);
}
finalVputVal(inthash,Kkey,Vvalue,booleanonlyIfAbsent,
booleanevict){
Node<K,V>[]tab;Node<K,V>p;intn,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;Kk;
//节点存在
if(p.hash==hash&&
((k=p.key)==key||(key!=null&&key.equals(k))))
e=p;
//该链为树
elseif(pinstanceofTreeNode)
e=((TreeNode<K,V>)p).putTreeVal(this,tab,hash,key,value);
//该链为链表
else{
for(intbinCount=0;;++binCount){
if((e=p.next)==null){
p.next=newNode(hash,key,value,null);
if(binCount>=TREEIFY_THRESHOLD-1)//-1for1st
treeifyBin(tab,hash);
break;
}
if(e.hash==hash&&
((k=e.key)==key||(key!=null&&key.equals(k))))
break;
p=e;
}
}
//写入
if(e!=null){//existingmappingforkey
VoldValue=e.value;
if(!onlyIfAbsent||oldValue==null)
e.value=value;
afterNodeAccess(e);
returnoldValue;
}
}
++modCount;
//超过loadfactor*currentcapacity,resize
if(++size>threshold)
resize();
afterNodeInsertion(evict);
returnnull;
}put()执行流程图如下:enterimagedescriptionhere2)获取方法:get(Objectkey)执行流程如下:首先比对首节点,如果首节点的hash值和key的hash值相同,并且首节点的键对象和key相同(地址相同或equals相等),则返回该节点;如果首节点比对不相同、那么看看是否存在下一个节点,如果存在的话,可以继续比对,如果不存在就意味着key没有匹配的键值对。源码及说明:publicVget(Objectkey){
Node<K,V>e;
return(e=getNode(hash(key),key))==null?null:e.value;
}
/**
*该方法是Map.get方法的具体实现
*接收两个参数
*@paramhashkey的hash值,根据hash值在节点数组中寻址,该hash值是通过hash(key)得到的
*@paramkeykey对象,当存在hash碰撞时,要逐个比对是否相等
*@return查找到则返回键值对节点对象,否则返回null
*/
finalNode<K,V>getNode(inthash,Objectkey){
Node<K,V>[]tab;Node<K,V>first,e;intn;Kk;//声明节点数组对象、链表的第一个节点对象、循环遍历时的当前节点对象、数组长度、节点的键对象
//节点数组赋值、数组长度赋值、通过位运算得到求模结果确定链表的首节点
if((tab=table)!=null&&(n=tab.length)>0&&
(first=tab[(n-1)&hash])!=null){
if(first.hash==hash&&//首先比对首节点,如果首节点的hash值和key的hash值相同,并且首节点的键对象和key相同(地址相同或equals相等),则返回该节点
((k=first.key)==key||(key!=null&&key.equals(k))))
returnfirst;//返回首节点
//如果首节点比对不相同、那么看看是否存在下一个节点,如果存在的话,可以继续比对,如果不存在就意味着key没有匹配的键值对
if((e=first.next)!=null){
//如果存在下一个节点e,那么先看看这个首节点是否是个树节点
if(firstinstanceofTreeNode)
//如果是首节点是树节点,那么遍历树来查找
return((TreeNode<K,V>)first).getTreeNode(hash,key);
//如果首节点不是树节点,就说明还是个普通的链表,那么逐个遍历比对即可
do{
if(e.hash==hash&&
((k=e.key)==key||(key!=null&&key.equals(k))))//比对时还是先看hash值是否相同、再看地址或equals
returne;//如果当前节点e的键对象和key相同,那么返回e
}while((e=e.next)!=null);//看看是否还有下一个节点,如果有,继续下一轮比对,否则跳出循环
}
}
returnnull;//在比对完了应该比对的树节点或者全部的链表节点都没能匹配到key,那么就返回null相关面试题1.Map常见实现类有哪些?答:Map的常见实现类如下列表:Hashtable:Java早期提供的一个哈希表实现,它是线程安全的,不支持null键和值,因为它的性能不如ConcurrentHashMap,所以很少被推荐使用;HashMap:最常用的哈希表实现,如果程序中没有多线程的需求,HashMap是一个很好的选择,支持null键和值,如果在多线程中可用ConcurrentHashMap替代;TreeMap:基于红黑树的一种提供顺序访问的Map,自身实现了key的自然排序,也可以指定的Comparator来自定义排序;LinkedHashMap:HashMap的一个子类,保存了记录的插入顺序,可在遍历时保持与插入一样的顺序。2.使用HashMap可能会遇到什么问题?如何避免?答:HashMap在并发场景中可能出现死循环的问题,这是因为HashMap在扩容的时候会对链表进行一次倒序处理,假设两个线程同时执行扩容操作,第一个线程正在执行B→A的时候,第二个线程又执行了A→B,这个时候就会出现B→A→B的问题,造成死循环。
解决的方法:升级JDK版本,在JDK8之后扩容不会再进行倒序,因此死循环的问题得到了极大的改善,但这不是终极的方案,因为HashMap本来就不是用在多线程版本下的,如果是多线程可使用ConcurrentHashMap替代HashMap。3.以下说法正确的是?A:Hashtable和HashMap都是非线程安全的
B:ConcurrentHashMap允许null作为key
C:HashMap允许null作为key
D:Hashtable允许null作为key
答:C
题目解析:Hashtable是线程安全的,ConcurrentHashMap和Hashtable是不允许null作为键和值的。4.TreeMap怎么实现根据value值倒序?答:使用Collections.sort(list,newComparator<Map.Entry<String,String>>()自定义比较器实现,先把TreeMap转换为ArrayList,在使用Collections.sort()根据value进行倒序,完整的实现代码如下。TreeMap<String,String>treeMap=newTreeMap();
treeMap.put("dog","dog");
treeMap.put("camel","camel");
treeMap.put("cat","cat");
treeMap.put("ant","ant");
//map.entrySet()转成List
List<Map.Entry<String,String>>list=newArrayList<>(treeMap.entrySet());
//通过比较器实现比较排序
Collections.sort(list,newComparator<Map.Entry<String,String>>(){
publicintcompare(Map.Entry<String,String>m1,Map.Entry<String,String>m2){
returnm2.getValue().compareTo(m1.getValue());
}
});
//打印结果
for(Map.Entry<String,String>item:list){
System.out.println(item.getKey()+":"+item.getValue());
}程序执行结果:dog:dog
cat:cat
camel:camel
ant:ant5.以下哪个Set实现了自动排序?A:LinedHashSet
B:HashSet
C:TreeSet
D:AbstractSet答:C6.以下程序运行的结果是什么?Hashtablehashtable=newHashtable();
hashtable.put("table",null);
System.out.println(hashtable.get("table"));答:程序执行报错:java.lang.NullPointerException。Hashtable不允许null键和值。7.HashMap有哪些重要的参数?用途分别是什么?答:HashMap有两个重要的参数:容量(Capacity)和负载因子(LoadFactor)。容量(Capacity):是指HashMap中桶的数量,默认的初始值为16。负载因子(LoadFactor):也被称为装载因子,LoadFactor是用来判定HashMap是否扩容的依据,默认值为0.75f,装载因子的计算公式=HashMap存放的KV总和(size)/Capacity。8.HashMap和Hashtable有什么区别?答:HashMap和Hashtable区别如下:Hashtable使用了synchronized关键字来保障线程安全,而HashMap是非线程安全的;HashMap允许K/V都为null,而HashtableK/V都不允许null;HashMap继承自AbstractMap类;而Hashtable继承自Dictionary类。9.什么是哈希冲突?答:当输入两个不同值,根据同一散列函数计算出相同的散列值的现象,我们就把它叫做碰撞(哈希碰撞)。10.有哪些方法可以解决哈希冲突?答:哈希冲突的常用解决方案有以下4种。开放定址法:当关键字的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,循环此过程直到找出一个不冲突的哈希地址,将相应元素存入其中。再哈希法:这种方法是同时构造多个不同的哈希函数,当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key),循环此过程直到找到一个不冲突的哈希地址,这种方法唯一的缺点就是增加了计算时间。链地址法:这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。建立公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。11.HashMap使用哪种方法来解决哈希冲突(哈希碰撞)?答:HashMap使用链表和红黑树来解决哈希冲突,详见本文put()方法的执行过程。12.HashMap的扩容为什么是2^n?答:这样做的目的是为了让散列更加均匀,从而减少哈希碰撞,以提供代码的执行效率。13.有哈希冲突的情况下HashMap如何取值?答:如果有哈希冲突,HashMap会循环链表中的每项key进行equals对比,返回对应的元素。相关源码如下:do{
if(e.hash==hash&&
((k=e.key)==key||(key!=null&&key.equals(k))))//比对时还是先看hash值是否相同、再看地址或equals
returne;//如果当前节点e的键对象和key相同,那么返回e
}while((e=e.next)!=null);//看看是否还有下一个节点,如果有,继续下一轮比对,否则跳出循环14.以下程序会输出什么结果?classPerson{
privateIntegerage;
publicbooleanequals(Objecto){
if(o==null||!(oinstanceofPerson)){
returnfalse;
}else{
returnthis.getAge().equals(((Person)o).getAge());
}
}
publicinthashCode(){
returnage.hashCode();
}
publicPerson(intage){
this.age=age;
}
publicvoidsetAge(intage){
this.age=age;
}
publicIntegergetAge(){
returnage;
}
publicstaticvoidmain(String[]args){
HashMap<Person,Integer>hashMap=newHashMap<>();
Personperson=newPerson(18);
hashMap.put(person,1);
System.out.println(hashMap.get(newPerson(18)));
}
}答:1
题目解析:因为Person重写了equals和hashCode方法,所有person对象和newPerson(18)的键值相同,所以结果就是1。15.为什么重写equals()时一定要重写hashCode()?答:因为Java规定,如果两个对象equals比较相等(结果为true),那么调用hashCode也必须相等。如果重写了equals()但没有重写hashCode(),就会与规定相违背,比如以下代码(故意注释掉hashCode方法):classPerson{
privateIntegerage;
publicbooleanequals(Objecto){
if(o==null||!(oinstanceofPerson)){
returnfalse;
}else{
returnthis.getAge().equals(((Person)o).getAge());
}
}
//publicinthashCode(){
//returnage.hashCode();
//}
publicPerson(intage){
this.age=age;
}
publicvoidsetAge(intage){
this.age=age;
}
publicIntegergetAge(){
returnage;
}
publicstaticvoidmain(String[]args){
Personp1=newPerson(18);
Personp2=newPerson(18);
System.out.println(p1.equals(p2));
System.out.println(p1.hashCode()+":"+p2.hashCode());
}
}执行的结果:true
21685669:2133927002如果重写hashCode()之后,执行的结果是:true
18:18这样就符合了Java的规定,因此重写equals()时一定要重写hashCode()。16.HashMap在JDK7多线程中使用会导致什么问题?答:HashMap在JDK7中会导致死循环的问题。因为在JDK7中,多线程进行HashMap扩容时会导致链表的循环引用,这个时候使用get()获取元素时就会导致死循环,造成CPU100%的情况。17.HashMap在JDK7和JDK8中有哪些不同?答:HashMap在JDK7和JDK8的主要区别如下。存储结构:JDK7使用的是数组+链表;JDK8使用的是数组+链表+红黑树。存放数据的规则:JDK7无冲突时,存放数组;冲突时,存放链表;JDK8在没有冲突的情况下直接存放数组,有冲突时,当链表长度小于8时,存放在单链表结构中,当链表长度大于8时,树化并存放至红黑树的数据结构中。插入数据方式:JDK7使用的是头插法(先将原位置的数据移到后1位,再插入数据到该位置);JDK8使用的是尾插法(直接插入到链表尾部/红黑树)。总结通过本文可以了解到:Map的常用实现类Hashtable是Java早期的线程安全的哈希表实现;HashMap是最常用的哈希表实现,但它是非线程安全的,可使用ConcurrentHashMap替代;TreeMap是基于红黑树的一种提供顺序访问的哈希表实现;LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,可在遍历时保持与插入一样的顺序。HashMap在JDK7可能在扩容时会导致链表的循环引用而造成CPU100%,HashMap在JDK8时数据结构变更为:数组+链表+红黑树的存储方式,在没有冲突的情况下直接存放数组,有冲突,当链表长度小于8时,存放在单链表结构中,当链表长度大于8时,树化并存放至红黑树的数据结构中。 如何轻松获得Offer你好,我是王磊,某上市公司技术研发经理,前奇虎360员工,有着10余年的编程工作经验,目前主要负责新员工技术面试和构建企业技术架构的相关事宜。随着面试过的人数增加,我发现面试者们暴露出了技术方面的很多问题,为了让更多面试者少走一些弯路,也为了让企业能招到合适的技术人才,于是就诞生了这个专栏。为了写好这个专栏内容,我先后拜访了一二十家互联网公司,与不同的面试官和面试者进行面对面探讨,深入了解了企业对于面试者的要求和常见的Java面试题型。之后我花了大半年的时间,结合自己4年多作为面试官的经历,把这些内容整理成文,用大约10万字的内容对Java的核心知识点和常见的500多道面试题,做了详细的介绍,也就是本专栏中你所看到的全部内容,希望对你能有所帮助。为什么要学这个专栏内容?「因为它能为你赢得面试的主动权,让你获得更多的Offer。」从业十多年,我从面试者变成面试官,在Java面试上积累了比较丰富的经验。其实,很多面试者在搜集面试资料的时候都踩过一些“坑”,你是不是也遇到过:免费搜索的面试题,内容不全面,这就算了,有时候答案都不准确;很多培训机构提供的面试宝典内容虽然不少,但深度不够,且面试题过于老旧脱离了企业实际需要;还有很多付费的面试题存在滥竽充数,提供了很多没有价值的面试题,钱花了,干货没学到;市面上大部分面试题只讲了基础概念,没有提供题目解析和示例代码,不利于读者真正的掌握背后的原理,只能死记硬背,且容易忘记。为了规避这些“坑”,我跑了很多家互联网公司,来确认Java面试中实际考察的高频知识点和常见题型。可是有了第一手素材后,我要如何让大家真正从我的讲解中学到干货、用到实处呢?经过反复验证,我才设计了如下的内容讲述模式。第一,500+面试题详解。如果你是还没走入职场的新人,我会为你提供完整的Java技术栈讲解,以及最新、最全、最实用的500多道Java面试题详解。第二,10万字Java核心知识点梳理。本专栏的每一篇内容,都采用的是「核心知识点+N道相关面试题」的模式,让你不单能应付面试,还能学到更多的Java核心知识。第三,技术、面试搭配平衡,不但让你学到心里,还助你展示出来。面对目前技术市场的相对冷淡和一个职位多个应聘者竞争的现状,面试者们只有掌握更多Java核心技能和面试理论知识,才能在众多面试者中脱颖而出。本专栏每篇文章大致分为两个部分:Java核心点介绍+相关面试题详解,这两部分内容相辅相成,前面的核心知识点介绍让后面的面试题更容易理解,后面的面试题加深了读者对于Java核心点的掌握。如此一来,让你所学及所用,不仅能够应付面试,更能学习到更多有价值的Java技术点,让你在面试中和工作中都能展示
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年林甸县教师招聘笔试模拟试题及答案解析
- 2026黑龙江省东方红林业局有限公司公开招聘22人考试备考试题及答案解析
- 2026年汾西县教师招聘笔试参考题库及答案解析
- 2026江苏南京鼓楼医院The Innovation Surgery 编辑部招聘2人笔试备考题库及答案解析
- 2026年赵县教师招聘考试备考试题及答案解析
- 2026年五河县教师招聘笔试备考试题及答案解析
- 2026-江西分宜图书馆招聘考试参考题库-含答案
- 2026福建省农业科学院数字农业研究所招聘编制外科研辅助人员笔试模拟试题及答案解析
- 2026湖南长沙市望城区人力资源和社会保障局公益性岗位人员招聘笔试备考题库及答案解析
- 2026安徽省国资委组织国有企业为高校毕业生开发见习岗位笔试参考题库及答案解析
- 【答案】《智能采矿》(河南理工大学)章节作业慕课答案
- 元气森林市场行业分析报告
- 西餐烹调基础-课件全套 重大版 项目1-7 西餐烹调基础知识 -西式快餐制作
- 2024年山东大学校长开学讲话稿8000字
- 标准制定立项汇报
- 2025年秋招:平安银行笔试真题及答案
- (2025)医院招聘护士考试题库(附参考答案)
- 水库大坝降等与报废评估导则
- 简易委托支付协议
- 2025年国投健康产业投资有限公司招聘笔试参考题库含答案解析
- 环卫驾驶员交通安全培训
评论
0/150
提交评论