职高毕业生面试Java岗位的幽默故事:从仰慕到薪资期望的趣谈

开始之前,先给大家讲个小故事吧:

职高毕业生面试Java岗位的幽默故事:从仰慕到薪资期望的趣谈

图片来源于网络

不是这个~

面试官:你好!请简单介绍一下你自己

骚年向大佬问好。在读书期间,骚年就对大佬十分仰慕,大佬一直是骚年的偶像。因此,骚年职高刚毕业,就迫不及待地开始学习 Java 技术,接着前来大佬所在的公司应聘。没想到,面试官竟然就是大佬。

面试官:等等,职...职高?

骚年说:这并不重要。噢,我亲爱的大佬,您知道吗?我对您充满敬仰,也很羡慕您。我羡慕您头顶那块没有头发的地方,那是我一直向往的地方……

面试官摸了摸从右边盖到左边的头发,接着咳嗽了几声,然后说道:“咱回归正题吧。嗯~这个,你是刚在找工作吗?”

骚年:不完全是,学习期间实习过一段时间

面试官:嗯,先问问,对薪资有什么期望吗?

骚年说:“我来算一算啊,我每个月的房租是 1200 呢。我每天要抽两包 6 块钱的烟,中午加晚上的饭一天要 20 块,还要喝饮料,用电用水之类的也得花钱。这样算下来,一个月得 8000 块呢,这样的话,我就赚不到钱啦,就是 8K 这么多呀。”

面试官询问:就只有 8K 吗?我们这边的薪资最低都是 28K 呢。那现在来了解一下你的技术水平吧。接着问道:前段时间微博的服务器出现了崩溃的情况,这是怎么回事呢?

骚年:啊?

面试官:额~拿错了台本,再来,说说Java集合!

骚年:这个我知道,List和Set

面试官:还有呢?

骚年:还有吗?没有了吧?

面试官说:“小伙子,你连 Java 集合都不能说全,竟然还敢要 8K 的薪资?你还是回家吧!”

骚年:......

细说Java集合

接口继承关系和实现

集合类存放在 Java.util 包中。其中主要有 3 种类型,分别是 set(集)、list(列表包含 Queue)以及 map(映射)。

Collection 是最为基础的接口,它包含了 List、Set、Queue 等。

Iterator 是一种可以用来遍历集合中数据的工具,它能够实现对集合数据的遍历操作。

3. Map:是映射表的基础接口

List

Java 的 List 是一种极为常用的数据类型。List 具备有序的特性。在 Java 中,List 总共拥有三个实现类,它们分别是 ArrayList、Vector 以及 LinkedList。

1、ArrayList(数组)

ArrayList 是常用的 List 实现类之一。它内部通过数组来实现。这种方式使得它能够对元素进行快速的随机访问。数组存在一个缺点,那就是每个元素之间不能有间隔。当数组的大小无法满足需求时,需要增加存储能力,此时就需要将已有数组的数据复制到新的存储空间中。从 ArrayList 的中间位置进行元素的插入操作时,需要对数组进行复制,这会产生一定代价。从 ArrayList 的中间位置进行元素的删除操作时,需要对数组进行移动,这也会有一定代价。所以,ArrayList 适合随机查找和遍历,而不太适合插入和删除。

2、Vector(数组实现、线程同步)

Vector 通过数组实现,这一点与 ArrayList 相同。它的不同之处在于支持线程的同步,意味着在某一特定时刻只有一个线程能够对 Vector 进行写入操作,这样能避免多线程同时写入而导致的不一致性。然而,实现同步需要耗费很高的成本,所以,相比访问 ArrayList,访问 Vector 会更慢。

3、LinkList(链表)

LinkedList 是以链表结构来存储数据的。它很适宜进行数据的动态插入与删除操作。而在随机访问和遍历速度方面相对较慢。此外,它还提供了一些在 List 接口中未定义的方法,这些方法专门用于对表头和表尾元素进行操作。凭借这些方法,它可以被当作堆栈、队列和双向队列来使用。

Set

Set 注重其独一无二的性质。这个体系主要用于存储元素,这些元素是无序的,也就是说存入和取出的顺序不一定相同。并且,Set 中的值不能重复。对象的相等性本质在于对对象 hashCode 值的判断(在 java 中是依据对象的内存地址计算出的此序号)。如果要让两个不同的对象被视为相等,就必须覆盖 Object 的 hashCode 方法和 equals 方法。

1、HashSet(Hash 表)

哈希表存放的是哈希值。HashSet 存储元素的顺序并非按照存入时的顺序,与 List 明显不同,它是按照哈希值来存储的,所以取数据也是按照哈希值来取得。元素的哈希值由元素的 hashcode 方法来获取。HashSet 会先判断两个元素的哈希值。若哈希值相同,接着会比较 equals 方法。当 equals 结果为 true 时,HashSet 视其为同一个元素。若 equals 为 false ,则不是同一个元素。

哈希值相同但 equals 为 false 的元素是如何存储的呢?在同样的哈希值下会顺延,可认为哈希值相同的元素放在一个哈希桶中。也就是说,哈希一样的元素会存为一列。图 1 表示 hashCode 值不相同的情况;图 2 表示 hashCode 值相同,但 equals 不相同的情况。

HashSet 依据 hashCode 值来明确元素在内存里的位置。在一个 hashCode 位置上能够放置多个元素。

2、TreeSet(二叉树)

TreeSet()利用二叉树的原理对新添加的对象按照特定顺序进行排序,包括升序和降序。每当增加一个对象时,都会进行排序操作,并且会将该对象插入到二叉树的指定位置。

只有这样,自己定义的类才可以正常使用 TreeSet 排序。

覆写 compare()函数时,需要返回相应的值,这样 TreeSet 才能按照一定的规则进行排序。

将此对象与指定对象进行顺序比较。若该对象小于指定对象,则返回负整数;若该对象等于指定对象,则返回零;若该对象大于指定对象,则返回正整数。

LinkHashSet 是由 HashSet 和 LinkedHashMap 组合而成的。

LinkedHashSet 继承了 HashSet,并且基于 LinkedHashMap 来实现。LinkedHashSet 底层利用 LinkedHashMap 来保存所有元素。它继承自 HashSet。它的所有方法操作与 HashSet 相同。所以 LinkedHashSet 的实现较为简单。它只提供了四个构造方法。通过传递一个标识参数,调用父类的构造器。底层构造一个 LinkedHashMap 来实现。在相关操作上与父类 HashSet 的操作相同。可直接调用父类 HashSet 的方法。

Map

1、HashMap(数组+链表+红黑树)

HashMap 依据键的 hashCode 值来存储数据。在大多数情形下,它能够直接定位到其对应的值。所以,它具有较为快速的访问速度。然而,它的遍历顺序是不确定的。HashMap 最多只允许有一条记录的键为 null,同时也允许多条记录的值为 null。HashMap 不具备线程安全的特性。在任何一个时刻,都有可能有多个线程同时对 HashMap 进行写入操作。这样的情况可能会引发数据的不一致性问题。倘若需要满足线程安全的要求,那么可以借助 Collections 的 synchronizedMap 方法,让 HashMap 具备线程安全的能力。另外,还可以使用 ConcurrentHashMap 来实现线程安全。我们用下面这张图来介绍HashMap 的结构。

(1)JAVA7 实现

从大方向来看,HashMap 内部有一个数组。并且数组中的每个元素是一个单向链表。在上述图中,每一个绿色的实体都是嵌套类 Entry 的实例。Entry 具备四个属性,分别是 key、value、hash 值以及用于单向链表的 next。

当前数组的容量为 capacity,它始终保持为 2 的 n 次方。数组可以进行扩容,扩容之后数组的大小是当前容量的 2 倍。

loadFactor:负载因子,默认为 0.75。

扩容的阈值为 capacity 与 loadFactor 的乘积。

(2)JAVA8 实现

Java8 对 HashMap 做了一些改动。其中最大的差别在于它利用了红黑树。因此,HashMap 由数组、链表以及红黑树共同组成。

根据 Java7 HashMap 的介绍可知,查找时依据 hash 值能迅速定位到数组的具体下标。然而,在此之后,需顺着链表逐个进行比较才能找到所需的元素,其时间复杂度取决于链表的长度,为 O(n)。为降低这部分开销,在 Java8 中,若链表中的元素超过 8 个,就会把链表转换为红黑树,在这些位置进行查找时能降低时间复杂度至 O(logN)。

2、ConcurrentHashMap

(1)Segment 段

ConcurrentHashMap 的思路与 HashMap 较为相似,然而由于它能够支持并发操作,故而会更为复杂一些。整个 ConcurrentHashMap 是由一个个 Segment 所构成的,Segment 意味着“部分”或者“一段”的含义,正因如此,在很多情形下都会将其称作分段锁。注意,在行文过程中,我有很多地方使用了“槽”来指代一个 segment。

Segment 继承了 ReentrantLock 来实现加锁,从而保证了线程安全。

简单理解为,ConcurrentHashMap 是由 Segment 组成的数组。Segment 通过继承 ReentrantLock 来实现加锁功能。因此,每次需要加锁的操作所锁住的是一个 segment。只要能确保每个 Segment 是线程安全的,就能够实现全局的线程安全。

(3)并行度(默认 16)

并行级别、并发数、Segment 数,其翻译并不重要,关键是要理解它。默认值为 16,这意味着 ConcurrentHashMap 有 16 个 Segments。所以从理论上讲,在这种情况下,最多能够同时支持 16 个线程并发进行写入操作,只要这些线程的操作分别分布在不同的 Segment 上。这个值在初始化时能被设置为其他值。然而,一旦完成初始化,它就无法进行扩容。具体到每个 Segment 内部来看,实际上每个 Segment 很类似于之前所介绍的 HashMap,只是它需要保证线程安全,因此在处理上会更麻烦一些。

(4)Java8 实现 (引入了红黑树)

Java8 对 ConcurrentHashMap 作出了较大的改动。同时,Java8 引入了红黑树。

3、HashTable(线程安全)

Hashtable 属于遗留类。很多映射的常用功能和 HashMap 较为相似。它与 HashMap 的不同之处在于,它是从 Dictionary 类继承而来的,并且具备线程安全的特性。在任何一个时间点,只有一个线程能够对 Hashtable 进行写入操作。其并发性能不如 ConcurrentHashMap,这是因为 ConcurrentHashMap 引入了分段锁。Hashtable 不适合在新代码中使用。在不需要线程安全的情况下,可以用 HashMap 来进行替换。而在需要线程安全的情况下,则可以用 ConcurrentHashMap 来替换。

4、TreeMap(可排序)

TreeMap 实现了 SortedMap 接口,它可以将保存的记录依据键进行排序。默认情况下是按照键值的升序来排序的,同时也能够指定排序的比较器。当通过 Iterator 去遍历 TreeMap 时,所得到的记录是已经排过序的。如果正在使用排序的映射,那么建议使用 TreeMap。使用 TreeMap 时,有两种情况。其一,key 要实现 Comparable 接口;其二,在构造 TreeMap 时传入自定义的 Comparator。如果不满足这两种情况,那么在运行时就会抛出 java.lang.ClassCastException 类型的异常。

5、LinkHashMap(记录插入顺序)

LinkedHashMap 是 HashMap 的一个子类。它保存了记录的插入顺序。当使用 Iterator 遍历 LinkedHashMap 时,先得到的记录必定是先插入的。同时,它也可以在构造时带参数,按照访问次序进行排序。

图看不清楚?

想要文档?

获取方式:

扫码添加晴栀小姐姐微信

备注:公众号

免费领取!!!

往期精选

CHOICENESS

是兄弟,就来“kan”我

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

27阅读

java中冒号,java双冒号

2026年09月06日

26阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

28阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

25阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

25阅读