Java是一種可以撰寫跨平臺應(yīng)用軟件的面向?qū)ο蟮某绦蛟O(shè)計語言。Java 技術(shù)具有卓越的通用性鲸拥、高效性拐格、平臺移植性和安全性,廣泛應(yīng)用于PC刑赶、數(shù)據(jù)中心捏浊、游戲控制臺、科學(xué)超級計算機(jī)撞叨、移動電話和互聯(lián)網(wǎng)金踪,同時擁有全球最大的開發(fā)者專業(yè)社群浊洞。
給你學(xué)習(xí)路線:html-css-js-jq-javase-數(shù)據(jù)庫-jsp-servlet-Struts2-hibernate-mybatis-spring4-springmvc-ssh-ssm
小編推薦一個學(xué)Java的學(xué)習(xí)裙【 六五零,五五四胡岔,六零七 】法希,無論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進(jìn)步一起學(xué)習(xí)靶瘸!裙內(nèi)有開發(fā)工具铁材,很多干貨和技術(shù)資料分享!
1奕锌。用StringBuilder(技術(shù)篇)
StingBuilder應(yīng)該在我們的java代碼默認(rèn)使用著觉,應(yīng)避免使用運(yùn)算符。也許你會對StringBuilder的語法糖的不同意見惊暴,如:
字符串x =“”+ args.length +“B”饼丘;
它將被編譯成:
0新java.lang.stringbuilder [ 16 ]
3杯
4最不發(fā)達(dá)國家[ 18 ]
6 java.lang.stringbuilder invokespecial(java。lang.string)[ 20 ]
9 aload_0 [參數(shù)]
10獲取數(shù)組長度
11 java.lang.stringbuilder.append虛調(diào)用(int):java.lang.stringbuilder [ 23 ]
14最不發(fā)達(dá)國家[ 27 ]
16 java.lang.stringbuilder.append虛調(diào)用(java辽话。lang.string):java.lang.stringbuilder [ 29 ]
19 java.lang.stringbuilder.tostring虛調(diào)用():java.lang.string [ 32 ]
22 astore_1 [X]
不過肄鸽,這是怎么回事呢?您需要使用以下部分來改進(jìn)字符串嗎油啤?
字符串x =“”+ args.length +“B”典徘;
如果(args.length = 1)
x = x + args [ 0 ];
現(xiàn)在使用的第二StringBuilder益咬,這個StringBuilder不在堆消耗更多的內(nèi)存逮诲,但它帶來的壓力,氣相色譜法幽告。
StringBuilder x =新StringBuilder(“”)梅鹦;
X.append(args。長度)冗锁;
X.append(“B”)齐唆;
如果(args.length = 1);
X.append(args [ 0 ])冻河;
總結(jié)
在上面的例子中箍邮,如果你依靠java編譯器創(chuàng)建的實(shí)例,編譯的效果幾乎沒有是否使用StringBuilder實(shí)例叨叙。請記锥П住:在N.O.P.E分公司,每個CPU周期時間花在GC或StringBuilder分配默認(rèn)的空間摔敛,我們浪費(fèi)了N x O X P的時間廷蓉。
在一般情況下,使用StringBuilder的效果優(yōu)于運(yùn)算符的使用马昙。如果可能的話桃犬,請選擇StringBuilder當(dāng)你需要通過參考多個方法,因?yàn)樽址念~外的資源行楞。jooq采用這樣一種方式來生成復(fù)雜的SQL語句攒暇。只有一個StringBuilder是整個AST抽象語法樹(樹)中使用SQL傳遞。
更悲劇的是子房,如果你還在使用StringBuffer形用,取代StringBuilder StringBuffer。畢竟证杭,對同步字符串并不需要太多田度。
2。避免使用正則表達(dá)式(好的技術(shù)文本)
正則表達(dá)式給人的印象是快速而簡單解愤。但在N.O.P.E分公司使用正則表達(dá)式將是最壞的決定镇饺。如果必須使用正則表達(dá)式,但在代碼中計算密集型送讲,至少會緩存模式奸笤,避免重復(fù)編譯模式。
靜態(tài)模式heavy_regex =
pattern.compile (((((((((((( x)*)* Z *”)哼鬓;
如果只使用一個簡單的正則表達(dá)式如下:
字符串[]部分= ipaddress.split(“监右。”)异希;
這是使用普通字符數(shù)組或基于索引的操作的最好方法健盒。例如,較低的可讀代碼實(shí)際上扮演相同的角色称簿。
int length = ipaddress.length()味榛;
int偏移量= 0;
int部分= 0予跌;
對于(int = i 0搏色;i <長度;i + +){
如果(我= =長1 | |
ipaddress.charat(i + 1)= =和# 39){券册。與# 39频轿;;
零件[零件]
IpAddress.substring(偏移烁焙,我+ 1)航邢;
零件+;
偏移量=i + 2骄蝇;
}
}
上面的代碼也表明過早的優(yōu)化是沒有意義的膳殷。盡管與拆分()方法相比,此代碼的可維護(hù)性相對較差九火。
挑戰(zhàn):聰明的小伙伴能想出更快的算法嗎赚窃?歡迎留言册招。
總結(jié)
正則表達(dá)式非常有用,但它們在使用時也要付出代價勒极。特別是是掰,在n.o.p.e分支的深度,你應(yīng)該避免使用任何代碼來避免正則表達(dá)式辱匿。同時對各種JDK字符串方法中使用正則表達(dá)式的小心键痛,如string.replaceall()或String.split()。您可以選擇使用更受歡迎的開發(fā)庫(如Apache公共資源庫)來進(jìn)行字符串操作匾七。
三.不要使用迭代器()方法
這個建議并不適用于一般的情況絮短,只適用于一個在N.O.P.E分行深景。盡管如此昨忆,還是應(yīng)該有所了解的丁频。java 5格式的方便,我們可以忘記內(nèi)循環(huán)的方法扔嵌,如:
對于(字符串值:字符串){
這里有用的東西
}
當(dāng)代碼運(yùn)行到這個循環(huán)的每一次限府,如果變量是一個字符串,程序會自動創(chuàng)建迭代器的實(shí)例痢缎。如果ArrayList使用胁勺,虛擬機(jī)會自動分配3的整數(shù)類型大小的內(nèi)存堆上的對象。
私有類ITR實(shí)現(xiàn)迭代器{
int指針独旷;
國際lastret = - 1署穗;
國際expectedmodcount = modcount;
/ /…
還可以用下面的等效循環(huán)代替上面的for循環(huán)嵌洼。這只是堆棧上的“浪費(fèi)”案疲。
int的大小= strings.size();
對于(int = i 0麻养;i <大泻址取;i + +){
字符串值:strings.get(我)鳖昌;
這里有用的東西
}
如果循環(huán)中字符串的值沒有改變备畦,則可以使用一個數(shù)組來實(shí)現(xiàn)循環(huán)。
對于(字符串值:stringarray){
這里有用的東西
}
總結(jié)
迭代器许昨,Iterable接口懂盐,和Foreach循環(huán)是非常好的,無論是從容易寫的角度糕档,還是從API的設(shè)計角度莉恼。但以使用它們?yōu)榇鷥r,為堆中的每一個循環(huán)創(chuàng)建一個對象。如果循環(huán)要多次執(zhí)行俐银,請注意避免產(chǎn)生無意義的實(shí)例尿背。這是更好地使用基本的指針而不是迭代器,迭代器接口和foreach循環(huán)悉患。
討論
一些反對上述觀點(diǎn)(尤其是代替迭代器指針操作)在Reddit討論詳細(xì)残家。
4榆俺。不要調(diào)用開銷過高的方法售躁。
有些方法很貴。以n.o.p.e分行為例茴晋,我們不提葉相關(guān)的方法陪捷,但這是可以。我們假設(shè)的JDBC驅(qū)動程序需要克服一切困難诺擅,計算resultset.wasnull(返回值的方法)市袖。我們自己的SQL框架的實(shí)現(xiàn)可能如下所示:
如果(type =整數(shù)類){
結(jié)果=(t)wasnull(RS,
integer.valueof(rs.getint(指數(shù))))烁涌;
}
和/ /然后…
靜態(tài)T wasnull(連接數(shù)據(jù)庫苍碟,T值)
拋出SQLException {
返回rs.wasnull()?空值撮执;
}
在上述邏輯的resultset.wasnull()方法被調(diào)用微峰,每一次的int值從獲得結(jié)果集,但是方法getInt()定義為:
返回類型:變量值抒钱;如果SQL查詢結(jié)果為null蜓肆,返回0。
因此谋币,一個簡單而有效的改進(jìn)方法如下:
靜態(tài)wasnull(T
連接數(shù)據(jù)庫仗扬,T值
)
拋出SQLException {
返回值= = null | |(
((價值。intvalue)= = 0和rs.wasnull()))
空值蕾额;
}
這是一件容易的事早芭。
總結(jié)
方法調(diào)用被緩存以替換葉節(jié)點(diǎn)中的高開銷方法,或者在方法約定允許時避免調(diào)用高開銷方法诅蝶。
5退个。使用原始類型和堆棧
大量的仿制藥在使用的例子從jooq,致使字節(jié)秤涩,短帜乞,int的封裝類的使用,和長筐眷。但在推廣專業(yè)的java 10或瓦爾哈拉的項目黎烈,它不應(yīng)該是一個限制的代碼。因?yàn)橄旅娴姆椒梢杂孟旅娴姆椒▉泶妫?/p>
/存儲在堆中
整數(shù)i=817598;
......如果這是用這種方式寫的:
/存儲在堆棧中
int = i 817598照棋;
使用數(shù)組時情況可能變得更糟资溃。
/在堆上生成三個對象
整數(shù)[ [] = { 1337, 424242 };
......如果這是用這種方式寫的:
僅在生成對象的堆上
int([]){ { 1337, 424242 }烈炭;
總結(jié)
當(dāng)我們在n.o.p.e.分支的深處溶锭,我們應(yīng)該盡量避免使用包裝類。做這件事的一件壞事是它給GC帶來了很大的壓力符隙。GC將太忙趴捅,無法清除包裝器類生成的對象。
因此霹疫,一種有效的優(yōu)化方法是使用基本數(shù)據(jù)類型拱绑,固定長度數(shù)組,并使用一系列的分割變量來確定數(shù)組中對象的位置丽蝎。
trove4j猎拨,遵循LGPL協(xié)議,是一個java集合類的庫屠阻,它為我們提供了比整形數(shù)組int []性能更好的實(shí)現(xiàn)红省。
例外
以下是此規(guī)則的例外情況:由于布爾和字節(jié)類型都不足以讓JDK為他們提供一個緩存的方法。我們可以寫這個:
布爾= true国觉;//語法糖:…
布爾A2 = boolean.valueof(真的)吧恃;
字節(jié)B1(字節(jié))= 123;//語法糖:…
字節(jié)B2 = byte.valueof((字節(jié))123)蛉加;
其他整數(shù)的基本類型也有類似的情況蚜枢,如char、短针饥、int和長厂抽。
不自動包裝或調(diào)用thetype.valueof()方法這些基本類型調(diào)用構(gòu)造函數(shù)的方法。
不要在包裝器類上調(diào)用構(gòu)造函數(shù)丁眼,除非您希望獲得堆中未創(chuàng)建的實(shí)例筷凤。
小編推薦一個學(xué)Java的學(xué)習(xí)裙【 六五零,五五四苞七,六零七 】藐守,無論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進(jìn)步一起學(xué)習(xí)蹂风!裙內(nèi)有開發(fā)工具卢厂,很多干貨和技術(shù)資料分享!
6惠啄。避免遞歸(真正的技術(shù)文本)
現(xiàn)在慎恒,像斯卡拉這樣的函數(shù)式編程語言被鼓勵使用遞歸任内。因?yàn)檫f歸通常意味著尾遞歸(尾遞歸),可以分解為單個的個體優(yōu)化融柬。如果使用編程語言死嗦,最好能支持它。但即使如此粒氧,也需要注意的是越除,算法的微調(diào)將使尾遞歸成為普通遞歸。
我們希望編譯器能自動檢測到這一點(diǎn)外盯。否則摘盆,我們將浪費(fèi)大量的堆棧幀,而我們只需要使用幾個局部變量门怪。
總結(jié)
有什么要說的這部分骡澈,除了迭代而不是遞歸的n.o.p.e使用分支锅纺。
7掷空。使用子查詢()
當(dāng)我們想要遍歷一個以鍵值對形式保存的映射時,我們必須為下面的代碼找到一個好的理由:
為(K鍵:map.keyset()){
V值:map.get(關(guān)鍵)囤锉;
}
更不用說以下文字了:
對于(進(jìn)入< k坦弟,v >輸入:map.entryset()){
K鍵= entry.getkey();
V值= entry.getvalue()官地;
}
我們應(yīng)該小心酿傍,當(dāng)我們使用n.o.p.e.分支圖。因?yàn)樵S多看似復(fù)雜的O(1)訪問操作是由一系列操作組成的驱入。這次訪問本身并不是免費(fèi)的赤炒。至少,如果你要使用地圖亏较,然后使用entrySet()方法進(jìn)行迭代莺褒!這樣,我們所要訪問的map.entry例雪情。
總結(jié)
的entrySet()方法必須使用當(dāng)你需要迭代的核心價值以地圖的形式遵岩。
8。使用EnumMap(真EnumSet或技術(shù))
例如巡通,在某些情況下尘执,當(dāng)使用配置映射時,我們可能知道預(yù)先存儲在map中的鍵值宴凉。如果這個關(guān)鍵是非常小的誊锭,我們應(yīng)該考慮使用EnumSet或EnumMap代替我們共同的好或HashMap。下面的代碼給出了一個明確的解釋:
私人瞬態(tài)對象[ ]瓦爾斯弥锄;
公共v(k鍵丧靡,v值){
/ /…
指數(shù)= key.ordinal()签孔;
瓦爾斯[索引] = masknull(價值);
/ /…
}
上層代碼的主要實(shí)現(xiàn)是使用數(shù)組而不是哈希表窘行。特別是饥追,當(dāng)你插入一個新的值到一個地圖,你要做的是得到一個恒定的序列號生成的編譯器為每個枚舉類型罐盔。如果有一個全球地圖的配置(例如但绕,只有一個實(shí)例),EnumMap將獲得更優(yōu)異的性能比HashMap如果訪問速度提高惶看。原因是使用EnumMap堆內(nèi)存小于HashMap(位)捏顺,和HashMap()調(diào)用hashCode方法和equals()在每一個關(guān)鍵值的方法。
總結(jié)
枚舉和EnumMap是親密的小伙伴纬黎。當(dāng)我們使用類似的枚舉的鍵值(枚舉等)的結(jié)構(gòu)幅骄,我們應(yīng)該考慮這些關(guān)鍵值被聲明為枚舉類型和使用它們作為EnumMap鍵。
9本今、習(xí)慣的hasCode優(yōu)化()方法和equals()方法(技術(shù)篇)
至少拆座,hashCode()=()方法進(jìn)行優(yōu)化時,EnumMap是沒有用的冠息。一個好的hashCode()方法是必要的因?yàn)樗梢苑乐共槐匾恼{(diào)用開銷大挪凑,等于()方法。
在每個類的繼承結(jié)構(gòu)中逛艰,都很容易接受簡單的對象躏碳。讓我們來看看如何jooq的org.jooq.table實(shí)施?
實(shí)施hashCode的最簡單和最快的方式()()如下:
基于一般的表/ abstracttable實(shí)施:
@Override
public int hashCode(){
與標(biāo)準(zhǔn)queryparts / [ 1938 ] #相比散怖,這是一個更有效的hashCode(實(shí)現(xiàn))
返回name.hashcode()菇绵;
}
名稱是表的名稱。我們甚至不需要考慮模式或其他表屬性镇眷,因?yàn)楸砻跀?shù)據(jù)庫中通常是唯一的咬最。和變量名是一個字符串,它本身已經(jīng)緩存的hashCode()值偏灿。
在這段代碼中的注釋非常重要丹诀,因?yàn)槔^承了abstractquerypart的abstracttable是基本實(shí)現(xiàn)任何抽象語法樹元。普通的抽象語法樹的元素沒有任何屬性翁垂,所以有關(guān)于hashCode的優(yōu)化沒有幻想()方法的實(shí)現(xiàn)铆遭。hashCode()方法后內(nèi)容如下:
abstractquerypart /通用的抽象語法樹的實(shí)現(xiàn):
@Override
public int hashCode(){
這是一個默認(rèn)的實(shí)現(xiàn)。
/具體實(shí)現(xiàn)子類應(yīng)覆蓋改進(jìn)性能的方法沿猜。
返回創(chuàng)建()枚荣。RenderInlined(這)HashCode();
}
換句話說啼肩,觸發(fā)整個SQL渲染工作流(呈現(xiàn)工作流)來計算普通抽象語法樹元素的哈希代碼橄妆。
等號()方法更有趣:
/ / abstracttable一般表實(shí)施的基礎(chǔ):
@Override
公共布爾值等于(對象{){
如果(= =){
返回true衙伶;
}
電話/ [ 2144 ] #高開銷abstractquerypart.equals()方法,
可能知道物體不等于害碾。
如果(這是abstracttable){
如果(stringutils.equals(名稱)
((((( abstracttable <矢劲?>)
返回super.equals(,)慌随;
}
返回false芬沉;
}
返回false;
}
首先阁猜,不要使用equals()方法的太早(不僅在n.o.p.e.)丸逸,如果:
這個=參數(shù)
不兼容:參數(shù)
注意:如果我們使用instanceof測試兼容型早期,后者的情況實(shí)際上包含參數(shù)= = null剃袍。
通過以上案例的比較黄刚,我們可以得出一些結(jié)論。jooq的table.equals()方法民效,例如憔维,用于比較兩個表是否是相同的。不管具體的實(shí)現(xiàn)類型是什么研铆,它們都必須具有相同的字段名埋同。例如,以下兩個元素不能相同:
com.example.generated.tables.my_table
DSL.tableByName(“my_other_table”)
如果我們可以很容易地判斷傳入?yún)?shù)是否等于實(shí)例本身(這個)棵红,那么當(dāng)結(jié)果為false時,我們可以放棄操作咧栗。如果返回結(jié)果是真的逆甜,我們可以進(jìn)一步判斷父類(超級)實(shí)現(xiàn)。當(dāng)大多數(shù)對象不同時致板,我們可以盡可能快地結(jié)束方法交煞,以節(jié)省CPU的執(zhí)行時間。
有些物體的相似性比其他物體的相似性高斟或。
在jooq素征,大多數(shù)的表的情況下jooq代碼生成器生成的,和equals()這些實(shí)例方法已經(jīng)深入優(yōu)化萝挤。哪種類型的衍生工具(派生表)(表御毅、表值函數(shù)(表值、函數(shù)))(數(shù)組怜珍、表)數(shù)組表表(連接表)端蛆、連接PivotTable(樞軸表)、公共表表達(dá)式(公共表表達(dá)式)酥泛、相等()是維護(hù)基本實(shí)現(xiàn)方法今豆。
10嫌拣。考慮使用set而不是單個元素(技術(shù)文本)
最后呆躲,還有一種情況异逐,可以應(yīng)用到所有的語言,不僅與java插掂。此外应役,該n.o.p.e.分支,我們的研究也將有助于了解從O(N3)為O(n log n)燥筷。
不幸的是箩祥,許多程序員使用簡單的本地算法來考慮問題。他們習(xí)慣于有條不紊地解決這個問題肆氓。這是“命令”形式的函數(shù)式編程風(fēng)格袍祖。這種編程風(fēng)格很容易從單純的命令式編程到面向程序設(shè)計的函數(shù)式編程。但是谢揪,這些樣式只在SQL和R語言中丟失蕉陋。
聲明式編程。
在SQL中拨扶,我們可以聲明數(shù)據(jù)庫的效果而不考慮算法的效果凳鬓。數(shù)據(jù)庫可以根據(jù)不同類型的數(shù)據(jù),例如約束患民、密鑰和索引來選擇最佳算法缩举。
從理論上講,我們最初在SQL和關(guān)系演算之后有一個基本概念匹颤。在實(shí)踐中仅孩,SQL廠商實(shí)現(xiàn)架空型高效優(yōu)化器CBO(基于成本的優(yōu)化器)在過去的幾十年里。然后印蓖,在2010版中辽慕,我們終于發(fā)掘了SQL的所有潛力。
但是我們不必在集合中實(shí)現(xiàn)SQL赦肃。所有語言和庫都支持集合溅蛉、集合、包和列表他宛。使用set的主要優(yōu)點(diǎn)是船侧,我們可以使代碼簡潔明了。例如堕汞,以下是書面的:
someotherset索美塞相交
而不是
8 / / java之前寫的
設(shè)置結(jié)果=新好()勺爱;
(對象為候選人:索美塞)
如果(someotherset.contains(候選人))
result.add(候選人);
即使有/ java 8不是很有幫助
someset.stream()
讯检。過濾器(someotherset::包含)
收集(Collectors.toSet())琐鲁;
有些人可能對函數(shù)式編程和java 8種不同的意見幫助我們編寫更簡單卫旱、更簡潔的算法。但這種觀點(diǎn)并不一定正確围段。我們可以把命令式j(luò)ava 7環(huán)路到j(luò)ava 8流的集合顾翼,但我們?nèi)匀皇褂孟嗤乃惴ā5荢QL樣式的表達(dá)式是不同的:
someotherset索美塞相交
上面的代碼可以在不同引擎上實(shí)現(xiàn)1000種不同的實(shí)現(xiàn)奈泪。我們今天學(xué)習(xí)的是适贸,在交叉操作時,兩套自動轉(zhuǎn)換為EnumSet涝桅。我們甚至可以做平行交叉作業(yè)而不需要調(diào)用底層的Stream.parallel()方法拜姿。
小編推薦一個學(xué)Java的學(xué)習(xí)裙【 六五零,五五四冯遂,六零七 】蕊肥,無論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進(jìn)步一起學(xué)習(xí)蛤肌!裙內(nèi)有開發(fā)工具壁却,很多干貨和技術(shù)資料分享!
總結(jié)
在這篇文章中裸准,我們討論的n.o.p.e.分支優(yōu)化展东。例如,深入研究算法的復(fù)雜度炒俱。作為開發(fā)商的jooq盐肃,我們很高興能優(yōu)化SQL生成。
每一個查詢是一個獨(dú)特的StringBuilder產(chǎn)生向胡。
模板引擎實(shí)際上處理字符而不是正則表達(dá)式恼蓬。
盡可能多地選擇數(shù)組,尤其是當(dāng)您遍歷偵聽器時僵芹。
通往jdbc的路很遠(yuǎn)。
等待小槐。
jooq是在“食物鏈”的底部拇派,因?yàn)樗亲詈笠粋€API調(diào)用的計算機(jī)程序,當(dāng)它的葉子JVM DBMS凿跳。在食物鏈的底部意味著任何線需要n x O X P時jooq執(zhí)行件豌,所以我想盡快優(yōu)化。
我們的業(yè)務(wù)邏輯可能不n.o.p.e.復(fù)雜控嗜。但底層框架可能非常復(fù)雜(本地SQL框架茧彤、本地庫等)。所以我們需要檢查它與java任務(wù)控制或其他工具疆栏,按照原則曾掂,我們確認(rèn)是否有一個地方是優(yōu)化今天提到惫谤。