一、說說&和&&的區別?
作為運算子:&將二進位制的每一位進行與運算
作為邏輯運算子:兩者都是與,&&如果左邊為假則終止右邊運算,即短路運算。&則需要把兩邊的比較執行完。
二、int和Integer的區別
int是Java的基本資料型別,而Integer是int的包裝類
int直接儲存整數值,而Integer是一個物件,包含了一些額外的方法和功能
int的預設值是0,而Integer的預設值是null
擴充套件:那麼我們在判斷這兩個型別是否相等的時候有什麼要注意的呢?
(1)int和int型別的比較可以直接用==判斷
(2)int和Integer型別的比較時也可以用==、Integer會自動拆箱為int
(3)Integer和Integer型別的比較中。如果數值範圍在[-128,127]之間可以用==。其他範圍只能用equals方法、原因是:JVM會
維護這個範圍內的快取,比如第一個Integer是127,會存放在快取中;在建立第二個Integer時會直接返回快取的127,所以兩者是相等的。
三、介面和抽象類的區別
1.語法層面
(1)抽象類可以提供成員方法的實現細節,而介面中只能存在public abstract(隱式宣告)方法(JDK8預設方法);
(2)抽象類中成員變數可以是各種型別的,而介面中的成員變數只能是public static final(隱式宣告)型別的(必須在宣告時賦值);
(3)抽象類可以有靜態程式碼塊和靜態方法,介面中不能含有靜態程式碼塊以及靜態方法
(4)一個類只能繼承一個抽象類,而一個類卻可以實現多個介面
2. 設計層面
(1)抽象類:是對整個類進行抽象,包括屬性,行為(方法),那麼一定是抽象類的種類(擁有同一種屬性或行為的類)
(2)介面:是對行為的抽象
(3)抽象類是一種模板設計,介面是一種規範。
3.怎麼選擇
(1)如果擁有一些方法,並想讓他們中的一些有預設的具體實現,請選擇抽象類
(2)如果想實現多重繼承,那麼請使用介面,由於java不支援多繼承,子類不能繼承多個類,但一個類可以實現多個介面,因此可以使用介面來解決
(3)如果基本功能在不斷變化,那麼就使用抽象類,如果使用介面,那麼每次變更都需要相應的去改變實現該介面的所有類
4. JDK8中為什麼會在介面中提供預設方法?
目的:用來減少抽象類和介面的差異,可以在介面中提供預設的實現方法並實現該介面的類不用強制去實現這個方法。JDK8中介面的靜態方法只能透過介面名直接去呼叫,介面中的預設方法因為不是abstract的,所以可重寫,也可以不重寫。
四、談談你對樹的理解(資料結構)?
1.樹的由來
陣列:檢索效率高
連結串列:新增刪除效率高
樹:綜合檢索和操作的效率
2.樹的分類
根據不同的分類方式,樹可以分為不同的型別
(1)根據樹分支的數量限制: 可以分為二叉樹和多叉樹。二叉樹最多隻有兩個子節點,而多叉樹一個節點可以有多於兩個的子節點
(2)根據樹節點的有序性:可以分為查詢樹和無序樹。查詢樹的基本特徵為任意一個節點所包含的鍵值,大於等於左孩子的鍵值,小於等於右孩子的鍵值。無序樹則沒有特定的鍵值大小關係。
(3)根據具體用途和特徵:例如紅黑樹、AVL樹、平衡二叉樹、平衡二叉搜尋樹。紅黑樹是一種自平衡二叉查詢樹,AVL樹也是一種自平衡二叉查詢樹,它要求任何節點的兩個子樹的高度差最大為1。平衡二叉樹和平衡二叉搜尋樹則是為了平衡樹的左右子樹的高度差。
(4)根據樹的完整性和是否包含空值:可以分為完全二叉樹、滿二叉樹、完全二叉搜尋樹、滿二叉搜尋樹等。完全二叉樹和滿二叉樹是包含所有節點的二叉樹,而完全二叉搜尋樹和滿二叉搜尋樹則是所有節點都按照一定順序排列得二叉搜尋樹。
3.常見樹的特點
(1)普通二叉查詢樹:傾斜的問題
(2)AVL自平衡:左旋右旋開銷問題
(3)紅黑樹:黑節點平衡、深度問題、二叉。作為記憶體中資料儲存
(4)B樹和B+樹:多路查詢、深度比較少、一般用於大規模資料儲存、索引
4.TreeMap和HashMap的區別
TreeMap: 本質就是紅黑樹的實現
HashMap:以jdk8為例。就是透過 陣列+連結串列+紅黑樹+演算法實現的
透過上面的實現也可以看到HashMap綜合的讀寫效率要比TreeMap高了
五、HashMap面試彙總
1.jdk8為什麼引入了紅黑樹
因為連結串列長度增加後檢索的效率急劇降低,複雜度是O(n)
2.解決hash衝突,為什麼不直接用紅黑樹?
因為紅黑樹需要進行左旋,右旋,變色這些操作來保持平衡,而單連結串列不需要。單元素小於8個的時候,此時做查詢操作,連結串列結構已經能保證查詢效能。當元素大於8個的時候,紅黑樹搜尋時間複雜度是O(logn),而連結串列是O(n),此時需要紅黑樹來加快查詢速度,但是新增節點的效率變慢了。
因此,如果一開始就用紅黑樹結構,元素太少,新增效率又比較慢,無疑這是浪費效能的。
3.為什麼連結串列改為紅黑樹的閾值是8?
首先和hashcode碰撞次數的泊松分佈有關,主要是為了尋找一種時間和空間的平衡。在負載因子0.75(HashMap預設)的情況下,單個hash槽內元素個數為8的機率小於百萬分之一,將7作為一個分水嶺,等於7時不做轉換,大於等於8才轉紅黑樹,小於等於6才轉連結串列。連結串列中元素個數為8時機率已經非常小,再多的就更少了,所以原作者在選擇連結串列元素個數時選擇了8,是根據機率統計而選擇的。
4.預設載入因子為什麼是0.75
這個是從時間和空間的角度綜合得出的
(1)如果是1.0 當陣列的值全部填充了才會發生擴容,此時Hash衝突時避免不了的。連結串列的操作或者紅黑樹的操作會犧牲時間來保證空間的利用率
(2)如果是0.5 當陣列中一半的資料利用了之後就會開始擴容。這時填充的資料少。hash衝突也會減少,底層連結串列和紅黑樹的高度也會降低。查詢效率增加。但是這時還有太多的空間沒有利用。空間資源浪費了。
(3)所以0.75是綜合考慮得出的
5.為什麼要右移16位?
static final int hash (Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
其實是為了減少碰撞,進一步降低hash衝突的機率。int型別的數值是4個位元組的,右移16位異或可以同時保留高16位於低16位的特徵。
當陣列的長度很短時,只有低位數的hashcode值能參與運算。而讓高16位參與運算可以更好的均勻雜湊,減少碰撞,進一步降低hash衝突的機率。並且使得高16位和低16位的資訊都被保留了。
在HashMap的put方法裡面,是透過key的hash值與陣列的長度取模計算得到陣列的位置。
而在絕大部分的情況下,n的值一般都會小於2^16次方,也就是65536。
所以也就意味著i的值,始終是使用hash值的低16位與(n-1)進行取模運算,這個是由與運算子&的特性決定的。
這樣就會造成key的雜湊度不高,導致大量的key集中儲存在固定的幾個陣列位置,很顯然會影響到資料查詢效能。
6. 為什麼Hash值要與length-1相與
把hash值對陣列長度取模運算,模運算的消耗很大,沒有位運算快。
當length總是2的n次方時,h&(length-1)運算等價於length取模,也就是h%length,但是&比%具有更高的效率。
7.介紹下put方法的流程
(1)首先根據key的值計算hash值,找到該元素在陣列中儲存的下標;
(2)如果陣列是空的,則呼叫resize進行初始化;
(3)如果沒有雜湊衝突直接放在對應的陣列下標裡;
(4)如果衝突了,且key已經存在,就覆蓋掉value;
(5)如果衝突後,發現該節點是紅黑樹,就將這個節點掛在樹上
(6)如果衝突後是連結串列,判斷該連結串列是否大於8,如果大於8並且陣列容量小於64,就進行擴容;如果連結串列節點大於8並且陣列的容量大於64,則將這個結構轉換為紅黑樹;否則,連結串列插入鍵值對,若key存在,就覆蓋掉value。