字串
String s1 = "aaa";
String s2 = "aa" + new String("a");
String s3 = new String("aaa");
System.out.println(s1.intern().equals(s1)); //true
System.out.println(s1.equals(s2)); //true
System.out.println(s3.equals(s1)); //true
System.out.println(s1.intern() == s1); //true
System.out.println(s1 == s2); //false
System.out.println(s3 == s1); //false
如何實作字串的反轉及替換?(答案有很多,遞回解法)
public static String reverse(String originStr) {
if(originStr == null || originStr.length() <= 1)
return originStr;
return reverse(originStr.substring(1)) + originStr.charAt(0);
}
反射
獲取類物件的方式
- 類名.class 如String.class
- 物件.class 如“a”.getClass()
- Class.forname(“類的全路徑”),如 Class.forname(“java.lang.String”)
使用類物件創建實體物件
- 類物件.newInstance()
- 類物件.getDeclaredConstructor(傳入引數型別的類物件)獲取指定的構造器物件Constructor,然后再用構造器物件.newInstance(傳入引數)
將獲取到的私有欄位或者私有方法變得可訪問
setAccessible(true)
呼叫物件方法
getMethod(方法名),呼叫method.invoke(obj, arg)
集合
ArrayList、Vector、LinkList
ArrayList底層實作是陣列,執行緒不安全,擴容大小是n+(n>>1),1.5倍,讀取某個索引元素快,初始容量為10
Vector底層實作也是陣列,執行緒安全,擴容大小是2n,讀取某個索引元素快,初始容量為10
LinkList底層實作是雙向鏈表,執行緒不安全,添加洗掉快,讀取某個索引元素慢
HashMap和Hashtable
HashMap底層實作是陣列+鏈表+紅黑樹(雙向鏈表),陣列初始容量為16,擴容為2n,鏈表元素大于等于8個(陣列長度小于64則使用擴容來防止鏈表過長,否則使用紅黑樹)時變成紅黑樹,執行緒不安全,key和value可為空
Hashtable執行緒安全,但是效率極低,如果需要使用執行緒安全類建議使用ConcurrentHashMap代替
千萬不要用可變型別做HashMap和HashSet鍵值
@Test
public void test2(){
HashSet<StringBuilder> stringBuilders = new HashSet<StringBuilder>();
StringBuilder aaa = new StringBuilder("aaa");
StringBuilder bbb = new StringBuilder("aaabbb");
stringBuilders.add(aaa);
stringBuilders.add(bbb);
System.out.println(stringBuilders); //[aaabbb, aaa]
StringBuilder s3 = aaa;
s3.append("bbb");
System.out.println(stringBuilders); //[aaabbb, aaabbb] 破壞了hashset的值唯一性
}
HashMap底層原理詳述
底層資料結構
1.7底層是陣列+鏈表實作
1.8底層是陣列+鏈表+紅黑樹(+雙向鏈表)實作
如何解決hash碰撞
解決hash碰撞的方法:重新hash,開放地址法(即在hash地址往上或者往下一個存放),公共溢位區(開辟一個新區域專門存放碰撞的值),鏈地址法
HashMap使用先擾動函式,再鏈地址法解決碰撞問題,hash(key)&(n-1)=hash(key)%n(在n是2的冪次方時成立)
為什么陣列長度必須是2的冪次方?
- 取模運算比位運算慢,2的冪次方可以將取模運算轉換成位運算
- 使用位運算公式hash(key)&(n-1)計算值存放位置的時候,如果不是2的冪次方則會使得部分位置永遠不會存放資料,浪費空間
- 擴容之后仍是2的冪次方,則n-1之后的二進制數只是在原本長度的二進制數位上高位進位1,即如果原本n-1的二進制數為1111,則擴容之后的二進制數為11111,則與hash(key)進行與運算之后要么還在陣列原本的位置(hash(key)進位數為0),要么在原本位置加上擴容前陣列長度的位置(hash(key)進位數為1),即用hash(key)&n(n為舊的陣列長度)判斷是等于0則放在新陣列原位置,大于0則放在新陣列原位置+n的位置
擴容機制
1.7超過擴容閾值(0.75*n)則先擴容再插入,頭插法
從鏈表頭開始,先將當前節點指向新陣列位置,然后移動到新陣列位置,依次回圈遍歷舊陣列的鏈表完成向新陣列的轉移
1.8先插入,尾插法,再判斷是否超過閾值進行擴容
紅黑樹加入雙向鏈表結構是為了擴容方便,無論是雙向鏈表還是單向鏈表,擴容的時候都是從鏈表頭開始,先將當前的鏈表分為原陣列位置和原陣列位置+n兩條鏈表,如果是單向鏈表則在分配好之后一次性放入這兩個位置,如果是紅黑樹+雙向鏈表則分配好之后再重新生成紅黑樹或者變成單向鏈表(鏈表長度<=6)
多執行緒下擴容問題
1.7擴容時的頭插法會導致回圈參考
1.8擴容時則可能會導致資料丟失
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/469800.html
標籤:其他
上一篇:Java位運算
