4.1.2 银行技术面---编程语言类(Java集合)

本文章将收录在专栏《手把手带你破解银行科技岗面试》,如果你银行科技岗(研发中心、数据中心、软开中心、金融科技岗、科技人才岗)感兴趣,欢迎点击此处订阅本专栏。本专栏将手把手带你破解银行科技岗面试,学习本专栏至少可以让你知道:

  • 我到底能报考哪些银行里的哪些机构
  • 我到底是否能达到这些岗位的招聘要求
  • 我到底如何提前准备这些岗位的招聘面试

根据我的经验,目前国内大多数银行的后端研发岗的技术栈都是Java那一套,一般上提问也是围绕着Java后端研发那一套展开(也会根据你的简历调整问题),这篇文章主要记录了我收集的银行技术面中问到的Java相关的问题,有问题也有我自己总结的答案,请大家参考。

为了便于分类记忆,我将这些问题分了如下几个目录:

  • Java 语言
  • Java 集合
  • Java 并发
  • Java JVM

限于篇幅,将分多篇文章更新,本文将更新Java 集合相关内容。

一、问题列表

我将面试收集到的高频问题给放在了这里,大家可以查漏补缺

  1. 请你讲讲对 Java 集合的了解?
  2. HashMap 源码分析(超级经典,源码可挖掘的东西很多)
  3. 其他几种Map的对比
  4. 讲一下jdk7和jdk8中map的区别
  5. 讲下CopyOnWriteArrayList

二、答案参考

2.1 请你讲讲对 Java 集合的了解?

1. List,Set,Map 三者的区别?

List(对付顺序的好帮手): 存储的元素是有序的、可重复的。

Set(注重独一无二的性质): 存储的元素是无序的、不可重复的。

Map(用 Key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x”代表 key,"y"代表 value,Key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。

2. List 主要实现类?

ArrayList 常用的,数组实现的

LinkedList 底层双向链表实现

Vector 和 ArrayList 基本一样,但是线程安全的

3. Set 主要实现类

HashSet 无序不可重复

LinkedHashSet 插入有序

TreeSet 大小有序

4. Map 的主要实现类

HashMap jdk1.7 之前用数组 + 链表 之后用 数组 + 红黑树。

LinkedHashMap 底层维护了一个链表,让插入变的有序。

TreeMap 还是可以按一定规则进行排列有序。

2.2 HashMap 源码分析(超级经典,源码可挖掘的东西很多)

八股文非常重要的一问:HashMap,经典面试题。今天把我看的一篇源码解析给大家分享一下。

一个比较不错的源码理解,可以看看:

https://zhuanlan.zhihu.com/p/79219960

HashMap 主要讲清楚三个点就可以了:

  • 数据结构是如何实现的,为什么这么做?
  • 根据数据结构讲,详细讲一下增 put 是如何实现的?
  • 根据数据结构讲,扩容机制是如何实现的?

1. 数据结构

jdk1.7 的数据结构是数组 + 链表实现的

jdk1.8 的数据结构是数组 + 链表/红黑树实现的

  • 数组
transient Node<K,V>[] table;    

记住这个 table,这属于常识性的东西!

  • Node 的源码
static class Node<K,V> implements Map.Entry<K,V> 
{    
    final int hash;    
    final K key;    
    V value;    
    Node<K,V> next;    
    ………… 
}        

可见 Node 里除了 K 和 V 还有一个 next,表示链表

2. 存储元素 put

以 jdk 1.8 为例!!!

上述 put 的步骤描述如下

(1)第一步:调用 put 方法传入键值对

(2)第二步:使用 hash 算法计算 hash 值

(3)第三步:根据 hash 值确定存放的位置,判断是否和其他键发生了哈希冲突

(4)第四步:若没有发生冲突,直接存放在数组中即可

(5)第五步:若发生了冲突,还要判断此时的数据结构是什么?

(6)第六步:若此时的数据结构是红黑树,那就直接插入红黑树中

(7)第七步:若此时的数据结构是链表,判断插入之后是否大于等于8

(8)第八步:插入之后大于 8 了,就要先调整为红黑树,在插入

(9)第九步:插入之后不大于 8,那么就直接插入到链表尾部即可。

  • put 源码
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

putVal 的五个参数的意思分别是

(1)第一个参数 hash:调用了 hash() 方法计算 hash 值,把 key 的 hash 值与与其自身的高十六位进行异或得到。

(2)第二个参数 key:就是我们传入的key值,也就是例子中的张三

(3)第三个参数 value:就是我们传入的value值,也就是例子中的20

(4)第四个参数 onlyIfAbsent:也就是当键相同时,不修改已存在的值

(5)第五个参数 evict :如果为false,那么数组就处于创建模式中,所以一般为true。

  • putVal 源码
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    //第一部分
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //第二部分
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    //第三部分
    else {
        Node<K,V> e; K k;
        //第三部分第一小节
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //第三部分第二小节
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        //第三部分第三小节
        else {
            for (int binCount = 0; ; ++binCount) {
                //第三小节第一段
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                //第三小节第一段
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                //第三小节第三段
                p = e;
            }
        }
        //第三部分第四小节
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    //第四部分
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

看着非常恶心,一行一行来,对着那个流程图来看!

(1)首先是数据结构

Node<K,V>[] tab; Node<K,V> p; int n, i;

前面这个 tab 就是数组,所谓的桶;后面这个 p 与当前插入节点在同一个桶内的节点,或者说就是冲突节点n 代表 tab 的 capacityi 代表要插入的位置

(2)第一部分

//第一部分
if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;

这部分就是说,如果当前 map 的数组 table 为 null,或者 tab 的长度为 0 的话,就重新 resize 一个 table 数组,在 resize 函数内部又新建了一个 table 数组,并赋给 putVal 方法里的 tab。这部分你只需要知道这个就行了,不用知道 resize 是干什么的,之后扩容会说,这俩一定分开讨论,不要瞎几把弄。

(3)第二部分

//第二部分
if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);

此处就是框图的第二步,表示计算 hash 冲突,通过 (n - 1) & hash 算出位置,为什么要用这种方法算出桶下标的位置?因为 n 在第一部分已经赋值为容量,其容量是 2 的次幂,所以桶下标不允许超过这个下标,因此需要把 hash 的高位直接屏蔽掉,相当于求了个余。第二部分就是在说,如果没有发现 hash 冲突,桶内啥都没有,就直接放进去就 ok 了,但是如果存在 hash 冲突,就转入第三部分。

// 补充知识:计算机对 2 的次幂求余可用位运算,只对 2 次幂有效!如下例子
X & (2^N - 1) // 表示 X 对 2^N 求余
// 举一反三:计算机对 2 的次幂求除可用位运算,只对 2 次幂有效,如下例子
X >> N // 表示 X 除以 2^N 

(4)第三部分

Node<K,V> e; K k; 

e 表示已经存在节点,如果 e 不为空说明有旧节点,直接改新值就可以了。

(a) 第三部分第一小节第三部分和第二部分是 if else 关系,是判断 hash 冲突的分支,二部分表示无 hash 冲突,三部分表示有 hash 冲突一 二 三小节是处理 hash 冲突的并列的三个 if-else!

//第二部分
if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);
//第三部分 承上
else {
    Node<K,V> e; K k;
    //第三部分第一小节
    if (p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;

第 8 ~ 9 行,第一小节,判断 p(与当前节点在一个桶内的节点)的和要插入的节点的 hash 和 key 是否一致,如果一致则直接替换掉存在的 key 对应的旧值 e

(b) 第三部分第二小节

// 第三部分第二小节
else if (p instanceof TreeNode)
       e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

判断插入的数据结构是红黑树还是链表

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

本专栏主要介绍银行科技岗的面试技巧和经验,涵盖了银行科技岗可能遇到的所有面试形式。可以帮助你识别面试套路,教你如何根据自身情况提前准备好专属于自己的“面试套话、满分话术”,把面试难度降维,从而提升面试能力、改善面试效果。 如果你对总行管培岗、直属研发中心、直属数据中心、省分行科技专项人才岗感兴趣,欢迎订阅本专栏,本专栏会手把手教你如何破解面试难题,拿到心仪的offer。

全部评论
请问Java的并发和jwm还会继续更新嘛
点赞 回复 分享
发布于 03-18 12:18 上海
哇哦,脆皮鸡又更新了呢,开心开心,支持支持,顶顶顶😍😍😍
点赞 回复 分享
发布于 2023-11-01 19:38 江苏

相关推荐

鼠鼠半夜睡不着,复盘了一下之前的面试,如果连着只算技术面的话,连续通过了17轮技术面,(Momenta两面,Nebula&nbsp;Graph两面,合合信息一面,虾皮两面,美团一面,腾讯三面,字节三面,阿里三面,我视约hr面为技术面通过)虽然只是日常实习,但是还都算中大厂,我从前端的视角出发,希望能给秋招和实习的小伙伴们一些启发。我就从老生长谈的八股,手写,项目,实习经历说起吧八股大家都背八股,你背的八股如何才能算优势呢。我认为比较核心的一个点是理解上下文。没错,理解技术的上下文。每个技术都不是凭空出现的。大部分都有专门的委员会制定来解决一些问题。我拿个最常见的八股例子吧,前端经常会问浏览器的缓存还有http版本差别。这两者其实有联系,面试官问你浏览器缓存的时候,你除了答出浏览器的缓存机制,如果还能答出expires什么时候有的,在http1.1为啥又引入了cache-control。你能把技术的上下文讲给面试官听,就能展示出你的思考。再比如非常常见的一个八股,vdom的作用价值。可能有人张口就来,什么减少频繁操作dom次数,影响性能。真的是完全这样吗?那vue新版为什么增加了无虚拟dom模式,svelte也没有虚拟dom,他们就性能不好吗?多去了解技术出现的背景,虚拟dom出现之前大家都用啥,他的出现带来的哪些改变。以这种方式去思考八股,你会发现你站在不一样的高度。手写我的手写刷的并不多,但我要确保我刷一题会一题。前端的手写包括两部分,我总结为力扣算法和非力扣算法。力扣算法前端是经典的hot题目,我的建议是跟着网站或模板刷,这里我就不打广告了。核心就一点,按照模板答题,保证同一个题目每一次做你的写法都相同。另外就是非力扣,我也是建议去看别人的总结。但是非力扣题就要有自己的思考去刷题。比如面试官喜欢问的深拷贝,很多人写深拷贝没考虑过循环引用、多类型对象嵌套的情况,如果写出一个面试时完美的深拷贝?注意我说的是面试,因为真的去看开源的一些库实现,没必要,用力过猛。再比如发布订阅模式,除了最基本的订阅,取消订阅,发布这三个方法,你练习过一次订阅,一键取消等方法吗,你知道LRU算法非常常考,那你用链表实现过LRU吗?非常高频的问题,你应该做到对于面试而言的极致,而不是可以写出的程度。项目项目有人说没亮点,但是面试官会帮你找出亮点,看你能不能接住。面试官经常会说,你的项目做了XX吗?很多时候他们说的你没想到的,就是亮点,那你下次面试抓紧搞懂加上去。不一定要真的体现在项目上,你会不会比你有没有真的在项目中使用更重要。因为是面试,你实际做的但讲不出来也会被认为你是虚的。同样,很多时候并不关心你做没做,要的是面试那个时刻讲清楚方案原理,有自己的思考。项目的亮点还可以让ai帮你总结。用一款流行的ai编辑器或者插件,喂给它整个项目或者一部分,让它总结你的亮点,拓展再拓展,就成了简历上唬人的亮点。实习经历我后面的面试这么顺利,和我在Momenta的实习经历也有关系。每次自我介绍时我都会讲我的实习经历。讲什么呢?讲你在整条业务线上的位置,注意是整条。我是前端,但我不只讲前端。我会说从车端,到云仓,到前端,再到用户的整个闭环。能说清楚整体业务是非常加分的,能体现出你对业务的思考。包括联调,和客户沟通等一些技术之外的问题。这是没实习的人面试时很难讲述的,有实习经历就要放大自己的优势。在这之后呢是一些其他杂七杂八的技巧。很多人都是练习面试,那你有没有练习过面试别人呢?这个不需要多少门槛,就和以前上学时互相提问一样。拿到一份简历,去面试别人,你就能站在面试官的角度去思考了,你就会理解面试官为什么会问有些问题了。关于复习和面经。我的建议是面经在面试的最后前几天刷,作为一种抱佛脚的手段,因为如果是同一个面试官,可能会出相似的题目。前期用地毯式复习,可以但也是最好仅仅把八股网站的题目作为一个提纲,自己去扩充里面的内容,去技术社区搜每一篇技术相关的文章来看看你不了解的地方。看到最后如果觉得我讲的不错,帮我点个免费的赞吧,有什么想知道也可以告诉我,我也会尽我能力看看能不能解决
daisy9542:我觉得讲得很不错,八股谁都会背,算法谁都练,网上千篇一律的项目谁都会照着写,但面试官更看重的是你的思考,这才是属于你的唯一的东西
我的秋招日记
点赞 评论 收藏
分享
评论
7
41
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务