淺談JAVA中HashMap、ArrayList、StringBuilder等的擴容機制

JAVA中的部分須要擴容的內容總結以下:
第一部分:java

HashMap<String, String> hmap=new HashMap<>();
HashSet<String> hset=new HashSet<>();
Hashtable<String, String> htable=new Hashtable<>();
第二部分:

CopyOnWriteArrayList<String> coarray=new CopyOnWriteArrayList<>();
ArrayList<String> array=new ArrayList<>();
Vector<String> vec=new Vector<>();
第三部分:

StringBuffer sb=new StringBuffer();
StringBuilder sbu=new StringBuilder();
先從如下幾個源碼方面分析:(JDK1.8)數組

一、初始容量。
二、擴容機制。
三、同類型之間對比。安全

1.1 HashMap:
1、初始容量定義:默認爲1 << 4(16)。最大容量爲1<< 30。
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
2、擴容加載因子爲(0.75),第一個臨界點在當HashMap中元素的數量等於table數組長度*加載因子(16*0.75=12),
若是超出則按oldThr << 1(原長度*2)擴容。
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold多線程

1.2 HashSet併發

1、初始容量定義:16。由於構造一個HashSet,其實至關於新建一個HashMap,而後取HashMap的Key。
擴容機制和HashMap同樣。
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}app

1.3 Hashtable<String, String> htable=new Hashtable<>();
public class Hashtable<K,V>
extends Dictionary<K,V>
1、初始容量定義:capacity (11)。
/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
this(11, 0.75f);
}
2、擴容加載因子(0.75),當超出默認長度(int)(11*0.75)=8時,擴容爲old*2+1。
int newCapacity = (oldCapacity << 1) + 1;

小結:HashTable和HashMap區別性能

第一,繼承不一樣。
public class Hashtable extends Dictionary implements Map
public class HashMap extends AbstractMap implements Map
第二:
Hashtable 中的方法是同步的,而HashMap中的方法在缺省狀況下是非同步的。在多線程併發的環境下,能夠直接使用
Hashtable,可是要使用HashMap的話就要本身增長同步處理了。
第三,Hashtable中,key和value都不容許出現null值。ui

在HashMap中,null能夠做爲鍵,這樣的鍵只有一個;能夠有一個或多個鍵所對應的值爲null。當get()方法返回null值時,
便可以表示 HashMap中沒有該鍵,也能夠表示該鍵所對應的值爲null。所以,在HashMap中不能由get()方法來判斷HashMap中
是否存在某個鍵, 而應該用containsKey()方法來判斷。this

第四,兩個遍歷方式的內部實現上不一樣。spa

Hashtable、HashMap都使用了 Iterator。而因爲歷史緣由,Hashtable還使用了Enumeration的方式 。

第五,哈希值的使用不一樣,HashTable直接使用對象的hashCode。而HashMap從新計算hash值。

第六,
Hashtable和HashMap它們兩個內部實現方式的數組的初始大小和擴容的方式。HashTable中hash數組默認大小是11,增長的方
式是 old*2+1。HashMap中hash數組的默認大小是16, 增長的方式是 old*2。


2.1 CopyOnWriteArrayList:
/**
* Creates an empty list.
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
CopyOnWriteArrayList在作修改操做時,每次都是從新建立一個新的數組,在新數組上操做,最終再將新數組替換掉原數組
。所以,在作修改操做時,仍能夠作讀取操做,讀取直接操做的原數組。讀和寫操做的對象都不一樣,所以讀操做和寫操做互
不干擾。只有寫與寫之間須要進行同步等待。另外,原數組被聲明爲volatile,這就保證了,一旦數組發生變化,則結果對
其它線程(讀線程和其它寫線程)是可見的。

CopyOnWriteArrayList並不像ArrayList同樣指定默認的初始容量。它也沒有自動擴容的機制,而是添加幾個元素,長度就相
應的增加多少。

CopyOnWriteArrayList適用於讀多寫少,既然是寫的狀況少,則不須要頻繁擴容。而且修改操做每次在生成新的數組時就指
定了新的容量,也就至關於擴容了,因此不須要額外的機制來實現擴容。
2.2 ArrayList<String> array=new ArrayList<>();
1、初始容量定義:10。
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;

2、擴容:oldCapacity + (oldCapacity >> 1),即原集合長度的1.5倍。
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
2.3 Vector<String> vec=new Vector<>();
1、初始容量定義:10。
public Vector() {
this(10);
}
2、擴容:當擴容因子大於0時,新數組長度爲原數組長度+擴容因子,不然新數組長度爲原數組長度的2倍。
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
小結:
1,ArrayList與Vector初始容量都爲10。
2,擴容機制不一樣,當超出當前長度時ArrayList擴展爲原來的1.5倍,而若不考慮擴容因子Vector擴展爲原來的2倍。

3,ArrayList爲非線程安全的,處理效率上較Vector快,若同時考慮線程安全和效率,可使用 CopyOnWriteArrayList。

3.1 StringBuffer sb=new StringBuffer();
1、初始容量定義:16。
public StringBuffer() {
super(16);
}
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
2、擴容:由於StringBuffer extends AbstractStringBuilder,因此其實是用的是AbstractStringBuilder
的擴容方法,當用append(str),添加字符串時,假設字符串中已有字符長度爲count的字符串,初始長度value=16,若要添加的
字符串長度(count+str.length())<=(value*2+2)則按value*2+2長度擴容,而且
value=value*2+2,若(count+str.length())>(value*2+2),則按count+str.length()長度擴容,而且
value=count+str.length()。下次超出時再按以上方法與value*2+2比較擴容。


private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;

3.2 StringBuilder sbu=new StringBuilder();
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
public StringBuilder() {
super(16);
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
小結:
1.StringBuilder是jdk1.5引進的,而StringBuffer在1.0就有了;
2.StringBuilder和StringBuffer都是可變的字符串。可以經過append或者insert等方法改動串的內容;
3.StringBuffer是線程安全的而StringBuilder不是,於是在多線程的環境下優先使用StringBuffer,而其它狀況下推薦使用
StringBuilder,因爲它更快。
4.StringBuilder和StringBuffer都繼承自AbstractStringBuilder類,AbStractStringBuilder主要實現了擴容、append、
insert方法。StrngBuilder和StringBuffer的相關方法都直接調用的父類。
5.StringBuilder和StringBuffer的初始容量都是16,程序猿儘可能手動設置初始值。以免屢次擴容所帶來的性能問題;
6.StringBuilder和StringBuffer的擴容機制是這種:首先試着將當前數組容量擴充爲原數組容量的2倍加上2,假設這個新容
量仍然小於預約的最小值(minimumCapacity),那麼就將新容量定爲(minimumCapacity),最後推斷是否溢出,若溢出,
則將容量定爲整型的最大值0x7fffffff。

相關文章
相關標籤/搜索