金三银四跳槽季,BAT美团滴滴java面试大纲(带答案版)之一:Java基础篇

Java基础篇:

题记:本系列文章,会尽可能模拟面试现场对话情景, 用口语而非书面语 ,采用问答形式来展示。另外每个问题都附上“延伸”,这部份内容是帮助小伙伴们更深的理解一些底层细节的补充,在面试中可能不多直接涉及,权当是提升自身水平的知识储备吧。java

第一部分:java容器相关

1.问:List 和 Set 都有什么区别?面试

分析:这种问题面试官通常想考察的都是你对这两种数据结构的了解,已经使用时候的选择依据,因此能够从数据结构和一些使用案例入手分别作个介绍数组

答:List,列表,元素可重复。经常使用的实现ArrayList和LinkedList,前者是数组方式来实现,后者是经过链表来实现,在使用选择的时候,通常考虑的是基本数据结构的特性,好比,数组读取效率较高,链表插入时效率较高。安全

Set,集合,特色就是存储的元素不可重复。经常使用是实现,是HashSet和TreeSet,分开来谈:数据结构

    HashSet,底层实现是HashMap,存储时,把值存在key,而value统一存储一个object对象。排重的时候,是先经过对象的hashcode来判断,若是不相等,直接存储。若是相等,会再对key作equals判断,若是依然相等,不存储,若是不相等,则存入,咱们知道,HashMap是数组+链表的基本结构,一样的,在HashSet中,也是经过一样的策略,存储在相同的数组位置下的链表中。多线程

    TreeSet,存入自定义对象时,对象须要实现Comparable接口,重写排序规则。使用场景通常是须要保证存储数据的有序和惟一性。底层数据结构是自平衡的二叉排序树(红黑树)并发

延伸:app

    a.在说到HashMap时,可能会直接引到HashMap相关话题,我发现这个问题面试官很是喜欢问,多是由于HashMap可聊的较多,也能很好的考验下应聘者对底层实现细节、源码阅读、刨根问底的态度。高并发

    b.涉及到二叉树了,小端就遇到过一次问红黑树特性的,由于以前准备过,成竹在胸的啪啪啪正要一一道来呢,结果刚说到第二个特性,面试官就问:红黑树和普通的平衡二叉树有什么区别?当时一脸懵逼样...回来后赶忙补足,核心区别就是:红黑树也是二叉查找树的一种,二叉树须要经过自旋、或其余方式(好比红黑树还能经过变色)来保证平衡(不然就成了链表结构了,没有时间复杂度上的优点了),红黑树限定的条件相对来讲比较宽松,也就是说在平衡的过程当中,消耗相对较小。线程

  c.因为HashSet无序,为了实现有序的目的,又不想用其余数据结构,能够用LinkedHashSet。简要说明,同HashSet和HashMap关系同样,也是使用了一个LinkedHashMap,LinkedHashMap和普通的HashMap的区别就是,在原有数据结构之上,采用双向链表的形式将全部entry(注意,是前面讲过的数组+链表中的各个链表里的元素,作了链接)连起来,顺序就是entry的插入顺序,这样能够保证元素的迭代顺序和插入顺序相同(有序性)

2.HashMap 是线程安全的吗,为何不是线程安全的?

分析:这种问题,既然面试官问起,确定是在这方面有的聊的。这个问题,在多数状况下,能清晰、全面的描述出问题前因后果便可,不多有面试官真的拿一段源码来考。固然,做为应聘者,若是能理解的很透彻,再用简单的图边写边讲,做为补充,仍是很是出彩的。

答:嗯,不是线程安全的。主要从两个方面来讲:

        a.在插入新数据的时候,多线程hash后的结果相同,插入位置也就会定位到数组的相同下标下的同一个链表中。在插入时,假如第二个线程在第一个线程插入的瞬间也插入,就可能会致使,覆盖前面插入的值。

        b.第二个非线程安全的影响是在扩容的时候,扩容会把全部值从新hash,插入到新的扩容后的“数组+链表”结构中。可能会致使环状链表状况出现,这样在读取节点的next节点时,永不为空,也就是著名的扩容时CPU使用率100%状况。

 延伸:

要作到心中有底,仍是须要知其然知其因此然才行,因此,撸下源码好好想一想,才能作到临阵不乱。

        a.建议好好看看源码,源码量不大,结构也比较清晰。

        b.针对环状链表的状况,我是看了别人写的图文并茂版的文章弄明白的,这里放上连接,供参考:HashMap高并发问题

3.问:那你是用什么数据结构来做为替代,知足线程安全的场景要求呢?

分析:这里通常面试官想考察的是对ConcurrentHashMap的了解,可是也有个别状况,会涉及到HashTable,小端就吃过这方面的亏,明明能够一笔带过的小知识点,却因为准备不充分,而没能答完整。

答:在Java里,提供了如下数据结构,能够解决线程安全问题:

a.HashTable,实现原理是经过Synchronize同步锁来把读写方法都加锁的方式。虽然线程安全了,可是效率低下,只要有读写,就不能作其余操做。

b.SynchronizedMap(涉及较少,了解便可),有条件的同步,是Collections类提供的一个方法返回的HashMap的多线程版本。实现是把基本的方法都加了同步锁。

c.ConcurrentHashMap(重头戏):根据jdk版本不一样,实现也有差异。

java8之前,是用segment(锁住map一段实现,默认是16段也就是能够并发数支持到16,也可自定义。读不受影响),数据结构为数组(segment)+数组(entry)+链表,适用于读多写少的场景。提供原子操做putIfAbsent()(没有则添加)。segment继承自ReentrantLock,来做为锁。

java8,元素结构为Node(实现Entry接口),数据结构为数组+链表;直接对Node进行加锁,粒度更小。当链表长度大于8,转换为红黑树,固然在转换前,先看下数组长度,若是小于64,那先经过扩容来实现;插入元素时,若是该位置为null,用CAS插入;若是不为null,则加Synchronize锁插入到链表;

扩展:

        a.咱们看到,数组的默认长度都是16,那么这个数字有什么深意吗?有!作运算时效率高!属于取巧的设计。一个是扩容的时候,直接向左位操做,移一位,扩张为二倍;二是hash取模的时候,hash值是32位,右移28位,剩高四位,而后与这个length-1也就是15(1111)按位与操做,使数据均匀分布。

        b.ConcurrentHashMap在获取size时候是怎么计算的呢?

1.7size(),先获取segment的大小,而后判断是否修改过,若是是,在加锁从新获取segment大小,而后把全部segment大小加在一块儿;

1.8size()的实现是用一个volatile 变量来作CAS修改,若是高并发,还会把部分值存到一个volatile数组。取值时,把这两部分的值加在一块儿。mappingcount()方法和size()方法实现方式同样

        c.hashmap可使用null做为key和value,存储于table数组第一个节点;hashtable不容许key和value为null.(这个在一个小公司被面试官问倒了,汗颜)

        d.了解一些其余的数据结构:

ConcurrentSkipListMap 数据有序

ConcurrentSkipListSet 能去重

        e.hashset是用hashmap实现,内部存的是key,全部的value都是同一个object

好,这篇就先写到这,你们还有什么想看的,在下面留言告诉我,我会优先整理出来哦。

 

     (期待你的留言交流!点下"推荐",手留余香,谢谢谢谢)

    (扫描下方二维码,关注我的公众号,第一时间看到你想要的更多专业文章)

 

        

相关文章
相关标签/搜索