力爭清晰完整準確(逐步完善,持續更新) 1、String類為什麼是final的 首先分析String的源碼: 類被final關鍵字限定,說明它不可以被繼承,沒有子類。即持有一個String對象的引用,它必然是String類,而不會是其他的類。 value[]是用來存儲值的,被final關鍵字修飾,說 ...
力爭清晰完整準確
1、String類為什麼是final的
首先分析String的源碼:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
- 類被final關鍵字限定,說明它不可以被繼承,沒有子類。即持有一個String對象的引用,它必然是String類,而不會是其他的類。
- value[]是用來存儲值的,被final關鍵字修飾,說明這個數組不可被其它數組替換—即數組的地址不可變更,但是數組的每個元素的值可以變更
private 限定符,保證String字元串數組的值不可在類外被修改。由於未對外暴露可修改的介面,所以String的值一旦被創建,即不可被修改。
- 線程安全
因為字元串是不可修改的(只能讀不能寫),多個線程可以共用同一個字元串實例
- 字元串常量池可以大大提高時空間效率
字元串常量池,詳情請移步 https://segmentfault.com/a/1190000009888357
2、JDK8的HashMap的源碼,實現原理,底層結構
HashMap的Hash衝突解決,後面單獨會寫一篇博客。
ConcurrentHashMap的鎖分段,大廠很喜歡問(最近華為電話面試問過我),簡單說一句,就是hashMap的數據是一個數組,用多個鎖來鎖,一個鎖鎖一個節點的數據鏈。
不像以前一個鎖鎖住整個數組,多個線程可分段訪問這些數據,自然效率就高了
- 首先看Node的源碼
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
HashMap用 transient Node<K,V>[] table 存值,本質上是鏈表數組(哈希數組+鏈表+紅黑樹),是Hash散列的,即數組非緊密排列,有空隙,詳見下圖
圖01
為什麼有紅黑樹,看put(新增)的源碼片段,如下:
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
TreeNode定義的源碼片段,如下:
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}
- 容量及動態擴容
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
預設容量-16。resize時,newCap = oldCap << 1( 2進位,左移1位,即*2,舊的容量翻倍,容量可能不是2的冪,視就又容量情況而定)
- 新增元素
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
1)如果以前這個key有值,put 操作會用新值替換舊值。
2)Hash衝突怎麼解決
hash(散列),就是key和存儲位置有個映射關係f,我們稱之為hash函數。hash衝突,就是不同的key,根據hash函數算出來的存儲位置相同,後面添加的元素就和原來的hashCode衝突了,所以要重新按照一定規則計算新的存儲位置。普通HashMap(java 8的HashMap結構如“圖1”,有紅黑樹)結構如下圖:
java8 中的HashMap為了提高查找效率,當鏈表衝突過高,大於閾值時,會將鏈表節點轉化成紅黑樹節點
- 裝載因數
static final float DEFAULT_LOAD_FACTOR = 0.75f;
load factor預設0.75 ,這個和概率統計有關(Hash衝突概率最低),詳見 http://en.wikipedia.org/wiki/Poisson_distribution
為了減少衝突,當hashMap的數組長度 > 臨界值 就會觸發擴容,所有元素rehash(重新計算hashCode和存儲位置)再放到擴容後的容器中,因為涉及到計算、數據查找、記憶體拷貝、移動等操作,非常耗時。
臨界值 = current capacity * current load factor。預設臨界值 = DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR = 16 x 0.75 = 12時,就會觸發擴容操作。
*****************************************************************************************************
精力有限,欲望太多,專註做好一件事就行
- 我只是一個程式猿。5年內把代碼寫好,技術博客字字推敲,堅持零拷貝和原創
- 寫博客的意義在於鍛煉邏輯條理性,加深對知識的系統性理解,鍛煉文筆,如果恰好又對別人有點幫助,那真是一件令人開心的事
*****************************************************************************************************