4.1.2 銀行技術(shù)面---編程語(yǔ)言類(Java集合)
本文章將收錄在專欄《手把手帶你破解銀行科技崗面試》,如果你對(duì)銀行科技崗(研發(fā)中心、數(shù)據(jù)中心、軟開中心、金融科技崗、科技人才崗)感興趣,歡迎點(diǎn)擊此處訂閱本專欄。本專欄將手把手帶你破解銀行科技崗面試,學(xué)習(xí)本專欄至少可以讓你知道:
- 我到底能報(bào)考哪些銀行里的哪些機(jī)構(gòu)?
- 我到底是否能達(dá)到這些崗位的招聘要求?
- 我到底如何提前準(zhǔn)備這些崗位的招聘面試?
根據(jù)我的經(jīng)驗(yàn),目前國(guó)內(nèi)大多數(shù)銀行的后端研發(fā)崗的技術(shù)棧都是Java那一套,一般上提問也是圍繞著Java后端研發(fā)那一套展開(也會(huì)根據(jù)你的簡(jiǎn)歷調(diào)整問題),這篇文章主要記錄了我收集的銀行技術(shù)面中問到的Java相關(guān)的問題,有問題也有我自己總結(jié)的答案,請(qǐng)大家參考。
為了便于分類記憶,我將這些問題分了如下幾個(gè)目錄:
- Java 語(yǔ)言
- Java 集合
- Java 并發(fā)
- Java JVM
限于篇幅,將分多篇文章更新,本文將更新Java 集合相關(guān)內(nèi)容。
一、問題列表
我將面試收集到的高頻問題給放在了這里,大家可以查漏補(bǔ)缺
- 請(qǐng)你講講對(duì) Java 集合的了解?
- HashMap 源碼分析(超級(jí)經(jīng)典,源碼可挖掘的東西很多)
- 其他幾種Map的對(duì)比
- 講一下jdk7和jdk8中map的區(qū)別
- 講下CopyOnWriteArrayList
二、答案參考
2.1 請(qǐng)你講講對(duì) Java 集合的了解?
1. List,Set,Map 三者的區(qū)別?
List(對(duì)付順序的好幫手): 存儲(chǔ)的元素是有序的、可重復(fù)的。
Set(注重獨(dú)一無二的性質(zhì)): 存儲(chǔ)的元素是無序的、不可重復(fù)的。
Map(用 Key 來搜索的專家): 使用鍵值對(duì)(key-value)存儲(chǔ),類似于數(shù)學(xué)上的函數(shù) y=f(x),“x”代表 key,"y"代表 value,Key 是無序的、不可重復(fù)的,value 是無序的、可重復(fù)的,每個(gè)鍵最多映射到一個(gè)值。
2. List 主要實(shí)現(xiàn)類?
ArrayList 常用的,數(shù)組實(shí)現(xiàn)的
LinkedList 底層雙向鏈表實(shí)現(xiàn)
Vector 和 ArrayList 基本一樣,但是線程安全的
3. Set 主要實(shí)現(xiàn)類
HashSet 無序不可重復(fù)
LinkedHashSet 插入有序
TreeSet 大小有序
4. Map 的主要實(shí)現(xiàn)類
HashMap jdk1.7 之前用數(shù)組 + 鏈表 之后用 數(shù)組 + 紅黑樹。
LinkedHashMap 底層維護(hù)了一個(gè)鏈表,讓插入變的有序。
TreeMap 還是可以按一定規(guī)則進(jìn)行排列有序。
2.2 HashMap 源碼分析(超級(jí)經(jīng)典,源碼可挖掘的東西很多)
八股文非常重要的一問:HashMap,經(jīng)典面試題。今天把我看的一篇源碼解析給大家分享一下。
一個(gè)比較不錯(cuò)的源碼理解,可以看看:
https://zhuanlan.zhihu.com/p/79219960
HashMap 主要講清楚三個(gè)點(diǎn)就可以了:
- 數(shù)據(jù)結(jié)構(gòu)是如何實(shí)現(xiàn)的,為什么這么做?
- 根據(jù)數(shù)據(jù)結(jié)構(gòu)講,詳細(xì)講一下增 put 是如何實(shí)現(xiàn)的?
- 根據(jù)數(shù)據(jù)結(jié)構(gòu)講,擴(kuò)容機(jī)制是如何實(shí)現(xiàn)的?
1. 數(shù)據(jù)結(jié)構(gòu)
jdk1.7 的數(shù)據(jù)結(jié)構(gòu)是數(shù)組 + 鏈表實(shí)現(xiàn)的
jdk1.8 的數(shù)據(jù)結(jié)構(gòu)是數(shù)組 + 鏈表/紅黑樹實(shí)現(xiàn)的
- 數(shù)組
transient Node<K,V>[] table;
記住這個(gè) table,這屬于常識(shí)性的東西!
- 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 還有一個(gè) next,表示鏈表
2. 存儲(chǔ)元素 put
以 jdk 1.8 為例!??!
上述 put 的步驟描述如下
(1)第一步:調(diào)用 put 方法傳入鍵值對(duì)
(2)第二步:使用 hash 算法計(jì)算 hash 值
(3)第三步:根據(jù) hash 值確定存放的位置,判斷是否和其他鍵發(fā)生了哈希沖突
(4)第四步:若沒有發(fā)生沖突,直接存放在數(shù)組中即可
(5)第五步:若發(fā)生了沖突,還要判斷此時(shí)的數(shù)據(jù)結(jié)構(gòu)是什么?
(6)第六步:若此時(shí)的數(shù)據(jù)結(jié)構(gòu)是紅黑樹,那就直接插入紅黑樹中
(7)第七步:若此時(shí)的數(shù)據(jù)結(jié)構(gòu)是鏈表,判斷插入之后是否大于等于8
(8)第八步:插入之后大于 8 了,就要先調(diào)整為紅黑樹,在插入
(9)第九步:插入之后不大于 8,那么就直接插入到鏈表尾部即可。
- put 源碼
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
putVal 的五個(gè)參數(shù)的意思分別是
(1)第一個(gè)參數(shù) hash:調(diào)用了 hash() 方法計(jì)算 hash 值,把 key 的 hash 值與與其自身的高十六位進(jìn)行異或得到。
(2)第二個(gè)參數(shù) key:就是我們傳入的key值,也就是例子中的張三
(3)第三個(gè)參數(shù) value:就是我們傳入的value值,也就是例子中的20
(4)第四個(gè)參數(shù) onlyIfAbsent:也就是當(dāng)鍵相同時(shí),不修改已存在的值
(5)第五個(gè)參數(shù) evict :如果為false,那么數(shù)組就處于創(chuàng)建模式中,所以一般為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; //第三部分第一小節(jié) if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; //第三部分第二小節(jié) else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //第三部分第三小節(jié) else { for (int binCount = 0; ; ++binCount) { //第三小節(jié)第一段 if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } //第三小節(jié)第一段 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; //第三小節(jié)第三段 p = e; } } //第三部分第四小節(jié) 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; }
看著非常惡心,一行一行來,對(duì)著那個(gè)流程圖來看!
(1)首先是數(shù)據(jù)結(jié)構(gòu)
Node<K,V>[] tab; Node<K,V> p; int n, i;
前面這個(gè) tab 就是數(shù)組,所謂的桶;后面這個(gè) p 與當(dāng)前插入節(jié)點(diǎn)在同一個(gè)桶內(nèi)的節(jié)點(diǎn),或者說就是沖突節(jié)點(diǎn)n 代表 tab 的 capacityi 代表要插入的位置
(2)第一部分
//第一部分 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length;
這部分就是說,如果當(dāng)前 map 的數(shù)組 table 為 null,或者 tab 的長(zhǎng)度為 0 的話,就重新 resize 一個(gè) table 數(shù)組,在 resize 函數(shù)內(nèi)部又新建了一個(gè) table 數(shù)組,并賦給 putVal 方法里的 tab。這部分你只需要知道這個(gè)就行了,不用知道 resize 是干什么的,之后擴(kuò)容會(huì)說,這倆一定分開討論,不要瞎幾把弄。
(3)第二部分
//第二部分 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null);
此處就是框圖的第二步,表示計(jì)算 hash 沖突,通過 (n - 1) & hash 算出位置,為什么要用這種方法算出桶下標(biāo)的位置?因?yàn)?n 在第一部分已經(jīng)賦值為容量,其容量是 2 的次冪,所以桶下標(biāo)不允許超過這個(gè)下標(biāo),因此需要把 hash 的高位直接屏蔽掉,相當(dāng)于求了個(gè)余。第二部分就是在說,如果沒有發(fā)現(xiàn) hash 沖突,桶內(nèi)啥都沒有,就直接放進(jìn)去就 ok 了,但是如果存在 hash 沖突,就轉(zhuǎn)入第三部分。
// 補(bǔ)充知識(shí):計(jì)算機(jī)對(duì) 2 的次冪求余可用位運(yùn)算,只對(duì) 2 次冪有效!如下例子 X & (2^N - 1) // 表示 X 對(duì) 2^N 求余 // 舉一反三:計(jì)算機(jī)對(duì) 2 的次冪求除可用位運(yùn)算,只對(duì) 2 次冪有效,如下例子 X >> N // 表示 X 除以 2^N
(4)第三部分
Node<K,V> e; K k;
e 表示已經(jīng)存在節(jié)點(diǎn),如果 e 不為空說明有舊節(jié)點(diǎn),直接改新值就可以了。
? (a) 第三部分第一小節(jié)第三部分和第二部分是 if else 關(guān)系,是判斷 hash 沖突的分支,二部分表示無 hash 沖突,三部分表示有 hash 沖突一 二 三小節(jié)是處理 hash 沖突的并列的三個(gè) if-else!
//第二部分 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); //第三部分 承上 else { Node<K,V> e; K k; //第三部分第一小節(jié) if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p;
第 8 ~ 9 行,第一小節(jié),判斷 p(與當(dāng)前節(jié)點(diǎn)在一個(gè)桶內(nèi)的節(jié)點(diǎn))的和要插入的節(jié)點(diǎn)的 hash 和 key 是否一致,如果一致則直接替換掉存在的 key 對(duì)應(yīng)的舊值 e
? (b) 第三部分第二小節(jié)
// 第三部分第二小節(jié) else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
判斷插入的數(shù)據(jù)結(jié)構(gòu)是紅黑樹還是鏈表
剩余60%內(nèi)容,訂閱專欄后可繼續(xù)查看/也可單篇購(gòu)買
本專欄主要介紹銀行科技崗的面試技巧和經(jīng)驗(yàn),涵蓋了銀行科技崗可能遇到的所有面試形式??梢詭椭阕R(shí)別面試套路,教你如何根據(jù)自身情況提前準(zhǔn)備好專屬于自己的“面試套話、滿分話術(shù)”,把面試難度降維,從而提升面試能力、改善面試效果。 如果你對(duì)總行管培崗、直屬研發(fā)中心、直屬數(shù)據(jù)中心、省分行科技專項(xiàng)人才崗感興趣,歡迎訂閱本專欄,本專欄會(huì)手把手教你如何破解面試難題,拿到心儀的offer。