关于java:入职大厂齐姐精选的-9-道-Java-集合面试题

Java 汇合框架其实都讲过了,有一篇讲 Collection 的,有一篇讲 HashMap 的,那没有看过的小伙伴快去补下啦,文末也都有链接;看过的小伙伴,那本文就是检测学习成绩的时候啦

明天这篇文章是单纯的从面试的角度登程,以答复面试题为线索,再把整个 Java 汇合框架温习一遍,心愿能帮忙大家拿下面试。

先上图:

当面试官问问题时,我会先把问题归类,锁定这个知识点在我的常识体系中的地位,而后延展开来想这一块有哪些重点内容,面试官问这个是想考查什么、接下来还想问什么。

这样本人的思路不会凌乱,还能预测面试官下一个问题,或者,也能够疏导面试官问出你精心筹备的问题,这场面试实质上就是你在主导、你在 show off 本人扎实的基础知识和良好的沟通交流能力。

其实我在 LRU 那篇文章里就说到过这个观点,而后就有读者问我,说会不会被面试官看穿?

答:看进去了又怎么?面试官阅人有数,是有可能看进去的,然而也只会莞尔一笑,感觉这个同学很用心。

精心筹备面试既是对面试官集体工夫的尊重,也是表明了你对这家公司的趣味,这样的员工不是每家公司都想要的吗?

好了,进入正题,明天就来解决这 9 大面试题。

1. ArrayList vs LinkedList

这题的问法很多,比方

  • 最简略的就间接问 ArrayList 和 LinkedList 的区别和分割;
  • 或者问你什么时候要抉择 ArrayList,什么时候抉择 LinkedList;
  • 或者在你们聊到某个场景、或者在算法题中,面试官问你如何抉择。

万变不离其宗。

首先论断是:

  • 绝大多数的情景下都偏差于用 ArrayList,除非你有明确的要应用 LinkedList 的理由。
  • 如果你不确定用哪个,就用 ArrayList

两者在实现层面的区别是:

  • ArrayList 是用一个可扩容的数组来实现的 (re-sizing array);
  • LinkedList 是用 doubly-linked list 来实现的。

数组和链表之间最大的区别就是数组是能够随机拜访的(random access)。

这个特点造成了在数组里能够通过下标用 O(1) 的工夫拿到任何地位的数,而链表则做不到,只能从头开始一一遍历。

两者在增删改查操作上的区别:

  • 在「改查」这两个性能上,因为数组可能随机拜访,所以 ArrayList 的效率高;
  • 在「增删」这两个性能上,如果不思考找到这个元素的工夫,数组因为物理上的连续性,当要增删元素时,在尾部还好,然而其余中央就会导致后续元素都要挪动,所以效率较低;而链表则能够轻松的断开和下一个元素的连贯,直接插入新元素或者移除旧元素。

然而呢,实际上你不能不思考找到元素的工夫啊。。。尽管 LinkedList 能够 O(1) 的工夫插入和删除元素,能够你得先找到中央啊!

不是有个例子么,修理这个整机只须要 1 美元,然而找到这个整机须要 9999 美元。咱们平时修 bug 也是如此,重点是找到 root cause 的过程。

而且如果是在尾部操作,数据量大时 ArrayList 会更快的。

事实上,LinkedList 是很多性能问题的 bug,那么为什么呢?

因为 ListNode 在物理内存里的不间断,导致它用了很多小的内存片段,这会影响很多过程的性能以及 cache-locality(局部性);所以即使是实践上的工夫复杂度和 ArrayList 一样时,也会导致实际上比 ArrayList 慢很多。

2. ArrayList vs Vector

答:

  1. Vector 是线程平安的,而 ArrayList 是线程不平安的;
  2. 扩容时扩多少的区别,文邹邹的说法就是 data growth methods不同,

    • Vector 默认是扩充至 2 倍;
    • ArrayList 默认是扩充至 1.5 倍。

回顾下这张图,

Vector 和 ArrayList 一样,也是继承自 java.util.AbstractList,底层也是用数组来实现的。

然而当初曾经被弃用了,因为它是线程平安的。任何益处都是有代价的,线程平安的代价就是效率低,在某些零碎里很容易成为瓶颈,所以当初大家不再在数据结构的层面加 synchronized,而是把这个工作转移给咱们程序员。

那怎么晓得扩容扩多少的呢?

看源码:


这是 Vecotr 的扩容实现,因为通常并不定义 capacityIncrement,所以默认状况下它是扩容两倍。

VS

这是 ArrayList 的扩容实现,算术右移操作是把这个数的二进制往右挪动一位,最右边补符号位,然而因为容量没有正数,所以还是补 0.

那右移一位的成果就是除以 2,那么定义的新容量就是原容量的 1.5 倍。

3. ArrayDeque vs LinkedList

首先要分明它们之间的关系:

答:

  1. ArrayDeque 是一个可扩容的数组,LinkedList 是链表构造;
  2. ArrayDeque 里不能够存 null 值,然而 LinkedList 能够;
  3. ArrayDeque 在操作头尾端的增删操作时更高效,然而 LinkedList 只有在当要移除两头某个元素且曾经找到了这个元素后的移除才是 O(1) 的;
  4. ArrayDeque 在内存应用方面更高效。
  5. 所以,只有不是必须要存 null 值,就抉择 ArrayDeque 吧!

那如果是一个很资深的面试官问你,什么状况下你要抉择用 LinkedList 呢?

答:Java 6 以前。因为 ArrayDeque 在 Java 6 之后才有的。
为了版本兼容的问题,理论工作中咱们不得不做一些斗争。

4. HashSet 实现原理

答:

HashSet 是基于 HashMap 来实现的,底层采纳 Hashmap 的 key 来贮存元素,次要特点是无序的,基本操作都是 O(1) 的工夫复杂度,很快。
所以它的实现原理能够用 HashMap 的来解释。

5. HashMap 实现原理

答:

  • JDK1.6/1.7数组 + 链表
  • JDK 1.8数组 + 红黑树

具体说来,

对于 HashMap 中的每个 key,首先通过 hash function 计算出一个哈希值,这个哈希值就代表了在桶里的编号,而“桶”实际上是通过数组来实现的,然而桶有可能比数组大呀,所以把这个哈希值模上数组的长度失去它在数组的 index,就这样把它放在了数组里。

这是现实状况下的 HashMap,但事实中,不同的元素可能会算出雷同的哈希值,这就是哈希碰撞,即多个 key 对应了同一个桶。

为了解决哈希碰撞呢,Java 采纳的是 Separate chaining 的解决形式,就是在碰撞的中央加个链子,也就是上文说的链表或者红黑树

具体的 put()get()这两个重要 API 的操作过程和原理,大家能够在公众号后盾回复「HashMap」获取文章浏览。

6. HashMap vs HashTable

答:

  1. Hashtable 是线程平安的,HashMap 并非线程平安;
  2. HashMap 容许 key 中有 null 值,Hashtable 是不容许的。这样的益处就是能够给一个默认值。

其实 HashMap 与 Hashtable 的关系,就像 ArrayList 与 Vector,以及 StringBuilder 与 StringBuffer。

Hashtable 是晚期 JDK 提供的接口,HashMap 是新版的。这些新版的改良都是因为 Java 5.0 之后容许数据结构不思考线程平安的问题,因为理论工作中咱们发现没有必要在数据结构的层面上上锁,加锁和放锁在零碎中是有开销的,外部锁有时候会成为程序的瓶颈。

所以 HashMap, ArrayList, StringBuilder 不再思考线程平安的问题,性能晋升了很多。

7. 为什么改 equals() 肯定要改 hashCode()?

答:

首先基于一个假如:任何两个 objecthashCode 都是不同的。也就是 hash function 是无效的。

那么在这个条件下,有两个 object 是相等的,那如果不重写 hashCode(),算进去的哈希值都不一样,就会去到不同的 buckets 了,就迷失在茫茫人海中了,再也无奈相认,就和 equals() 条件矛盾了,证毕。

  1. hashCode() 决定了 key 放在这个桶里的编号,也就是在数组里的 index
  2. equals() 是用来比拟两个 object 是否雷同的。

8. 如何解决哈希抵触?

一般来说哈希抵触有两大类解决形式:

  • Separate chaining
  • Open addressing

Java 中采纳的是第一种 Separate chaining,即在产生碰撞的那个桶前面再加一条“链”来存储。

那么这个“链”应用的具体是什么数据结构,不同的版本稍有不同,上文也提到过了:

  • JDK1.6 和 1.7 是用链表存储的,这样如果碰撞很多的话,就变成了在链表上的查找,worst case 就是 O(n);
  • JDK 1.8 进行了优化,当链表长度较大时(超过 8),会采纳红黑树来存储,这样大大提高了查找效率。

(话说,这个还真的喜爱考,曾经在屡次面试中被问过了,还有面试官问为什么是超过“8”才用红黑树 ????)

第二种办法 open addressing 也是十分重要的思维,因为在实在的分布式系统里,有很多中央会用到 hash 的思维但又不适宜用 seprate chaining

这种办法是程序查找,如果这个桶里曾经被占了,那就依照“某种形式”持续找下一个没有被占的桶,直到找到第一个空的。

如图所示,John Smith 和 Sandra Dee 产生了哈希抵触,都被计算到 152 号桶,于是 Sandra 就去了下一个空位 – 153 号桶,当然也会对之后的 key 产生影响:Ted Baker 计算结果本应是放在 153 号的,但鉴于曾经被 Sandra 占了,就只能再去下一个空位了,所以到了 154 号。

这种形式叫做 Linear probing 线性探查,就像上图所示,一个个的顺着找下一个空位。当然还有其余的形式,比方去找平方数 Double hashing.

9. Collection vs Collections

这俩看似相近,实则相差十万八千里,就如同坏蛋坏蛋卡的区别似的。

Collection

  • 汇合接口;
  • Java 汇合框架root interface
  • 落脚点是一个 interface
  • 蕴含了以下这些接口和类:

想零碎学习 Collection,能够在公众号内回复「汇合」,获取爆款文章。

Collections 是工具类 utility class,是汇合的操作类,提供了一些静态方法供咱们应用,比方:

  • addAll()
  • binarySearch()
  • sort()
  • shuffle()
  • reverse()

好了,以上就是汇合的常考面试题汇总和答案了,心愿不仅能帮忙你拿下面试,也能真的了解透彻,灵活运用。

最近看到本人的文章在其余平台被别人搬运,请大家认准全网对立惟一标识「码农田小齐」,并且恳请大家如果看到没有写明作者和起源出处的我的文章,告知我一声,这些文章都是本人的心肝宝贝啊嗷呜~

最初,如果你感觉一个人保持的很难,想有小伙伴一起学习、互相监督打气的,记得退出我的自习室。

我是小齐,纽约程序媛,终生学习者,每天晚上 9 点,云自习室里不见不散!

更多干货文章见我的 Github: https://github.com/xiaoqi6666…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理