HikariCP為什么自己造了一個FastList?
HikiriCP作為當(dāng)今世界上最快的數(shù)據(jù)庫連接池中間件,其對代碼追求的極致一直被開源愛好者津津樂道。HikariCP之所以這么快的其中一個原因就是:開發(fā)FastList取代ArrayList。那么FastList是個什么東西?相比ArrayList有哪些出色的地方?接下來讓我們通過對源碼的分析來一探究竟。
首先,讓我們打開HikariCP的源代碼,如果你不想下載它的源碼的話,你也可以只打開 https://github.com/brettwooldridge/HikariCP/blob/master/src/main/java/com/zaxxer/hikari/util/FastList.java 即可,就能看到FastList的源碼。
我們首先看一下這個類的注釋,如下所示。通過這段注釋我們可以得出幾個結(jié)論,1. FastList并不是完全憑空造出來的而是基于ArrayList造出來的,正所謂站在巨人的肩膀上。2. 由這個類的注釋我們可知,它沒有range checking,即范圍檢查。如果看過ArrayList的源碼的話,我們就知道,它的get()、set()、remove()等很多方法中都執(zhí)行了范圍檢查(rangeCheck(index)):
/**
?*?Fast?list?without?range?checking.
?*
?*?@author?Brett?Wooldridge
?*/
public?final?class?FastList?extends?ArrayList
{
???...?...
}
我們現(xiàn)在大概知道了FastList相比ArrayList的改進(jìn),下面列舉出兩個類具體對比如下表格所示:
| 操作 | FastList | ArrayList |
|---|---|---|
| add | 重寫 | 支持 |
| get | 重寫 | 支持 |
| removeLast | 支持 | 不支持 |
| remove(Object) | 重寫 | 支持 |
| clear | 重寫 | 支持 |
| size | 一樣 | 一樣 |
| isEmpty | 一樣 | 一樣 |
| set | 重寫 | 支持 |
| remove(int) | 重寫 | 支持 |
| iterator | 重寫 | 支持 |
| toArray、containsAll、contains、addAll、removeAll... ... | throw new UnsupportedOperationException() | 支持 |
精簡部分
如上表格可知,F(xiàn)astList相比ArrayList,它屏蔽了很多它不需要的操作方法,例如:toArray、containsAll、contains、addAll、removeAll... ... 。只要調(diào)用這些方法,就會拋出UnsupportedOperationException異常。這是因為,F(xiàn)astList是HikariCP用來管理Statement(例如PrepareStatement)的,它并不需要這些方法。
add
add方法是一個非常有用、使用頻率很高的方法。而且剛才我們說了,HikariCP是用FastList用來管理Statement的,這就意味著,在我們使用拿出數(shù)據(jù)庫連接執(zhí)行SQL時,都需要創(chuàng)建Statement,這就需要調(diào)用FastList的add()方法,所以這個方法是一個非常高頻的方法。FastList的add()源碼如下。它和ArrayList最大的區(qū)別是,F(xiàn)astList假設(shè)大部分情況下不需要擴容,直接給數(shù)據(jù)最后一個元素賦值即可。而ArrayList恰恰相反,需要先確保容量足夠:
public?boolean?add(T?element)
{
???try?{
??????elementData[size++]?=?element;
???}
???catch?(ArrayIndexOutOfBoundsException?e)?{
??????//?overflow-conscious?code
??????final?int?oldCapacity?=?elementData.length;
??????final?int?newCapacity?=?oldCapacity?<1;
??????@SuppressWarnings("unchecked")
??????final?T[]?newElementData?=?(T[])?Array.newInstance(clazz,?newCapacity);
??????System.arraycopy(elementData,?0,?newElementData,?0,?oldCapacity);
??????newElementData[size?-?1]?=?element;
??????elementData?=?newElementData;?????
???}
???return?true;
}
FastList的add方法與ArrayList相比還有一點不同,F(xiàn)astList需要擴容時,通過左移一位來實現(xiàn)成倍擴容(final int newCapacity = oldCapacity << 1),而ArrayList每次都是1.5倍擴容(int newCapacity = oldCapacity + (oldCapacity >> 1))。很明顯FastList的擴容少了一次+操作,性能更高。
get&set&remove
get也是使用頻率極高的一個方法,那么FastList做了哪些優(yōu)化了,請看源碼。優(yōu)化的地方非常清楚了,F(xiàn)astList假定絕大部分調(diào)用get方法的index是合法的,從而不進(jìn)行范圍檢查。而ArrayList則需要先進(jìn)行范圍檢查,再獲取具體位置的值。我們知道:無論什么語言編寫的多么高性能的代碼,只要有代碼執(zhí)行,就會有性能損耗。這也是FastList干掉范圍檢查的原因,能快一點是一點:
/**
?*?@return?the?element,?or?ArrayIndexOutOfBounds?is?thrown?if?the?index?is?invalid
?*/
@Override
public?T?get(int?index)
{
???//?ArrayList需要先調(diào)用rangeCheck(index)進(jìn)行范圍檢查
???return?elementData[index];
}
set方法以及remove(int)方法和get方法進(jìn)行了完全一樣的優(yōu)化,去掉了范圍檢查,它們都假定操作的index是完全合法的,只要每次操作的index完全有效,那么范圍檢查就是多余的,就可以通過干掉范圍檢查來提高性能。
removeLast
這是FastList新增的一個方法,ArrayList是沒有這個方法的。為什么HikariCP新增了一個這樣的方法呢?我們知道,在獲取數(shù)據(jù)庫連接執(zhí)行SQL時,如果創(chuàng)建了多個Statement,那么后創(chuàng)建的Statement需要先關(guān)閉。以3個Statement為例,我們依次創(chuàng)建了stmt1,stmt2,stmt3。那么良好的編程習(xí)慣是先執(zhí)行stmt3.close(),再stmt2.close(),最后stmt1.close()。而HikiriCP是用FastList管理Statement這個過程中實際調(diào)用了3次add()方法,然后依次調(diào)用3次removeLast方法,這就是為什么HikariCP需要造一個removeList方法的原因:
public?T?removeLast()
{
???T?element?=?elementData[--size];
???elementData[size]?=?null;
???return?element;
}
size&isEmpty
這兩個方法FastList都沒有進(jìn)行任何優(yōu)化,和ArrayList是一樣的。
總結(jié)
現(xiàn)在知道HikariCP為什么要這樣做了吧?這是因為,HikariCP的FastList和JDK中的ArrayList定位不一樣,ArrayList是提供給無數(shù)使用JDK的用戶使用的,所以,它的使用環(huán)境非常惡劣,那么它必須做各種合法性的檢查。而FastList是HikariCP用來管理Statement的,是給它自己使用的,是特定場景下為了性能極致優(yōu)化而造出來的一個東西,它只適用于這樣特定的場景。
往期推薦
掃一掃,關(guān)注我
一起學(xué)習(xí),一起進(jìn)步
每周贈書,福利不斷
﹀
﹀
﹀
深度內(nèi)容
推薦加入




