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

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

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

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

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

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

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

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

單例模式重點(diǎn)在于在整個(gè)系統(tǒng)上共享一些創(chuàng)建時(shí)較耗資源的對(duì)象。整個(gè)應(yīng)用中只維護(hù)一個(gè)特定類實(shí)例,它被所有組件共同使用。Java.lang.Runtime 是單例模式的經(jīng)典例子。從 Java 5 開始你可以使用枚舉(enum)來實(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í)的封裝層次。如果你使用工廠來創(chuàng)建對(duì)象,之后你可以使用更高級(jí)和更高性能的實(shí)現(xiàn)來替換原始的產(chǎn)品實(shí)現(xiàn)或類,這不需要在調(diào)用層做任何修改。

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

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

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

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

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

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

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

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

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

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

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

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

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

如果希望把一系列行為都規(guī)范在類繼承層次內(nèi),并且可以更好地在同一個(gè)地方進(jìn)行編碼,那么抽象類是一個(gè)更好的選擇。有時(shí),接口和抽象類可以一起使用,接口中定義函數(shù),而在抽象類中定義默認(rèn)的實(shí)現(xiàn)。2020至2021年Android開發(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ì)列、鏈表、樹、圖、堆、散列表。

1)數(shù)組:在內(nèi)存中連續(xù)存儲(chǔ)多個(gè)元素的結(jié)構(gòu)。數(shù)組元素通過下標(biāo)訪問,下標(biāo)從0開始。優(yōu)點(diǎn):訪問速度快;缺點(diǎn):數(shù)組大小固定后無法擴(kuò)容,只能存儲(chǔ)一種類型的數(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ù)元素的邏輯順序是通過鏈表的指針地址實(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)楹写罅恐羔樣?,固占用空間大,查找耗時(shí)。適用場(chǎng)景:數(shù)據(jù)量小,需頻繁增加刪除操作。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7)Comparable接口:用于比較

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

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

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

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)部類,每一個(gè)Entry包含一個(gè)key-value鍵值對(duì),鏈表是為解決哈希沖突而存在。

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

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

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

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

至于為什么要擴(kuò)容,如果不擴(kuò)容,HashMap中數(shù)組處的鏈表會(huì)越來越長(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,如果該位置沒值即table[index]為空,則直接將該鍵值對(duì)存放在table[index]處。

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

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

5)判斷HashMap中數(shù)據(jù)個(gè)數(shù)是否超過了(最大容量*裝載因子),如果超過了,還需要對(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類型,是則調(diào)用getTreeNode(hash, key)從二叉樹中查找結(jié)點(diǎn),不是TreeNode類型說明還是鏈表型,就遍歷鏈表找到相同的key值返回對(duì)應(yīng)的value值即可。

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

當(dāng)兩個(gè)對(duì)象的hashcode相同,它們的bucket位置相同,hashMap會(huì)用鏈表或是紅黑樹來存儲(chǔ)對(duì)象。Entry類里有一個(gè)next屬性,作用是指向下一個(gè)Entry。第一個(gè)鍵值對(duì)A進(jìn)來,通過計(jì)算其key的hash得到index,記做Entry[index]=A。一會(huì)又進(jìn)來一個(gè)鍵值對(duì)B,通過計(jì)算其key的hash也是index,HashMap會(huì)將B.next=A, Entry[index]=B.如果又進(jìn)來C,其key的hash也是index,會(huì)將C.next=B, Entry[index]=C.這樣bucket為index的地方存放了ABC三個(gè)鍵值對(duì),它們能過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填滿了75%的bucket時(shí),和其他集合類一樣,將會(huì)創(chuàng)建原來HashMap大小兩倍的bucket數(shù)組,來重新調(diào)整HashMap的大小,并將原來的對(duì)象放入新的bucket數(shù)組中。

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

而在jdk1.8中,會(huì)將原來鏈表結(jié)構(gòu)保存至節(jié)點(diǎn)e中,將原來數(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用來充當(dāng)鎖角色,每一個(gè)segment包含了對(duì)自己的HashEntry的操作,如getput eplace操作,這些操作發(fā)生時(shí),對(duì)自己的HashEntry進(jìn)行鎖定。由于每一個(gè)segment寫操作只鎖定自己的HashEntry,可以存在多個(gè)線程同時(shí)寫的情況。

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ù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)的,并發(fā)制定用synchronized和CAS操作。

2)Segment實(shí)現(xiàn)了ReentrantLock重入鎖,當(dāng)執(zhí)行put操作,會(huì)進(jìn)行第一次key的hash來定位Segment的位置,若該Segment還沒有初始化,會(huì)通過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ù)組來維護(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,無需為每個(gè)鍵值對(duì)創(chuàng)建Node對(duì)象,且在數(shù)組中連續(xù)存放,更省空間。

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

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

4)ArrayMap采用二分法查找,mHashes中的hash值是按照從小到大的順序連續(xù)存放的,通過二分查找來獲取對(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專門對(duì)基本類型做了優(yōu)化,Key只能是可排序的基本類型,如intlong,對(duì)value,除了泛型Value,還對(duì)每種基本類型有單獨(dú)實(shí)現(xiàn),如SparseBooleanArraySparseLongArray等。無需包裝,直接使用基本類型值,無需hash,直接使用基本類型值索引和判斷相等,無碰撞,無需調(diào)用hashCode方法,無需equals比較。SparseArray延遲刪除。

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

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

(1) put(Key, Value)方法

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

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

3)如果沒有相同的key值,調(diào)用addEntry方法,在addEntry中判斷count大小是否超過了最大容量限制,如果超過了需要重新rehash(),容量變成原來容量*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è)線程安全的類,在對(duì)外方法都添加了synchronized方法,序列化方法上也添加了synchronized同步鎖方法,而HashMap非線程安全。這也導(dǎo)致Hashtable的讀寫等操作比HashMap慢。

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

3)Hashtable根據(jù)key值的hashCode計(jì)算索引,(hash&0x7FFFFFFF)%tab.length,保證hash值始終為正數(shù)且不超過表的長(zhǎng)度。而HashMap中計(jì)算索引值是通過hash(key)&(tab.length-1),是通過與操作,計(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沒有單獨(dú)的get方法,用的是HashMap的。HashSet實(shí)現(xiàn)了Set接口,不允許集合中出現(xiàn)重復(fù)元素,將對(duì)象存儲(chǔ)進(jìn)HashSet前,要先確保對(duì)象重寫了hashCode()和equals方法,以保證放入set對(duì)象是唯一的。

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

HashMap在放入key-value鍵值對(duì)是,先通過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ì)大大增加。通過索引查找元素速度很快。適合場(chǎng)合:查詢比較多的場(chǎng)景

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

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

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

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

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

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

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)景(百度、小米、樂視)

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

10.view 繪制流程(百度)

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

12.ANR 定位和修正

13.什么情況導(dǎo)致 oom(樂視、美團(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)畫框架實(shí)現(xiàn)原理(騰訊)

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

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

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

23.ContentProvider(樂視)

24.fragment 生命周期

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

26.Android Glide 源碼解析

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

2020至2021年Android開發(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開發(fā)面試習(xí)題整理,持續(xù)更新中_數(shù)據(jù)結(jié)構(gòu)_04

對(duì)于Android開發(fā)的朋友來說應(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)幫助了很多的安卓開發(fā)者,希望也能幫助到你。

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

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