在使用Java編程語言時,我們將繼續討論與建議的實踐有關的系列文章,我們將討論String性能調優。 特別是,我們將重點介紹使用默認編碼時如何有效地處理字符到字節和字節到字符的轉換。 本文總結了兩種提議的自定義方法與兩種經典方法(“ String.getBytes() ”和NIO ByteBuffer )的性能比較, 后者將字符轉換為字節,反之亦然。
所有討論的主題均基于用例,這些用例來自于電信行業的關鍵任務超高性能生產系統的開發。
在閱讀本文的每個部分之前,強烈建議您參考相關的Java API文檔以獲取詳細信息和代碼示例。
所有測試均針對具有以下特征的Sony Vaio進行:
- 系統:openSUSE 11.1(x86_64)
- 處理器(CPU):Intel(R)Core(TM)2 Duo CPU T6670 @ 2.20GHz
- 處理器速度:1,200.00 MHz
- 總內存(RAM):2.8 GB
- Java:OpenJDK 1.6.0_0 64位
應用以下測試配置:
- 并發工作者線程數:1
- 每個工作者的測試重復次數:1000000
- 整體測試次數:100
字符到字節和字節到字符的轉換
字符到字節和字節到字符的轉換被認為是Java開發人員的常見任務,這些開發人員正在針對網絡環境進行編程,處理字節數據流,序列化String對象,實現通信協議等。因此,Java提供了一些實用程序來啟用開發人員將String (或字符數組)轉換為等效的字節數組,反之亦然。
String類的“ getBytes(charsetName) ”操作可能是將String轉換為其等效的字節數組的最常用方法。 由于可以根據所使用的編碼方案來不同地表示每個字符,因此,上述操作需要“ charsetName ”以便正確轉換String字符也就不足為奇了。 如果未提供“ charsetName ”,則該操作使用平臺的默認字符集將String編碼為字節序列。
將字符數組轉換為其等效字節數組的另一種“經典”方法是使用NIO包的ByteBuffer類。 稍后將提供特定方法的示例代碼片段。
與更細粒度的方法相比,上述兩種方法雖然非常流行并且毫無爭議地易于使用和直接使用,但它們的性能都大大不足。 請記住, 我們不是在字符編碼之間進行轉換 。 為了在字符編碼之間進行轉換,您應該使用“ String.getBytes(charsetName) ”或NIO框架方法和實用程序來使用“經典”方法。
當所有要轉換的字符均為ASCII字符時,建議的轉換方法如下所示:
public static byte[] stringToBytesASCII(String str) {char[] buffer = str.toCharArray();byte[] b = new byte[buffer.length];for (int i = 0; i < b.length; i++) {b[i] = (byte) buffer[i];}return b;
}
通過將每個字符值轉換為等效的字節來構造結果字節數組,因為我們知道所有字符都在ASCII范圍內(0 – 127),因此只能占據一個 字節大小。
使用結果字節數組,我們可以通過使用“經典” 字符串構造函數“ new String(byte []) ”轉換回原始String 。
對于默認的字符編碼,我們可以使用下面顯示的方法將String轉換為字節數組,反之亦然:
public static byte[] stringToBytesUTFCustom(String str) {char[] buffer = str.toCharArray();byte[] b = new byte[buffer.length << 1];for(int i = 0; i < buffer.length; i++) {int bpos = i << 1;b[bpos] = (byte) ((buffer[i]&0xFF00)>>8);b[bpos + 1] = (byte) (buffer[i]&0x00FF);}return b;
}
Java中的每種字符類型都占用2個字節的大小。 為了將String轉換為等效的字節數組,我們將String的每個字符轉換為其2字節表示形式。
使用結果字節數組,我們可以使用以下提供的方法將其轉換回原始的String :
public static String bytesToStringUTFCustom(byte[] bytes) {char[] buffer = new char[bytes.length >> 1];for(int i = 0; i < buffer.length; i++) {int bpos = i << 1;char c = (char)(((bytes[bpos]&0x00FF)<<8) + (bytes[bpos+1]&0x00FF));buffer[i] = c;}return new String(buffer);
}
我們從其2字節表示形式構造每個String字符。 使用結果字符數組,我們可以通過使用“經典” 字符串構造函數“ new String(char []) ”將其轉換回原始String 。
最后但并非最不重要的一點是,我們提供了兩個使用NIO包的示例方法,以便將String轉換為其等效的字節數組,反之亦然:
public static byte[] stringToBytesUTFNIO(String str) {char[] buffer = str.toCharArray();byte[] b = new byte[buffer.length << 1];CharBuffer cBuffer = ByteBuffer.wrap(b).asCharBuffer();for(int i = 0; i < buffer.length; i++)cBuffer.put(buffer[i]);return b;
}
public static String bytesToStringUTFNIO(byte[] bytes) {CharBuffer cBuffer = ByteBuffer.wrap(bytes).asCharBuffer();return cBuffer.toString();
}
對于本文的最后一部分,我們提供了上述字符串到字節數組和字節數組到字符串轉換方法的性能比較表。 我們已經使用輸入字符串“ test string ”測試了所有方法。
首先將String轉換為字節數組的性能比較表:

橫軸表示測試運行的次數,縱軸表示每次測試運行的每秒平均事務數(TPS)。 因此,較高的值更好。 不出所料,與“ stringToBytesASCII(String) ”和“ stringToBytesUTFCustom(String) ”建議的方法相比,“ String.getBytes() ”和“ stringToBytesUTFNIO(String) ”方法的執行效果均較差。 如您所見,與“經典”方法相比,我們提出的方法可將TPS提高近30%。
最后將字節數組轉換為String的性能對比圖:

橫軸表示測試運行的次數,縱軸表示每次測試運行的每秒平均事務數(TPS)。 因此,較高的值更好。 不出所料,與“ bytesToStringUTFCustom(byte []) ”建議的方法相比,“ new String(byte []) ”和“ bytesToStringUTFNIO(byte []) ”方法的執行效果均較差。 如您所見,與“ new String(byte []) ”方法相比,我們提出的方法使TPS增長了近15%,與“ bytesToStringUTFNIO(byte []) ”方法相比,TPS增長了近30%。
總之,當您處理字符到字節或字節到字符的轉換,而又不想更改所使用的編碼時,可以通過使用自定義(細粒度)方法而不是使用提供的“經典”方法來獲得卓越的性能。 String類和NIO包。 當將測試字符串轉換為等效的字節數組時,與“經典”方法相比,我們提出的方法總體上提高了45%的性能。
快樂編碼
賈斯汀
聚苯乙烯
考慮到我們的一些讀者提出的使用“ String.charAt(int) ”操作而不是使用“ String.toCharArray() ”來將String字符轉換為字節的主張后,我更改了我們提出的方法,并重新執行測試。 如預期的那樣,進一步實現了性能提升。 特別地,在TPS 額外 13%的平均增加被記錄為“stringToBytesASCII(字符串)”方法和TPS的額外 2%平均增加被記錄為“stringToBytesUTFCustom(字符串)”。 因此,您應該使用更改后的方法,因為它們的性能甚至比原始方法還要好。 更新的方法如下所示:
public static byte[] stringToBytesASCII(String str) {byte[] b = new byte[str.length()];for (int i = 0; i < b.length; i++) {b[i] = (byte) str.charAt(i);}return b;
}
public static byte[] stringToBytesUTFCustom(String str) {byte[] b = new byte[str.length() << 1];for(int i = 0; i < str.length(); i++) {char strChar = str.charAt(i);int bpos = i << 1;b[bpos] = (byte) ((strChar&0xFF00)>>8);b[bpos + 1] = (byte) (strChar&0x00FF); }return b;
}
- Java最佳實踐–多線程環境中的DateFormat
- Java最佳實踐–高性能序列化
- Java最佳實踐– Vector vs ArrayList vs HashSet
- Java最佳實踐–字符串性能和精確字符串匹配
- Java最佳實踐–隊列之戰和鏈接的ConcurrentHashMap
翻譯自: https://www.javacodegeeks.com/2010/11/java-best-practices-char-to-byte-and.html