HashMap和Hashtable的區(qū)別:(HashMap是Hashtable的輕量級(jí)實(shí)現(xiàn))
1最主要的區(qū)別就是:hashtable是線程安全的九杂,而hashmap是非線程安全的(hashtable里面的方法都添加了synchronized關(guān)鍵字來(lái)確保線程同步投剥。(2)hashtable是不允許key和value為null的青灼,但是hashmap就可以。(3)Hashtable和HashMap擴(kuò)容的方法不一樣仗岸,HashTable中hash數(shù)組默認(rèn)大小11耿焊,擴(kuò)容方式是 old*2+1。HashMap中hash數(shù)組的默認(rèn)大小是16奕坟,而且一定是2的指數(shù)祥款,增加為原來(lái)的2倍,沒(méi)有加1月杉、(4)兩者通過(guò)hash值散列到hash表的算法不一樣刃跛,HashTbale是古老的除留余數(shù)法,直接使用hashcode苛萎,而后者是強(qiáng)制容量為2的冪桨昙,重新根據(jù)hashcode計(jì)算hash值,在使用hash? 位與? (hash表長(zhǎng)度 – 1)腌歉,也等價(jià)取膜蛙酪,但更加高效,取得的位置更加分散翘盖,偶數(shù)桂塞,奇數(shù)保證了都會(huì)分散到。前者就不能保證馍驯。
JDK8 對(duì)hashmap進(jìn)行了重新整理阁危,并且還能引出紅黑樹(shù)。
2 hashmap 的源碼就是數(shù)組+鏈表:數(shù)組中存的是
說(shuō)一下HashMap的流程:首先根據(jù)key.hashcode()算出hash值狂打,然后根據(jù)hash得出下角標(biāo)index,這樣就避免了一個(gè)一個(gè)去找吟吝。如果index相同的話菱父,就會(huì)在鏈表中查找。hash值和索引值的兩個(gè)方法是HashMap最為核心的部分,二者能保證哈希表的元素均勻的分布浙宜。
HashMap的擴(kuò)容機(jī)制:hashmap的構(gòu)造器里指明了兩個(gè)對(duì)于理解HashMap比較重要的兩個(gè)參數(shù) int initialCapacity, float loadFactor,這兩個(gè)參數(shù)會(huì)影響HashMap效率官辽。HashMap底層采用散列數(shù)組實(shí)現(xiàn),利用initialCapacity來(lái)設(shè)置數(shù)組的大小粟瞬,也就是散列桶的大小同仆。在不斷add后,這些桶可能被沾滿了裙品,利用每個(gè)桶附帶的鏈表增加元素俗批,但是有個(gè)缺點(diǎn),他會(huì)退化成LinkedList市怎,使get和put方法時(shí)間開(kāi)銷上升岁忘。但是HashMap 采用了增加桶的數(shù)量,這樣get和put 又回到近于常數(shù)的復(fù)雜度上区匠。關(guān)于擴(kuò)容reSize()干像,它會(huì)新建一個(gè)HashMap,然后將舊的hashmap中的元素添加到新的HashMap中〕叟可以看出麻汰,擴(kuò)容是一件相當(dāng)耗時(shí)的操作。需要重新計(jì)算這些元素在新的數(shù)組中的位置并進(jìn)行復(fù)制處理戚篙。
HashMap什么時(shí)候回增加容量呢五鲫?因?yàn)樾蕟?wèn)題,JDK采用了預(yù)加載方式岔擂,當(dāng)size > initialCapacity * loadFactor位喂,hashmap就會(huì)調(diào)用內(nèi)部的reSize方法。
如果key 為null智亮,就會(huì)直接在哈希表的第一個(gè)位置table[0]對(duì)應(yīng)的鏈表上面查找忆某。
如果put時(shí),hash值相同產(chǎn)生沖突時(shí)阔蛉,就會(huì)使用頭插法弃舒,將該元素插入到該下標(biāo)的鏈表第一個(gè)位置,后面的依次往后移状原。
HashMap和ConcurrentHashMap的區(qū)別:
ConcurrentHashMap:在hashMap的基礎(chǔ)上聋呢,ConcurrentHashMap將數(shù)據(jù)分為多個(gè)segment,默認(rèn)16個(gè)(concurrency level)颠区,然后每次操作對(duì)一個(gè)segment加鎖削锰,避免多線程鎖的幾率,提高并發(fā)效率毕莱。
TreeMap器贩、HashMap颅夺、LindedHashMap的區(qū)別:
TreeMap:實(shí)現(xiàn)了SortMap接口,能夠把他保存的記錄根據(jù)鍵值排序蛹稍,默認(rèn)是按鍵值的升序排序吧黄,也可以比較器,當(dāng)用iterator遍歷TreeMap時(shí)唆姐,得到的記錄是排過(guò)序的拗慨。注意,此實(shí)現(xiàn)不是同步的奉芦。需要外部同步赵抢。一般是通過(guò)對(duì)自然封裝該映射的對(duì)象執(zhí)行同步操作來(lái)完成的∩Γ可以通過(guò)這樣來(lái)進(jìn)行封裝:(下面這兩種集合也可以通過(guò)這種方式進(jìn)行封裝)
SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));
HashMap:鍵和值都可以為空烦却,不保證映射的順序,多次訪問(wèn)先巴,映射的順序可能不一樣短绸,非線程安全
LinkedHashMap:內(nèi)部維持了一個(gè)雙向鏈表,可以保持順序筹裕。保存了記錄的插入順序,在iterator遍歷LinkedHashMap時(shí)窄驹,先得到的肯定是先插入的朝卒。在遍歷的時(shí)候可能比HashMap慢,不過(guò)有一種情況乐埠,當(dāng)HashMap的容量特別大抗斤,但是實(shí)際數(shù)據(jù)比較小時(shí),遍歷起來(lái)可能比LinkedHashMap慢丈咐。因?yàn)長(zhǎng)inkedHashMap只和他的實(shí)際數(shù)據(jù)有關(guān)瑞眼,而HashMap和他的容量有關(guān)。
使用場(chǎng)景:一般情況下棵逊,我們用的最多的是HashMapHashMap里面存入的鍵值對(duì)在取出的時(shí)候是隨機(jī)的,它根據(jù)鍵的HashCode值存儲(chǔ)數(shù)據(jù),根據(jù)鍵可以直接獲取它的值伤疙,具有很快的訪問(wèn)速度。在Map 中插入辆影、刪除和定位元素徒像,HashMap 是最好的選擇。 ? ? ? ? ? ?TreeMap取出來(lái)的是排序后的鍵值對(duì)蛙讥。但如果您要按自然順序或自定義順序遍歷鍵锯蛀,那么TreeMap會(huì)更好。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?LinkedHashMap 是HashMap的一個(gè)子類次慢,如果需要輸出的順序和輸入的相同,那么用LinkedHashMap可以實(shí)現(xiàn),它還可以按讀取順序來(lái)排列旁涤,像連接池中可以應(yīng)用翔曲。
Collection包結(jié)構(gòu),與Collections的區(qū)別:
Collection是集合繼承結(jié)構(gòu)中的頂層接口
Collections 是提供了對(duì)集合進(jìn)行操作的強(qiáng)大方法的工具類 劈愚,它包含有各種有關(guān)集合操作的靜態(tài)多態(tài)方法瞳遍。此類不能實(shí)例化
try catch finally,try里有return造虎,finally還執(zhí)行么傅蹂?(總之 finally 永遠(yuǎn)執(zhí)行!)
Condition 1:如果try中沒(méi)有異常且try中有return(執(zhí)行順序)try----finally---return
Condition 2:如果try中有異常并且try中有return :try----catch---finally---return
Condition 3:try中有異常算凿,try-catch-finally里都沒(méi)有return 份蝴,finally 之后有個(gè)return:try----catch---finally? ? ? try中有異常以后,根據(jù)java的異常機(jī)制先執(zhí)行catch后執(zhí)行finally氓轰,此時(shí)錯(cuò)誤異常已經(jīng)拋出婚夫,程序因異常而終止,所以你的return是不會(huì)執(zhí)行的
Condition 4:當(dāng) try和finally中都有return時(shí)署鸡,finally中的return會(huì)覆蓋掉其它位置的return(多個(gè)return會(huì)報(bào)unreachable code案糙,編譯不會(huì)通過(guò))。
Condition 5:當(dāng)finally中不存在return靴庆,而catch中存在return时捌,但finally中要修改catch中return 的變量值時(shí)
int ret =0;
try{thrownewException();
}catch(Exceptione)
{
ret =1;returnret;
}finally{
ret =2;
}
最后返回值是1,因?yàn)閞eturn的值在執(zhí)行finally之前已經(jīng)確定下來(lái)了
Excption與Error包結(jié)構(gòu)炉抒。OOM你遇到過(guò)哪些情況奢讨,SOF你遇到過(guò)哪些情況:
SOF:StackOverflowError,(堆棧溢出)當(dāng)應(yīng)用程序遞歸太深而發(fā)生堆棧溢出,以為一般默認(rèn)為1-2m,一旦出現(xiàn)死循環(huán)或者大量的遞歸調(diào)用焰薄,在不斷的壓棧過(guò)程中拿诸,造成棧容量超過(guò)1m而導(dǎo)致溢出。原因有:遞歸調(diào)用塞茅,大量循環(huán)或者死循環(huán)亩码,全局變量是否過(guò)多,數(shù)組野瘦、list描沟、map數(shù)據(jù)過(guò)大
android的OOM(out of Memory)當(dāng)內(nèi)存占有量超過(guò)了虛擬機(jī)的分配最大值就會(huì)發(fā)生OOM(VM分配不出更多的page),一般出現(xiàn)這種情況:加載的圖片太多或者太大缅刽,分配特大的數(shù)組啊掏,內(nèi)存相應(yīng)資源過(guò)多來(lái)不及釋放。
java多態(tài)的實(shí)現(xiàn)原理
方法的重載和方法的復(fù)寫(xiě)都是多態(tài)的表現(xiàn)衰猛。
同一個(gè)類的不同表現(xiàn)形式迟蜜,不同的形態(tài)是通過(guò)不同的子類體現(xiàn),java通過(guò)將子類對(duì)象引用賦值給超類對(duì)象變量啡省,來(lái)實(shí)現(xiàn)動(dòng)態(tài)方法調(diào)用娜睛。
多態(tài)允許具體訪問(wèn)時(shí)實(shí)現(xiàn)方法的動(dòng)態(tài)綁定髓霞,java對(duì)于動(dòng)態(tài)綁定的實(shí)現(xiàn)主要依賴方法表。通過(guò)繼承和接口的多態(tài)實(shí)現(xiàn)有所不同畦戒。
繼承:在執(zhí)行某個(gè)方法時(shí)方库,在方法區(qū)找到該類的方法表,再確認(rèn)該方法在方法表中的偏移量障斋。找到該方法后如果被重寫(xiě)則直接調(diào)用纵潦,否則沒(méi)有重寫(xiě)父類該方法,這時(shí)會(huì)按照繼承關(guān)系搜索父類的方法表中該偏移量對(duì)應(yīng)的方法垃环。
接口:java允許一個(gè)類實(shí)現(xiàn)多個(gè)接口邀层,從某種意義上說(shuō)相當(dāng)于多繼承,這樣同一個(gè)的方法在不同類中方法表的位置就不一樣了遂庄。所以不能通過(guò)偏移量的方法寥院,而是通過(guò)搜索整個(gè)完整的方法表。
JVM結(jié)構(gòu):
并在方法區(qū)建立該類的類型信息(包括方法代碼秸谢、類變量、成員變量霹肝、以及方法表)估蹄。注意:這個(gè)方法區(qū)中的類型信息跟堆中存放的class對(duì)象是不同的。在方法區(qū)中沫换,這個(gè)class的類型信息只有唯一的實(shí)例(所以方法區(qū)是各個(gè)線程共享的內(nèi)存區(qū)域)元媚。而在堆中可以有多個(gè)該class對(duì)象,可以通過(guò)堆中的class對(duì)象方法到方法區(qū)中的類型信息苗沧。就像在java反射機(jī)制那樣,通過(guò)class對(duì)象可以訪問(wèn)到該類的所有信息一樣炭晒〈眩【重點(diǎn)】方法表是實(shí)現(xiàn)動(dòng)態(tài)調(diào)用的核心,為了優(yōu)化對(duì)象調(diào)用方法的速度网严,方法區(qū)的類型信息會(huì)增加一個(gè)指針识樱,該指針指向記錄該類方法的方法表,方法表中的每一項(xiàng)都是對(duì)應(yīng)方法的指針震束,這些方法中包括從父類繼承的所有方法以及自身重寫(xiě)的方法怜庸。
java的方法調(diào)用方式:動(dòng)態(tài)方法調(diào)用和靜態(tài)方法調(diào)用。靜態(tài)方法調(diào)用是指對(duì)于類的靜態(tài)方法的調(diào)用方式垢村,是靜態(tài)綁定的割疾。而動(dòng)態(tài)方法調(diào)用需要有方法調(diào)用所作用的對(duì)象、是動(dòng)態(tài)綁定的嘉栓。類調(diào)用時(shí)在編譯時(shí)就已經(jīng)確定調(diào)用方法的情況宏榕。實(shí)例調(diào)用時(shí)在調(diào)用的時(shí)候才確定的具體方法的調(diào)用方法拓诸。這就是動(dòng)態(tài)綁定,也是多態(tài)要解決的核心問(wèn)題麻昼。JVM的方法調(diào)用指令有四個(gè)奠支,分別是invokestatic,invokespecial抚芦,invokesvirtual和invokeinterface倍谜。前兩個(gè)是靜態(tài)綁定,后兩個(gè)是動(dòng)態(tài)綁定的叉抡。本文也可以說(shuō)是對(duì)于JVM后兩種調(diào)用實(shí)現(xiàn)的考察
如果子類改寫(xiě)了父類的方法尔崔,那么子類和父類的那些同名的方法共享一個(gè)方法表項(xiàng)。因此卜壕,方法表的偏移量總是固定的您旁。所有繼承父類的子類的方法表中,其父類所定義的方法的偏移量也總是一個(gè)定值轴捎。Person或Object中的任意一個(gè)方法鹤盒,在它們的方法表和其子類Girl和Boy的方法表中的位置 (index) 是一樣的。這樣JVM在調(diào)用實(shí)例方法其實(shí)只需要指定調(diào)用方法表中的第幾個(gè)方法即可侦副。
(1)在常量池(這里有個(gè)錯(cuò)誤侦锯,上圖為ClassReference常量池而非Party的常量池)中找到方法調(diào)用的符號(hào)引用。
(2)查看Person的方法表秦驯,得到speak方法在該方法表的偏移量(假設(shè)為15)尺碰,這樣就得到該方法的直接引用。
(3)根據(jù)this指針得到具體的對(duì)象(即?girl?所指向的位于堆中的對(duì)象)译隘。
(4)根據(jù)對(duì)象得到該對(duì)象對(duì)應(yīng)的方法表亲桥,根據(jù)偏移量15查看有無(wú)重寫(xiě)(override)該方法,如果重寫(xiě)固耘,則可以直接調(diào)用(Girl的方法表的speak項(xiàng)指向自身的方法而非父類)题篷;如果沒(méi)有重寫(xiě),則需要拿到按照繼承關(guān)系從下往上的基類(這里是Person類)的方法表厅目,同樣按照這個(gè)偏移量15查看有無(wú)該方法番枚。
可以看到损敷,由于接口的介入葫笼,繼承自接口 IDance 的方法 dance()在類 Dancer 和 Snake 的方法表中的位置已經(jīng)不一樣了,顯然我們無(wú)法僅根據(jù)偏移量來(lái)進(jìn)行方法的調(diào)用拗馒。
Java 對(duì)于接口方法的調(diào)用是采用搜索方法表的方式路星,如,要在Dancer的方法表中找到dance()方法诱桂,必須搜索Dancer的整個(gè)方法表奥额。
因?yàn)槊看谓涌谡{(diào)用都要搜索方法表苫幢,所以從效率上來(lái)說(shuō),接口方法的調(diào)用總是慢于類方法的調(diào)用的垫挨。