Android 2020年面试系列(01 — Java 集合)


三月你好 。但愿三月可以花开疫散 。


俗话说 “面试造航母 ,工作拧螺丝”。虽然这有点夸大的意思  ,但是如果不能向面试官证明你可以造航母 ,那你可能连拧螺丝的机会都没有。(还有就是面试官问的问题 ,不一定在每个版本迭代的时候都能使用上 ,但是在你在职期间一定会用到的。)

2020 年面试系列文章 ,属于总结性文章 ,会把一些公认的面试技术点整理出来,并且附上每个技术点的原理和博客 。目的是对面试前的技术储备 。

业内公认的面试金三银四 ,今年的面试黄金时期可能会推迟到金五银六等等 。所以尽量在五月份之前完成这个系列 。这个系列文章不会超过十篇 ,我不太喜欢那种大而全的东西 ,目前我希望这个系列属于某些技术点的认识 (面试必问的技术点)。我希望看完这个系列的文章可以对找工作的小伙伴有所帮助 。


面试交流群:(欢迎老哥们加入)


本篇文章主要是介绍 Java 集合的知识 ,着重分析的是 HashMap 跟 ConcurrentHashMap 。


Java 技术点 集合

如果有人说在面试的时候没有遇到过问 Java 集合技术点的面试官 。我会说我不信  ,你遇到的面试官是假的吧 。

。。。

OK 言归正传 。 附上一个集合关系图

所有集合类都位于java.util包下。Java的集合类主要由两个接口派生而出:CollectionMap,Collection和Map是Java集合框架的根接口。


附图说明

1.  Collection 是一个高度抽象出来的集合接口 ,包含了集合的顶层函数 。Collection 集合分为 List 和 Set 两大分支 。

    1.1  List 是一个有序的队列,每一个元素都有它的索引 。第一个元素的索引值是0 。List的实现类有LinkedList , ArrayList , Vector , Stack 。

    1.2  Set 是一个不允许有重复元素的集合 。Set 的实现类有 HastSet 和 TreeSet 。HashSet依赖于HashMap,它实际上是通过HashMap实现的 ;TreeSet依赖于TreeMap ,它实际上是通过TreeMap实现的 。


2.  Map 是一个映射接口 ,即key-value键值对 。Map 中的每一个元素包含 “一个 key ” 和 “ key 对应的 value ” 。AbstractMap 是个抽象类 ,它实现了 Map 接口中的大部分 API 。而 HashMap ,TreeMap ,WeakHashMap 都是继承于AbstractMap 。Hashtable 虽然继承于 Dictionary ,但它实现了Map接口 。

3.  Iterator 是遍历集合的工具 ,即我们通常通过 Iterator 迭代器来遍历集合 。我们说 Collection 依赖于 Iterator ,是因为Collection的实现类都要实现 iterator() 函数,返回一个 Iterator 对象。ListIterator 是专门为遍历 List 而存在的 。

4.  Enumeration 是JDK 1.0 引入的抽象类 。作用和 Iterator 一样也是遍历集合 ;但是 Enumeration 的功能要比 Iterator 少。在上面的框图中,Enumeration 只能在 Hashtable , Vector , Stack 中使用 。

5.  最后看 Arrays 和 Collections 。它们是操作数组、集合的两个工具类。

集合特性 

List 接口(对象按照索引位置排序 ,可以有重复对象 ,允许按照对象在集合中的索引位置检索对象)

    ArrayList   线程不同步 ,数据结构为数组 ,特点:查询快、增删慢 

    LinkedList 线程不同步 ,数据结构为链表 ,特点:查询慢、增删快

    Vector       线程同步 ,数据结构为数组 ,特点:查询慢、增删慢 (过时)



Set 接口 (不允许有重复元素 ,对象不按特定方式排序 。实现类中的对象可以按特定方式排序 ,也可以实现 Comparator 接口来自定义排序方式)

     HashSet      线程不同步 ,数据结构为哈希表 ,无序 

     LinkedHashSet    线程不同步 ,数据结构为链表 ,有序

     TreeSet    线程不同步 ,数据结构为二叉树 ,有序


Map 接口(每一个元素包含一个键值对象 )

     HashMap    线程不同步,数据结构哈希表等等(此集合需要深入分析 ,划重点 ,面试必问题 ,要不赌一个面包机)

     LinkedHashMap    线程不同步 ,数据结构为链表 ,保留插入顺序

     HashTab    线程同步 ,数据结构为哈希表 ,键值对都不允许为 null (过时)

     TreeMap    线程不同步 ,数据结构为红黑树 ,保留插入顺序 

     ConcurrentHashMap    线程安全(面试单独问过)


Android 集合 

SparseIntArray ,SparseLongArray ,SparseBooleanArray 只存储对应类型值


(参考文章 https://www.cnblogs.com/xiaoxi/p/6089984.html)

PS:线程不同步意味着不安全 ,不安全意味着操作快 ;线程同步意味着安全 ,操作起来就会相对来说慢 。

上述是集合的整体分析 ,如果要问那一个集合是面试中必问的, 我想应该是 HashMap 。HashMap 可以说是集合界的实力选手 ,除了一些特定的场景 ,你可以在项目中搜一下  ,HashMap使用的一定是最多的 。接下来余篇就单独对 HashMap进行分析 。要想对 HashMap 有一定的深入 ,除了知道表面的东西之外,分析源码是必须的 。如果没有读过 HaspMap 的源码 ,那一定是死记硬背 。


HashMap  

它的底层基于哈希表,采用数组存储数据,使用链表来解决哈希碰撞。在JDK1.8中还引入了红黑树来解决链表长度过长导致的查询速度下降问题。

HashMap 面试能问的真的是太多太多了 。譬如

1.  HashMap 的工作原理是什么?

使用 put(key, value) 存储对象到 HashMap 中,使用 get(key) 从 HashMap 中获取对象。当我们给 put() 方法传递键和值时,我们先对键调用 hashCode() 方法 ,计算并返回的 hashCode 是用于找到 Map 数组的 bucket 位置来储存 Node 对象。

以下是具体的 put 过程(JDK1.8):

对 Key 求 Hash 值,然后再计算下标。

如果没有碰撞,直接放入桶中(碰撞的意思是计算得到的 Hash 值相同,需要放到同一个 bucket 中)。

如果碰撞了,以链表的方式链接到后面。

如果链表长度超过阀值(TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表。

如果节点已经存在就替换旧值。

如果桶满了(容量16*加载因子0.75),就需要 resize(扩容2倍后重排)。

以下是具体 get 过程:

调用 get() 方法,HashMap 会使用键对象的 hashcode 找到 bucket 位置,找到 bucket 位置之后,会调用 keys.equals() 方法去找到链表中正确的节点,最终找到要找的值对象。


2.  有什么方法可以减少碰撞?

扰动函数可以减少碰撞。

原理是如果两个不相等的对象返回不同的 hashcode 的话,那么碰撞的几率就会小些。这就意味着存链表结构减小,这样取值的话就不会频繁调用 equal 方法,从而提高 HashMap 的性能(扰动即 Hash 方法内部的算法实现,目的是让不同对象返回不同hashcode)。

使用不可变的、声明作 final 对象,并且采用合适的 equals() 和 hashCode() 方法,将会减少碰撞的发生。不可变性使得能够缓存不同键的 hashcode,这将提高整个获取对象的速度,使用 String、Integer 这样的 wrapper 类作为键是非常好的选择。


3.  HashMap 中 hash 函数怎么是实现的?

我们在 hashmap 中要找到某个元素,需要根据 key 的 hash 值来求得对应数组中的位置,如何计算这个位置就是 hash 算法。前面说过,hashmap 的数据结构是数组和链表的结合,所以我们当然希望这个 hashmap 里面的元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个。那么当我们用 hash 算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表。 所以,我们首先想到的就是把 hashcode 对数组长度取模运算。这样一来,元素的分布相对来说是比较均匀的。但是“模”运算的消耗还是比较大的,能不能找一种更快速、消耗更小的方式?

简单来说就是:

1. 高16 bit 不变,低16 bit 和高16 bit 做了一个异或(得到的 hashcode 转化为32位二进制,前16位和后16位低16 bit和高16 bit做了一个异或)。

2. (n·1) & hash = -> 得到下标。

4.  拉链法导致的链表过深,为什么不用二叉查找树代替而选择红黑树?为什么不一直使用红黑树?

之所以选择红黑树是为了解决二叉查找树的缺陷 :二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了 ,造成层次很深的问题),遍历查找会非常慢 。 而红黑树在插入新数据后可能需要通过左旋 、右旋 、变色这些操作来保持平衡 。引入红黑树就是为了查找数据快 ,解决链表查询深度的问题 。我们知道红黑树属于平衡二叉树 ,为了保持“平衡”是需要付出代价的 ,但是该代价所损耗的资源要比遍历线性链表要少 。所以当长度大于8的时候 ,会使用红黑树 ;如果链表长度很短的话 ,根本不需要引入红黑树 ,引入反而会慢 。 

PS:关于二叉树的缺陷 ,这个之前面试的时候也也被问过 ,当时也算是说了一下大概 。还有就是红黑树通过哪些方式来保持平衡的 ?可以参考文章 https://mp.weixin.qq.com/s/jz1ajDUygZ7sXLQFHyfjWA 


5.  如果 HashMap 的大小超过了负载因子(load factor)定义的容量怎么办?

HashMap 默认的负载因子大小为0.75。也就是说,当一个 Map 填满了75%的 bucket 时候,和其它集合类一样(如 ArrayList 等),将会创建原来 HashMap 大小的两倍的 bucket 数组来重新调整 Map 大小,并将原来的对象放入新的 bucket 数组中。这个过程叫作 rehashing。当调用 hash 方法找到新的 bucket 位置,这个值只可能在两个地方,一个是原下标的位置,另一种是在下标为 <原下标+原容量> 的位置。


PS:关于 HashMap面试问题 ,更多的可以参考 https://mp.weixin.qq.com/s/Ogu4WnxNP38PP3MB0aJjyg


源码分析

推荐文章 :https://www.jianshu.com/p/f16bfeeeea88  

搞明白 HaspMap 的原理就可以了 。


ConcurrentHashMap

ConcurrentHashMap 是 Java 并发包中提供的一个线程安全且高效的 HashMap 实现,以弥补 HashMap 不适合在并发环境中操作使用的不足 。

PS :这个在集合中也算是比较突出的一个集合 。


在 Java 中,HashMap 是非线程安全的,如果想在多线程下安全的操作 map,主要有以下解决方法:

第一种方法,使用Hashtable线程安全类;

第二种方法,使用Collections.synchronizedMap方法,对方法进行加同步锁;

第三种方法,使用并发包中的ConcurrentHashMap类;


ConcurrentHashMap 类所采用的正是分段锁的思想,将 HashMap 进行切割,把 HashMap 中的哈希数组切分成小数组,每个小数组有 n 个 HashEntry 组成,其中小数组继承自ReentrantLock(可重入锁),这个小数组名叫Segment


可参考文章:https://mp.weixin.qq.com/s/CH3gTbf55Cuabstfn2lpFg


PS:附上 Android 面试题 300 道面试题



PS:如有遗漏和其他理解欢迎补充。

如果感觉文章对您有帮助 ,可以关注我的公众号 SuperMaxs (如果有技术问题可以通过公众号加私人微信)。

个人Github 账号 :https://github.com/spuermax



唯有学习才是大势所趋 。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容