當(dāng)前位置:首頁(yè) > IT技術(shù) > 移動(dòng)平臺(tái) > 正文

2020至2021年Android開(kāi)發(fā)面試習(xí)題整理,持續(xù)更新中
2021-09-24 14:53:22

設(shè)計(jì)模式面試題

1.請(qǐng)列舉出在 JDK 中幾個(gè)常用的設(shè)計(jì)模式?

單例模式(Singleton pattern)用于 Runtime,Calendar 和其他的一些類(lèi)中。工廠模式 (Factory pattern)被用于各種不可變的類(lèi)如 Boolean,像 Boolean.valueOf,觀察者模式 (Observer pattern)被用于 Swing 和很多的事件監(jiān)聽(tīng)中。裝飾器設(shè)計(jì)模式(Decorator design pattern)被用于多個(gè) Java IO 類(lèi)中。

2.什么是設(shè)計(jì)模式?你是否在你的代碼里面使用過(guò)任何設(shè)計(jì)模式?

設(shè)計(jì)模式是世界上各種各樣程序員用來(lái)解決特定設(shè)計(jì)問(wèn)題的嘗試和測(cè)試的方法。設(shè)計(jì)模式是代碼可用性的延伸

3.Java 中什么叫單例設(shè)計(jì)模式?請(qǐng)用 Java 寫(xiě)出線程安全的單例模式

單例模式重點(diǎn)在于在整個(gè)系統(tǒng)上共享一些創(chuàng)建時(shí)較耗資源的對(duì)象。整個(gè)應(yīng)用中只維護(hù)一個(gè)特定類(lèi)實(shí)例,它被所有組件共同使用。Java.lang.Runtime 是單例模式的經(jīng)典例子。從 Java 5 開(kāi)始你可以使用枚舉(enum)來(lái)實(shí)現(xiàn)線程安全的單例。

4.在 Java 中,什么叫觀察者設(shè)計(jì)模式(observer design pattern)?

觀察者模式是基于對(duì)象的狀態(tài)變化和觀察者的通訊,以便他們作出相應(yīng)的操作。簡(jiǎn)單的例子就是一個(gè)天氣系統(tǒng),當(dāng)天氣變化時(shí)必須在展示給公眾的視圖中進(jìn)行反映。這個(gè)視圖對(duì)象是一個(gè)主體,而不同的視圖是觀察者。

5.使用工廠模式最主要的好處是什么?在哪里使用?

工廠模式的最大好處是增加了創(chuàng)建對(duì)象時(shí)的封裝層次。如果你使用工廠來(lái)創(chuàng)建對(duì)象,之后你可以使用更高級(jí)和更高性能的實(shí)現(xiàn)來(lái)替換原始的產(chǎn)品實(shí)現(xiàn)或類(lèi),這不需要在調(diào)用層做任何修改。

6.舉一個(gè)用 Java 實(shí)現(xiàn)的裝飾模式(decorator design pattern)?它是作用于對(duì)象層次還是類(lèi)層次?

裝飾模式增加強(qiáng)了單個(gè)對(duì)象的能力。Java IO 到處都使用了裝飾模式,典型例子就是Buffered 系列類(lèi)如 BufferedReader 和 BufferedWriter,它們?cè)鰪?qiáng)了 Reader 和 Writer 對(duì)象,以實(shí)現(xiàn)提升性能的 Buffer 層次的讀取和寫(xiě)入。

7.在 Java 中,為什么不允許從靜態(tài)方法中訪問(wèn)非靜態(tài)變量?

Java 中不能從靜態(tài)上下文訪問(wèn)非靜態(tài)數(shù)據(jù)只是因?yàn)榉庆o態(tài)變量是跟具體的對(duì)象實(shí)例關(guān)聯(lián)的,而靜態(tài)的卻沒(méi)有和任何實(shí)例關(guān)聯(lián)。

8.設(shè)計(jì)一個(gè) ATM 機(jī),請(qǐng)說(shuō)出你的設(shè)計(jì)思路?

比如設(shè)計(jì)金融系統(tǒng)來(lái)說(shuō),必須知道它們應(yīng)該在任何情況下都能夠正常工作。不管是斷電還是其他情況,ATM 應(yīng)該保持正確的狀態(tài)(事務(wù)) , 想想 加鎖(locking)、事務(wù)(transaction)、錯(cuò)誤條件(error condition)、邊界條件(boundary condition) 等等。盡管你不能想到具體的設(shè)計(jì),但如果你可以指出非功能性需求,提出一些問(wèn)題,想到關(guān)于邊界條件,這些都會(huì)是很好的。

9.在 Java 中,什么時(shí)候用重載,什么時(shí)候用重寫(xiě)?

如果你看到一個(gè)類(lèi)的不同實(shí)現(xiàn)有著不同的方式來(lái)做同一件事,那么就應(yīng)該用重寫(xiě)(overriding),而重載(overloading)是用不同的輸入做同一件事。在 Java 中,重載的方法簽名不同,而重寫(xiě)并不是。

10.舉例說(shuō)明什么情況下會(huì)更傾向于使用抽象類(lèi)而不是接口?

接口和抽象類(lèi)都遵循”面向接口而不是實(shí)現(xiàn)編碼”設(shè)計(jì)原則,它可以增加代碼的靈活性,可以適應(yīng)不斷變化的需求。下面有幾個(gè)點(diǎn)可以幫助你回答這個(gè)問(wèn)題:

在 Java 中,你只能繼承一個(gè)類(lèi),但可以實(shí)現(xiàn)多個(gè)接口。所以一旦你繼承了一個(gè)類(lèi),你就失去了繼承其他類(lèi)的機(jī)會(huì)了。

接口通常被用來(lái)表示附屬描述或行為如:Runnable、Clonable、Serializable 等等,因此當(dāng)你使用抽象類(lèi)來(lái)表示行為時(shí),你的類(lèi)就不能同時(shí)是 Runnable 和 Clonable(注:這里的意思是指如果把 Runnable 等實(shí)現(xiàn)為抽象類(lèi)的情況),因?yàn)樵?Java 中你不能繼承兩個(gè)類(lèi),但當(dāng)你使用 接口時(shí),你的類(lèi)就可以同時(shí)擁有多個(gè)不同的行為。

在一些對(duì)時(shí)間要求比較高的應(yīng)用中,傾向于使用抽象類(lèi),它會(huì)比接口稍快一點(diǎn)。

如果希望把一系列行為都規(guī)范在類(lèi)繼承層次內(nèi),并且可以更好地在同一個(gè)地方進(jìn)行編碼,那么抽象類(lèi)是一個(gè)更好的選擇。有時(shí),接口和抽象類(lèi)可以一起使用,接口中定義函數(shù),而在抽象類(lèi)中定義默認(rèn)的實(shí)現(xiàn)。2020至2021年Android開(kāi)發(fā)面試習(xí)題整理,持續(xù)更新中_數(shù)據(jù)結(jié)構(gòu)

數(shù)據(jù)結(jié)構(gòu)面試題

1、常用數(shù)據(jù)結(jié)構(gòu)有哪些?

數(shù)據(jù)結(jié)構(gòu)是指相互之間存在著一種或多種關(guān)系的數(shù)據(jù)元素的集合和該集合中數(shù)據(jù)元素間的關(guān)系組成。常用的數(shù)據(jù)有:數(shù)組、棧、隊(duì)列、鏈表、樹(shù)、圖、堆、散列表。

1)數(shù)組:在內(nèi)存中連續(xù)存儲(chǔ)多個(gè)元素的結(jié)構(gòu)。數(shù)組元素通過(guò)下標(biāo)訪問(wèn),下標(biāo)從0開(kāi)始。優(yōu)點(diǎn):訪問(wèn)速度快;缺點(diǎn):數(shù)組大小固定后無(wú)法擴(kuò)容,只能存儲(chǔ)一種類(lèi)型的數(shù)據(jù),添加刪除操作慢。適用場(chǎng)景:適用于需頻繁查找,對(duì)存儲(chǔ)空間要求不高,很少添加刪除。

2)棧:一種特殊的線性表,只可以在棧頂操作,先進(jìn)后出,從棧頂放入元素叫入棧,從棧頂取出元素叫出棧。應(yīng)用場(chǎng)景:用于實(shí)現(xiàn)遞歸功能,如斐波那契數(shù)列。

3)隊(duì)列:一種線性表,在列表一端添加元素,另一端取出,先進(jìn)先出。使用場(chǎng)景:多線程阻塞隊(duì)列管理中。

4)鏈表:物理存儲(chǔ)單元上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表的指針地址實(shí)現(xiàn),每個(gè)元素包含兩個(gè)結(jié)點(diǎn),一個(gè)是存儲(chǔ)元素的數(shù)據(jù)域,一個(gè)是指向下一個(gè)結(jié)點(diǎn)地址的指針域。有單鏈表、雙向鏈表、循環(huán)鏈表。優(yōu)點(diǎn):可以任意加減元素,不需要初始化容量,添加刪除元素只需改變前后兩個(gè)元素結(jié)點(diǎn)的指針域即可。缺點(diǎn):因?yàn)楹写罅恐羔樣颍陶加每臻g大,查找耗時(shí)。適用場(chǎng)景:數(shù)據(jù)量小,需頻繁增加刪除操作。

5)樹(shù):由n個(gè)有限節(jié)點(diǎn)組成一種具有層次關(guān)系的集合。二叉樹(shù)(每個(gè)結(jié)點(diǎn)最多有兩個(gè)子樹(shù),結(jié)點(diǎn)的度最大為2,左子樹(shù)和右子樹(shù)有順序)、紅黑樹(shù)(HashMap底層源碼)、B+樹(shù)(mysql的數(shù)據(jù)庫(kù)索引結(jié)構(gòu))

6)散列表(哈希表):根據(jù)鍵值對(duì)來(lái)存儲(chǔ)訪問(wèn)。

7)堆:堆中某個(gè)節(jié)點(diǎn)的值總是不大于或不小于其父節(jié)點(diǎn)的值,堆總是一棵完全二叉樹(shù)。

8)圖:由結(jié)點(diǎn)的有窮集合V和邊的集合E組成。

2、并發(fā)集合了解哪些?

1)并發(fā)List,包括Vector和CopyOnWriteArrayList是兩個(gè)線程安全的List,Vector讀寫(xiě)操作都用了同步,CopyOnWriteArrayList在寫(xiě)的時(shí)候會(huì)復(fù)制一個(gè)副本,對(duì)副本寫(xiě),寫(xiě)完用副本替換原值,讀時(shí)不需要同步。

2)并發(fā)Set,CopyOnWriteArraySet基于CopyOnWriteArrayList來(lái)實(shí)現(xiàn)的,不允許存在重復(fù)的對(duì)象。

3)并發(fā)Map,ConcurrentHashMap,內(nèi)部實(shí)現(xiàn)了鎖分離,get操作是無(wú)鎖的。

4)并發(fā)Queue,ConcurrentLinkedQueue適用于高并發(fā)場(chǎng)景下的隊(duì)列,通過(guò)無(wú)鎖方式實(shí)現(xiàn)。 BlockingQueue阻塞隊(duì)列,應(yīng)用場(chǎng)景,生產(chǎn)者-消費(fèi)者模式,若生產(chǎn)快于消費(fèi),生產(chǎn)隊(duì)列裝滿(mǎn)時(shí)會(huì)阻塞,等待消費(fèi)。

5)并發(fā)Deque, LinkedBlockingDueue沒(méi)有進(jìn)行讀寫(xiě)鎖分離,同一時(shí)間只能有一個(gè)線程對(duì)其操作。

6)并發(fā)鎖重入鎖ReentrantLock,互斥鎖,一次最多只能一個(gè)線程拿到鎖。

7)讀寫(xiě)鎖ReadWriteLock,有讀取和寫(xiě)入鎖兩種,讀取允許多個(gè)讀取線程同時(shí)持有,而寫(xiě)入只能有一個(gè)線程持有。

3、列舉java的集合以及集合之間的繼承關(guān)系

4、容器類(lèi)介紹以及之間的區(qū)別

1)Collection接口:集合框架的根接口,它是集合類(lèi)框架中最具一般性的頂層接口。

2)Map接口:提供了鍵值對(duì)的映射關(guān)系的集合,關(guān)鍵字不能有重復(fù)值,每個(gè)關(guān)鍵字至多可映射一個(gè)值。HashMap(通過(guò)散列機(jī)制,用于快速訪問(wèn)),TreeMap(保持key處于排序狀態(tài),訪問(wèn)速度不如hashmap), LinkedHashMap(保持元素的插入順序)

3)Set接口:可包含重復(fù)的元素,LinkedHashSet TreeSet(用紅黑樹(shù)來(lái)存儲(chǔ)元素) HashSet

4)List接口:可通過(guò)索引對(duì)元素進(jìn)行精準(zhǔn)的插入和查找,實(shí)現(xiàn)類(lèi)有ArrayList LinkedList

5)Queue接口:繼承自Collection接口,LinkedList實(shí)現(xiàn)了Queue接口,提供了支持隊(duì)列的行為。

6)Iterator接口:為了迭代集合

7)Comparable接口:用于比較

5、List,Set,Map的區(qū)別

Set是一個(gè)無(wú)序的集合,不能包含重復(fù)的元素;

list是一個(gè)有序的集合可以包含重復(fù)的元素,提供了按索引訪問(wèn)的方式;

map包含了key-value對(duì),map中key必須唯一,value可以重復(fù)。

6、HashMap的實(shí)現(xiàn)原理

1)數(shù)據(jù)結(jié)構(gòu)

jdk1.7及以前,HashMap由數(shù)組+鏈表組成,數(shù)組Entry是HashMap的主體,Entry是HashMap中的一個(gè)靜態(tài)內(nèi)部類(lèi),每一個(gè)Entry包含一個(gè)key-value鍵值對(duì),鏈表是為解決哈希沖突而存在。

從jdk1.8起,HashMap是由數(shù)組+鏈表/紅黑樹(shù)組成,當(dāng)某個(gè)bucket位置的鏈表長(zhǎng)度達(dá)到閥值8時(shí),這個(gè)鏈表就轉(zhuǎn)變成紅黑樹(shù)。

2)HashMap是線程不安全的,存儲(chǔ)比較快,能接受null值,HashMap通過(guò)put(key, value)來(lái)儲(chǔ)存元素,通過(guò)get(key)來(lái)得到value值,通過(guò)hash算法來(lái)計(jì)算hashcode值,用hashcode標(biāo)識(shí)Entry在bucket中存儲(chǔ)的位置。

3)HashMap中為什么要使用加載因子,為什么要進(jìn)行擴(kuò)容

加載因子是指當(dāng)HashMap中存儲(chǔ)的元素/最大空間值的閥值,如果超過(guò)這個(gè)值,就會(huì)進(jìn)行擴(kuò)容。加載因子是為了讓空間得到充分利用,如果加載因子太大,雖對(duì)空間利用更充分,但查找效率會(huì)降低;如果加載因子太小,表中的數(shù)據(jù)過(guò)于稀疏,很多空間還沒(méi)用就開(kāi)始擴(kuò)容,就會(huì)對(duì)空間造成浪費(fèi)。

至于為什么要擴(kuò)容,如果不擴(kuò)容,HashMap中數(shù)組處的鏈表會(huì)越來(lái)越長(zhǎng),這樣查找效率就會(huì)大大降低。

6.1 HashMap如何put數(shù)據(jù)(從HashMap源碼角度講解)?

當(dāng)我們使用put(key, value)存儲(chǔ)對(duì)象到HashMap中時(shí),具體實(shí)現(xiàn)步驟如下:

1)先判斷table數(shù)組是否為空,為空以默認(rèn)大小構(gòu)建table,table默認(rèn)空間大小為16

2)計(jì)算key的hash值,并計(jì)算hash&(n-1)值得到在數(shù)組中的位置index,如果該位置沒(méi)值即table[index]為空,則直接將該鍵值對(duì)存放在table[index]處。

3)如果table[index]處不為空,說(shuō)明發(fā)生了hash沖突,判斷table[index]處結(jié)點(diǎn)是否是TreeNode(紅黑樹(shù)結(jié)點(diǎn))類(lèi)型數(shù)據(jù),如果是則執(zhí)行putTreeVal方法,按紅黑樹(shù)規(guī)則將鍵值對(duì)存入;

4)如果table[index]是鏈表形式,遍歷該鏈表上的數(shù)據(jù),將該鍵值對(duì)放在table[index]處,并將其指向原index處的鏈表。判斷鏈表上的結(jié)點(diǎn)數(shù)是否大于鏈表最大結(jié)點(diǎn)限制(默認(rèn)為8),如果超過(guò)了需執(zhí)行treeifyBin()操作,則要將該鏈表轉(zhuǎn)換成紅黑樹(shù)結(jié)構(gòu)。

5)判斷HashMap中數(shù)據(jù)個(gè)數(shù)是否超過(guò)了(最大容量*裝載因子),如果超過(guò)了,還需要對(duì)其進(jìn)行擴(kuò)容操作。

6.2 HashMap如何get數(shù)據(jù)?

get(key)方法獲取key的hash值,計(jì)算hash&(n-1)得到在鏈表數(shù)組中的位置first=table[hash&(n-1)],先判斷first(即數(shù)組中的那個(gè))的key是否與參數(shù)key相等,不等的話,判斷結(jié)點(diǎn)是否是TreeNode類(lèi)型,是則調(diào)用getTreeNode(hash, key)從二叉樹(shù)中查找結(jié)點(diǎn),不是TreeNode類(lèi)型說(shuō)明還是鏈表型,就遍歷鏈表找到相同的key值返回對(duì)應(yīng)的value值即可。

6.3 當(dāng)兩個(gè)對(duì)象的hashcode相同,即發(fā)生碰撞時(shí),HashMap如何處理

當(dāng)兩個(gè)對(duì)象的hashcode相同,它們的bucket位置相同,hashMap會(huì)用鏈表或是紅黑樹(shù)來(lái)存儲(chǔ)對(duì)象。Entry類(lèi)里有一個(gè)next屬性,作用是指向下一個(gè)Entry。第一個(gè)鍵值對(duì)A進(jìn)來(lái),通過(guò)計(jì)算其key的hash得到index,記做Entry[index]=A。一會(huì)又進(jìn)來(lái)一個(gè)鍵值對(duì)B,通過(guò)計(jì)算其key的hash也是index,HashMap會(huì)將B.next=A, Entry[index]=B.如果又進(jìn)來(lái)C,其key的hash也是index,會(huì)將C.next=B, Entry[index]=C.這樣bucket為index的地方存放了ABC三個(gè)鍵值對(duì),它們能過(guò)next屬性鏈在一起。數(shù)組中存儲(chǔ)的是最后插入的元素,其他元素都在后面的鏈表里。

6.4 如果兩個(gè)鍵的hashcode相同,如何獲取值對(duì)象?

當(dāng)調(diào)用get方法時(shí),hashmap會(huì)使用鍵對(duì)象的hashcode找到bucket位置,找到bucket位置后,會(huì)調(diào)用key.equals()方法去找到鏈表中正確的節(jié)點(diǎn),最終找到值對(duì)象。

6.5 hashMap如何擴(kuò)容

HashMap默認(rèn)負(fù)載因?yàn)槭?.75,當(dāng)一個(gè)map填滿(mǎn)了75%的bucket時(shí),和其他集合類(lèi)一樣,將會(huì)創(chuàng)建原來(lái)HashMap大小兩倍的bucket數(shù)組,來(lái)重新調(diào)整HashMap的大小,并將原來(lái)的對(duì)象放入新的bucket數(shù)組中。

在jdk1.7及以前,多線程擴(kuò)容可能出現(xiàn)死循環(huán)。因?yàn)樵谡{(diào)整大小過(guò)程中,存儲(chǔ)在某個(gè)bucket位置中的鏈表元素次序會(huì)反過(guò)來(lái),而多線程情況下可能某個(gè)線程翻轉(zhuǎn)完鏈表,另外一個(gè)線程又開(kāi)始翻轉(zhuǎn),條件競(jìng)爭(zhēng)發(fā)生了,那么就死循環(huán)了。

而在jdk1.8中,會(huì)將原來(lái)鏈表結(jié)構(gòu)保存至節(jié)點(diǎn)e中,將原來(lái)數(shù)組中的位置設(shè)為null,然后依次遍歷e,根據(jù)hash&n是否為0分成兩條支鏈,保存在新數(shù)組中。如果多線程情況可能會(huì)取到null值造成數(shù)據(jù)丟失。

7、ConcurrentHashMap的實(shí)現(xiàn)原理

1)jdk1.7及以前:一個(gè)ConcurrentHashMap由一個(gè)segment數(shù)組和多個(gè)HashEntry組成,每一個(gè)segment都包含一個(gè)HashEntry數(shù)組, Segment繼承ReentrantLock用來(lái)充當(dāng)鎖角色,每一個(gè)segment包含了對(duì)自己的HashEntry的操作,如getput eplace操作,這些操作發(fā)生時(shí),對(duì)自己的HashEntry進(jìn)行鎖定。由于每一個(gè)segment寫(xiě)操作只鎖定自己的HashEntry,可以存在多個(gè)線程同時(shí)寫(xiě)的情況。

jdk1.8以后:ConcurrentHashMap取消了segments字段,采用transient volatile HashEntry<K, V> table保存數(shù)據(jù),采用table數(shù)組元素作為鎖,實(shí)現(xiàn)對(duì)每一個(gè)數(shù)組數(shù)據(jù)進(jìn)行加鎖,進(jìn)一小減少并發(fā)沖突概率。ConcurrentHashMap是用Node數(shù)組+鏈表+紅黑樹(shù)數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)的,并發(fā)制定用synchronized和CAS操作。

2)Segment實(shí)現(xiàn)了ReentrantLock重入鎖,當(dāng)執(zhí)行put操作,會(huì)進(jìn)行第一次key的hash來(lái)定位Segment的位置,若該Segment還沒(méi)有初始化,會(huì)通過(guò)CAS操作進(jìn)行賦值,再進(jìn)行第二次hash操作,找到相應(yīng)的HashEntry位置。

8、ArrayMap和HashMap的對(duì)比

1)存儲(chǔ)方式不一樣,HashMap內(nèi)部有一個(gè)Node<K,V>[]對(duì)象,每個(gè)鍵值對(duì)都會(huì)存儲(chǔ)到這個(gè)對(duì)象里,當(dāng)用put方法添加鍵值對(duì)時(shí),會(huì)new一個(gè)Node對(duì)象,tab[i] = newNode(hash, key, value, next);

ArrayMap存儲(chǔ)則是由兩個(gè)數(shù)組來(lái)維護(hù),int[] mHashes; Object[] mArray; mHashes數(shù)組中保存的是每一項(xiàng)的HashCode值,mArray存的是鍵值對(duì),每?jī)蓚€(gè)元素代表一個(gè)鍵值對(duì),前面保存key,后面保存value。mHashes[index]=hash; mArray[index<<1]=key; mArray[(index<<1)+1]=value;

ArrayMap相對(duì)于HashMap,無(wú)需為每個(gè)鍵值對(duì)創(chuàng)建Node對(duì)象,且在數(shù)組中連續(xù)存放,更省空間。

2)添加數(shù)據(jù)時(shí)擴(kuò)容處理不一樣,進(jìn)行了new操作,重新創(chuàng)建對(duì)象,開(kāi)銷(xiāo)很大;而ArrayMap用的是copy數(shù)據(jù),所有效率相對(duì)高些;

3)ArrayMap提供了數(shù)組收縮功能,在clear或remove后,會(huì)重新收縮數(shù)組,釋放空間;

4)ArrayMap采用二分法查找,mHashes中的hash值是按照從小到大的順序連續(xù)存放的,通過(guò)二分查找來(lái)獲取對(duì)應(yīng)hash下標(biāo)index,去mArray中查找鍵值對(duì)。mHashes中的index2是mArray中的key下標(biāo),index2+1為value的下標(biāo),由于存在hash碰撞情況,二分查找到的下標(biāo)可能是多個(gè)連續(xù)相同的hash值中的任意一個(gè),此時(shí)需要用equals比對(duì)命中的key對(duì)象是否相等,不相等,應(yīng)當(dāng)從當(dāng)前index先向后再向前遍歷所有相同hash值。

5)sparseArray比ArrayMap進(jìn)一步優(yōu)化空間,SparseArray專(zhuān)門(mén)對(duì)基本類(lèi)型做了優(yōu)化,Key只能是可排序的基本類(lèi)型,如intlong,對(duì)value,除了泛型Value,還對(duì)每種基本類(lèi)型有單獨(dú)實(shí)現(xiàn),如SparseBooleanArraySparseLongArray等。無(wú)需包裝,直接使用基本類(lèi)型值,無(wú)需hash,直接使用基本類(lèi)型值索引和判斷相等,無(wú)碰撞,無(wú)需調(diào)用hashCode方法,無(wú)需equals比較。SparseArray延遲刪除。

9、HashTable實(shí)現(xiàn)原理

Hashtable中的無(wú)參構(gòu)造方法Hashtable()中調(diào)用了this(11, 0.75f),說(shuō)明它默認(rèn)容量是11,加載因子是0.75,在構(gòu)造方法上會(huì)new HashtableEntry[initialCapacity]; 會(huì)新建一個(gè)容量是初始容量的HashtableEntry數(shù)組。HashtableEntry數(shù)組中包含hashKeyValue ext變量,鏈表形式,重寫(xiě)了hashCode和equals方法。Hashtable所有public方法都在方法體上加上了synchronized鎖操作,說(shuō)明它是線程安全的。它還實(shí)現(xiàn)了Serializable接口中的writeObject和readObject方法,分別實(shí)現(xiàn)了逐行讀取和寫(xiě)入的功能,并且加了synchronized鎖操作。

(1) put(Key, Value)方法

1)先判斷value是否為空,為空拋出空指針異常;

2)根據(jù)key的hashCode()值,計(jì)算table表中的位置索引(hash&0x7FFFFFFF)%tab.length值index,如果該索引處有值,再判斷該索引處鏈表中是否包含相同的key,如果key值相同則替換舊值。

3)如果沒(méi)有相同的key值,調(diào)用addEntry方法,在addEntry中判斷count大小是否超過(guò)了最大容量限制,如果超過(guò)了需要重新rehash(),容量變成原來(lái)容量*2+1,將原表中的值都重新計(jì)算hash值放入新表中。再構(gòu)造一個(gè)HashtableEntry對(duì)象放入相應(yīng)的table表頭,如果原索引處有值,則將table[index].next指向原索引處的鏈表。

(2)get方法

根所key.hashCode(),計(jì)算它在table表中的位置,(hash&0x7FFFFFFF)%tab.length,遍歷該索引處表的位置中是否有值,是否存在鏈表,再判斷是key值和hash值是否相等,相等則返回對(duì)應(yīng)的value值。

10、HashMap和HashTable的區(qū)別

1)Hashtable是個(gè)線程安全的類(lèi),在對(duì)外方法都添加了synchronized方法,序列化方法上也添加了synchronized同步鎖方法,而HashMap非線程安全。這也導(dǎo)致Hashtable的讀寫(xiě)等操作比HashMap慢。

2)Hashtable不允許值和鍵為空,若為空會(huì)拋出空指針。而HashMap允許鍵和值為空;

3)Hashtable根據(jù)key值的hashCode計(jì)算索引,(hash&0x7FFFFFFF)%tab.length,保證hash值始終為正數(shù)且不超過(guò)表的長(zhǎng)度。而HashMap中計(jì)算索引值是通過(guò)hash(key)&(tab.length-1),是通過(guò)與操作,計(jì)算出在表中的位置會(huì)比Hashtable快。

4)Hashtable容量能為任意大于等于1的正數(shù),而HashMap的容量必須為2^n,Hashtable默認(rèn)容量為11,HashMap初始容量為16

5)Hashtable每次擴(kuò)容,新容量為舊容量的2倍+1,而HashMap為舊容量的2倍。

11、HashMap與HashSet的區(qū)別 HashSet底層實(shí)現(xiàn)是HashMap,內(nèi)部包含一個(gè)HashMap<E, Ojbect> map變量

private transient HashMap<E,Object> map; 一個(gè)Object PRESENT變量(當(dāng)成插入map中的value值)

private static final Object PRESENT = new Object(); HashSet中元素都存到HashMap鍵值對(duì)的Key上面。具體可以查看HashSet的add方法,直接調(diào)用了HashMap的put方法,將值作為HashMap的鍵,值用一個(gè)固定的PRESENT值。

public boolean add(E e) {
return map.put(e, PRESENT)==null;
}


HashSet沒(méi)有單獨(dú)的get方法,用的是HashMap的。HashSet實(shí)現(xiàn)了Set接口,不允許集合中出現(xiàn)重復(fù)元素,將對(duì)象存儲(chǔ)進(jìn)HashSet前,要先確保對(duì)象重寫(xiě)了hashCode()和equals方法,以保證放入set對(duì)象是唯一的。

12、HashSet與HashMap怎么判斷集合元素重復(fù)?

HashMap在放入key-value鍵值對(duì)是,先通過(guò)key計(jì)算其hashCode()值,再與tab.length-1做與操作,確定下標(biāo)index處是否有值,如果有值,再調(diào)用key對(duì)象的equals方法,對(duì)象不同則插入到表頭,相同則覆蓋;

HashSet是將數(shù)據(jù)存放到HashMap的key中,HashMap是key-value形式的數(shù)據(jù)結(jié)構(gòu),它的key是唯一的,HashSet利用此原理保證放入的對(duì)象唯一性。

13、集合Set實(shí)現(xiàn)Hash怎么防止碰撞

HashSet底層實(shí)現(xiàn)是HashMap,HashMap如果兩個(gè)不同Key對(duì)象的hashCode()值相等,會(huì)用鏈表存儲(chǔ),HashSet也一樣。

14、ArrayList和LinkedList的區(qū)別,以及應(yīng)用場(chǎng)景

ArrayList底層是用數(shù)組實(shí)現(xiàn)的,隨著元素添加,其大小是動(dòng)態(tài)增大的;在內(nèi)存中是連續(xù)存放的;如果在集合末尾添加或刪除元素,所用時(shí)間是一致的,如果在列表中間添加或刪除元素,所用時(shí)間會(huì)大大增加。通過(guò)索引查找元素速度很快。適合場(chǎng)合:查詢(xún)比較多的場(chǎng)景

LinkedList底層是通過(guò)雙向鏈表實(shí)現(xiàn)的,LinkedList和ArrayList相比,增刪速度快,但查詢(xún)和修改值速度慢。在內(nèi)存中不是連續(xù)內(nèi)存。場(chǎng)景:增刪操作比較多的場(chǎng)景。

-二叉樹(shù)的深度優(yōu)先遍歷和廣度優(yōu)先遍歷的具體實(shí)現(xiàn) -堆的結(jié)構(gòu) -堆和樹(shù)的區(qū)別 -堆和棧在內(nèi)存中的區(qū)別是什么(解答提示:可以從數(shù)據(jù)結(jié)構(gòu)方面以及實(shí)際實(shí)現(xiàn)方面兩個(gè)方面去回答)? -什么是深拷貝和淺拷貝 -手寫(xiě)鏈表逆序代碼 -講一下對(duì)樹(shù),B+樹(shù)的理解 -講一下對(duì)圖的理解 -判斷單鏈表成環(huán)與否? -鏈表翻轉(zhuǎn)(即:翻轉(zhuǎn)一個(gè)單項(xiàng)鏈表) -合并多個(gè)單有序鏈表(假設(shè)都是遞增的)2020至2021年Android開(kāi)發(fā)面試習(xí)題整理,持續(xù)更新中_Android_02

Android 基礎(chǔ)與底層機(jī)制面試題

1.數(shù)據(jù)庫(kù)的操作類(lèi)型有哪些,如何導(dǎo)入外部數(shù)據(jù)庫(kù)?

2.是否使用過(guò)本地廣播,和全局廣播有什么差別?

3.是否使用過(guò) IntentService,作用是什么, AIDL 解決了什么問(wèn)題? (小米)

4.Activity、 Window、 View 三者的差別, fragment 的特點(diǎn)?(360)

5.描述一次網(wǎng)絡(luò)請(qǐng)求的流程(新浪)

6.Handler、 Thread 和 HandlerThread 的差別(小米)

7.低版本 SDK 實(shí)現(xiàn)高版本 api(小米)

8.launch mode 應(yīng)用場(chǎng)景(百度、小米、樂(lè)視)

9.touch 事件傳遞流程(小米)

10.view 繪制流程(百度)

11.什么情況導(dǎo)致內(nèi)存泄漏(美團(tuán))

12.ANR 定位和修正

13.什么情況導(dǎo)致 oom(樂(lè)視、美團(tuán))

14.Android Service 與 Activity 之間通信的幾種方式

15.Android 各個(gè)版本 API 的區(qū)別

16.如何保證一個(gè)后臺(tái)服務(wù)不被殺死,比較省電的方式是什么?(百度)

17.Requestlayout, onlayout, onDraw, DrawChild 區(qū)別與聯(lián)系(獵豹)

18.invalidate()和 postInvalidate() 的區(qū)別及使用(百度)

19.Android 動(dòng)畫(huà)框架實(shí)現(xiàn)原理(騰訊)

20.Android 為每個(gè)應(yīng)用程序分配的內(nèi)存大小是多少?(美團(tuán))

21.LinearLayout 對(duì)比 RelativeLayout(百度)

22.優(yōu)化自定義 view(百度、樂(lè)視、小米)

23.ContentProvider(樂(lè)視)

24.fragment 生命周期

25.volley 解析(美團(tuán)、樂(lè)視)

26.Android Glide 源碼解析

27.Android 屬性動(dòng)畫(huà)特性(樂(lè)視、小米)

2020至2021年Android開(kāi)發(fā)面試習(xí)題整理,持續(xù)更新中_數(shù)據(jù)結(jié)構(gòu)_03

最后

由于篇幅有限,僅展示部分內(nèi)容,所有的知識(shí)點(diǎn)整理的詳細(xì)內(nèi)容,有需要的朋友可以加入粉絲裙872206502進(jìn)行免費(fèi)獲取

2020至2021年Android開(kāi)發(fā)面試習(xí)題整理,持續(xù)更新中_數(shù)據(jù)結(jié)構(gòu)_04

對(duì)于Android開(kāi)發(fā)的朋友來(lái)說(shuō)應(yīng)該是非常完整的面試文檔筆記了,為了更好地整理每個(gè)模塊,我參考了很多網(wǎng)上的優(yōu)質(zhì)博文和項(xiàng)目,力求不漏掉每一個(gè)知識(shí)點(diǎn)。很多朋友靠著這些內(nèi)容進(jìn)行復(fù)習(xí),拿到了BATJ等大廠的offer,這個(gè)文檔筆記也已經(jīng)幫助了很多的安卓開(kāi)發(fā)者,希望也能幫助到你。

本文摘自 :https://blog.51cto.com/u

開(kāi)通會(huì)員,享受整站包年服務(wù)立即開(kāi)通 >