# 七行代碼的九種性能優化方法
原題如下,如下代碼用于拼接字符串,試找出所有9種性能優化方法
```java
public String buildProvince(List<Org> orgs){
StringBuilder sb = new StringBuilder();
for(Org org:orgs){
if(sb.length()!=0){
sb.append(",")
}
sb.append(org.getProvinceId());
}
return sb.toString();
}
```
> 所有優化方法實現在 https://gitee.com/xiandafu/java-performance/tree/master/helloworld,優化的JMH測試結果也在例子的comment里
>
> 這個例子的9種優化方法,在本書里均有介紹
## 1 檢查是否為空
提前檢測參數,盡早返回可能的結果.
```java
public static String buildProvinceWithEmptyCheck(List<Org> orgs){
//檢測是否為空
if(orgs.isEmpty()){
return "";
}
.......
}
```
## 2 檢測集合只有一個
如果集合只有一個,則只需要做特殊處理
```java
public static String buildProvinceWithSizeCheck(List<Org> orgs){
//如果只包含一個元素
if(orgs.size()==1){
return String.valueOf(orgs.get(0).getProvinceId());
}
.......
}
```
Java的String的split方法,就是對正則表達式判斷,如果長度為1,則不采用正則表達式解析字符串,從而提供系統性能
## 3 采用subString
```java
public static String buildProvinceWithSubString(List<Org> orgs){
StringBuilder sb = new StringBuilder();
for(Org org:orgs){
sb.append(org.getProvinceId()).append(",");
}
//去掉最后一個逗號
return sb.substring(0,sb.length());
}
```
這樣,避免了每次循環中的使用if來判斷。(注意,這里假定orgs是有值的)
## 4 使用char拼接
```java
public static String buildProvinceWithChar(List<Org> orgs){
StringBuilder sb = new StringBuilder();
for(Org org:orgs){
//使用char 代替String
sb.append(org.getProvinceId()).append(',');
}
sb.setLength(sb.length()-1);
return sb.toString();
}
```
通過StringBuilder源碼,可以看到append(char)效率比append(String)要高多了
## 5 預先估計StringBuilder容量
對集合,StringBuilder等預先估計一個容量,避免容量再次增長帶來的性能影響
```java
public static String buildProvinceWithInitCapacity(List<Org> orgs){
//假設省Id不超過2位,初始化一個大小
StringBuilder sb = new StringBuilder(orgs.size()*3);
.......
}
```
## 6 采用StringBuffer
StringBuffer并不比StringBuilder慢
```java
public static String buildProvinceWithStringBuffer(List<Org> orgs){
//StringBuffer
StringBuffer sb = new StringBuffer();
........
return sb.toString();
}
```
一方面虛擬機有鎖逃逸優化,避免了Synchronized 帶來的性能影響,另外,StringBuffer的toString方法實現比較特別,它的實現如下
```java
@Override
public synchronized String toString() {
return new String(toStringCache, true);
}
```
也就是用StringBuffer構造String的時候,復用了StringBuffer的char[], 對比StringBuilder,則采用更安全的方式,構造String的時候重新復制了一份。
## 7 provinceId提前轉為字符串
int轉為字符串實際上相當耗時,可以提前轉化,這也是所有優化方式里最明顯的的一種
```java
public static String buildProvinceWithCacheData(List<Org> orgs){
StringBuilder sb = new StringBuilder();
for(Org org:orgs){
//使用預先初始化好的chars
char[] chars = strProvince[org.getProvinceId()];
sb.append(chars).append(',');
}
sb.setLength(sb.length()-1);
return sb.toString();
}
static char[][] strProvince;
static{
strProvince = new char[31][];
for(int i=0;i<31;i++){
strProvince[i] = String.valueOf(i).toCharArray();
}
}
```
再Java源碼里,int裝箱成Integer,也采用預先初始化了一部分Integer數據,以提高性能
## 8 緩存StringBuilder
如果每次調用都需要構建一個StringBuilder,可以放到ThreadLocal里緩存起來,也有較好的性能
```java
public static String buildProvinceWithCache(List<Org> orgs){
StringBuilder sb = local.get();
for(Org org:orgs){
if(sb.length()!=0){
sb.append(",");
}
sb.append(org.getProvinceId());
}
String str = sb.toString();
sb.setLength(0);
return str;
}
static ThreadLocal<StringBuilder> local = ThreadLocal.withInitial((Supplier) () -> new StringBuilder());
```
在Java源碼里,FloatingDecimal,用于講Double轉化String,也是采用了類似機制。 Jackson,FastJSON等高速序列化工具,也會提供可復用的輸出緩存區。如Jackson使用了BufferRecycler
## 9 循環展開
其他語言都有循環展開作為性能優化,Java也有此效果,這里僅僅作為演示例子,在此場景不具備實際用途
```java
/**
* 如果預先知道容量,比如能整除4,可以減少循環判斷
* @param orgs
* @return
*/
public static String buildProvinceWithLoop(List<Org> orgs){
StringBuilder sb = new StringBuilder();
int count = orgs.size()/4;
int i=0;
for(;i<count;i++){
sb.append(orgs.get(i).getProvinceId()).append(",");
sb.append(orgs.get(i+1).getProvinceId()).append(",");
sb.append(orgs.get(i+2).getProvinceId()).append(",");
sb.append(orgs.get(i+3).getProvinceId()).append(",");
}
//去掉最后一個逗號
sb.setLength(sb.length()-1);
return sb.toString();
}
```
- 內容介紹
- 第一章 Java系統優化
- 1.1 可優化的代碼
- 1.2 性能監控
- 1.3 JMH
- 1.3.1 使用JMH
- 1.3.2 JMH常用設置
- 1.3.3 注意事項
- 1.3.4 單元測試
- 第二章 字符串和數字
- 2 字符串和數字操作
- 2.1 構造字符串
- 2.2 字符串拼接
- 2.3 字符串格式化
- 2.4 字符串查找
- 2.6 intern方法
- 2.7 UUID
- 2.8 StringUtils類
- 2.9 前綴樹過濾
- 2.10 數字裝箱
- 2.11 BigDecimal
- 第三章 并發和異步編程
- 3.1 不安全的代碼
- 3.2 Java并發編程
- 3.2.1 volatile
- 3.2.2 synchronized
- 3.2.3 Lock
- 3.2.4 Condition
- 3.2.5 讀寫鎖
- 3.2.6 semaphore
- 3.2.7 柵欄
- 3.3 Java并發工具
- 3.3.1 原子變量
- 3.3.2 Queue
- 3.3.3 Future
- 3.4 Java線程池
- 3.5 異步編程
- 3.5.1 創建異步任務
- 3.5.2 完成時回調
- 3.5.3 串行執行
- 3.5.4 并行執行
- 3.5.5 接收任務處理結果
- 第四章 代碼性能優化
- 4.1 int 轉 String
- 4.2 使用Native 方法
- 4.3 日期格式化
- 4.4 switch 優化
- 4.5 優先用局部變量
- 4.6 預處理
- 4.7 預分配
- 4.8 預編譯
- 4.9 預先編碼
- 4.10 謹慎使用Exception
- 4.11 批處理
- 4.12 展開循環
- 4.13 靜態方法調用
- 4.14 高速Map存取
- 4.15 位運算
- 4.16 反射
- 4.17 壓縮
- 4.18 可變數組
- 4.19 System.nanoTime()
- 4.20 ThreadLocalRandom
- 4.21 Base64
- 4.22 辨別重量級對象
- 4.23 池化技術
- 4.24 實現hashCode
- 4.25 錯誤優化策略
- 4.25.1 final無法幫助內聯
- 4.25.2 subString 內存泄露
- 4.25.3 循環優化
- 4.25.4 循環中捕捉異常
- 4.25.5 StringBuffer性能不如StringBuilder高
- 第五章 高性能工具
- 5.1 高速緩存 caffeine
- 5.1.1 安裝
- 5.1.2 caffeine 基本使用
- 5.1.3 淘汰策略
- 5.1.4 statistics 功能
- 5.1.5 caffeine高命中率
- 5.1.6 卓越性能
- 5.2 selma映射工具
- 5.3 Json工具 Jackson
- 5.3.1 Jackson三種使用方式
- 5.3.3 對象綁定
- 5.3.2 Jackson 樹遍歷
- 5.3.4 流式操作
- 5.3.6 自定義 JsonSerializer
- 5.3.7 集合的反序列化
- 5.3.8 性能提升和優化
- 5.4 HikariCP
- 5.4.1 HikariCP安裝
- 5.4.3 HikariCP 性能測試
- 5.4.4 性能優化說明
- 5.5 文本處理Beetl
- 5.5.1 安裝和配置
- 5.5.2 腳本引擎
- 5.5.3 特點
- 5.5.4 性能優化
- 5.6 MessagePack
- 5.7 ReflectASM
- 第六章 Java注釋
- 6.1 JavaDoc
- 6.2 Tag
- 6.2.1 {@link}
- 6.2.2 @deprecated
- 6.2.3 {@literal}
- 6.2.4 {@code}
- 6.2.5 {@value}
- 6.2.6 @author
- 6.2.7 @param 和 @return
- 6.2.8 @throws
- 6.2.9 @see
- 6.2.10 自動拷貝
- 6.3 Package-Info
- 6.4 HTML生成
- 6.5 Markdown-doclet
- 第七章 可讀性代碼
- 7.1 精簡注釋
- 7.2 變量
- 7.2.1 變量命名
- 7.2.2 變量的位置
- 7.2.3 中間變量
- 7.3 方法
- 7.3 .1 方法簽名
- 7.3.2 小方法
- 7.3.3 單一職責
- 7.3.3 小類
- 7.4 分支
- 7.4.1 if else
- 7.4.2 switch case
- 7.5 發現對象
- 7.3.1 不要用String
- 7.3.2 不要用數組,Map
- 7.6 checked異常
- 7.7 其他
- 7.7.1 避免自動格式化
- 7.7.2 關于Null
- 第八章 JIT優化
- 8.1 解釋和編譯
- 8.2 C1和C2
- 8.3 代碼緩存
- 8.4 JITWatch
- 8.5 內聯
- 8.6 虛方法調用
- 第九章 代碼審查
- 9.1 ConcurrentHashMap陷阱
- 9.2 字符串搜索
- 9.3 IO輸出
- 9.4 字符串拼接
- 9.5 方法的入參和出參
- 9.6 RPC調用定義的返回值
- 9.7 Integer使用
- 9.8 排序
- 9.9 判斷特殊的ID
- 9.10 優化if結構
- 9.11 文件COPY
- 9.12 siwtch優化
- 9.13 Encoder
- 9.14一個JMH例子
- 9.15 注釋
- 9.16 完善注釋
- 9.17 方法抽取
- 9.18 遍歷Map
- 9.19 日期格式化
- 9.20 日志框架設計的問題
- 9.21 持久化到數據庫
- 9.22 某個RPC框架
- 9.23 循環調用
- 9.24 Lock使用
- 9.25 字符集
- 9.26 處理枚舉值
- 9.27 任務執行
- 9.28 開關判斷
- 9.29 JDBC操作
- 9.30 Controller代碼
- 9.31 停止任務
- 9.32 log框架
- 9.33 縮短UUID
- 9.34 Dubbo ThreadPool設置
- 9.35 壓縮設備信息
- 第十章 ASM運行時增強
- 10.1 Java字節碼
- 10.1.1 基礎知識
- 10.1.2 class文件格式
- 10.2 Java方法的執行
- 10.2.1 方法在內存的表示
- 10.2.3 方法在class文件中的表示
- 10.2.3 指令的分類
- 10.2.4 操作數棧的變化分析
- 10.3 Bytecode Outline插件
- 10.4 ASM入門
- 10.4.1 生成類名及構造函數
- 10.4.2 生成main方法
- 10.4.3 調用生成的代碼
- 10.5 ASM增強代碼
- 10.5.1 使用反射實現
- 10.5.2 使用ASM生成輔助類
- 10.5.3 switch語句的分類
- 10.5.4 獲取bean中的property
- 10.5.5 switch語句的實現
- 10.5.6 性能對比
- 第十一章 JSR269編譯時增強
- 11.1 Java編譯的過程
- 11.2 注解處理器入門
- 11.3 相關概念介紹
- 11.3.1 AbstractProcessor
- 11.3.2 Element與TypeMirror
- 11.4 注解處理器進階
- 11.4.1 JsonWriter注解
- 11.4.2 處理器與生成輔助類
- 11.4.3 使用生成的Mapper類
- 11.4.4 注解處理器的使用
- 11.5 調試注解處理器
- 11.5.1 Eclipse中調試注解處理器
- 11.5.2 Idea中調試注解處理
- 附錄A OQL分析JVM內存(免費)
- 附錄B 7行代碼的9種性能優化方法(免費)
- 附錄 C CPUCacheTest更多討論(免費)