Java 如何高效打印ByteArray的內(nèi)容

在使用TCP協(xié)議作為傳輸協(xié)議時(shí),很多時(shí)候都需要輸出原始報(bào)文用來查看傳輸數(shù)據(jù)是否正確。特別是在物聯(lián)網(wǎng)中的硬件服務(wù)器,上下行的數(shù)據(jù)量非常大,任何關(guān)于報(bào)文的處理的開銷都會(huì)因?yàn)橄⒌脑黾佣杀斗糯蟆?/p>

不管使用ByteArray或者ByteBuffer當(dāng)做數(shù)據(jù)容器,輸出日志時(shí),都需要進(jìn)行兩步,第一步:把字節(jié)數(shù)組的字節(jié)轉(zhuǎn)成字符;第二步:在拼接字符形成字符串。

一般的處理方式

迭代拼接字符串

創(chuàng)建StringBuilder,迭代ByteArray,格式化每個(gè)byte后,追加到StringBuilder中,最后使用StringBuilder.toString()方法輸出完整字符串。

    /**
     * 根據(jù)字節(jié)數(shù)組,輸出對(duì)應(yīng)的格式化字符串
     * @param bytes 字節(jié)數(shù)組
     * @return 字節(jié)數(shù)組字符串
     */
    public static String printBytesByStringBuilder(byte[] bytes){
        StringBuilder stringBuilder = new StringBuilder();

        for (byte aByte : bytes) {
            stringBuilder.append(byte2String(aByte));
        }

        return stringBuilder.toString();

    }

    public static String byte2String(byte b){

        return String.format("%02x ",b);
    }

特定優(yōu)化處理

預(yù)分配字符數(shù)組,使用常量池來實(shí)現(xiàn)字節(jié)到字符的轉(zhuǎn)換。填充提前預(yù)分配好的字符數(shù)組。輸出字符串!
第一步:窮舉所有byte對(duì)應(yīng)的字符數(shù)組


窮舉所有的byte對(duì)應(yīng)的兩位字符

第二步:使用索引的方式進(jìn)行字符串拼接


    /**
     * 使用字符數(shù)組進(jìn)行byte字節(jié)信息的輸出 如果默認(rèn)進(jìn)制標(biāo)識(shí)的話,不打印'0x',一個(gè)byte只需要兩個(gè)char 例如: 0x9  = '0' + '9' 0xAF = 'A' + 'F'
     * 0x9 0xAF = > '0'+'9'+' '+'A'+'F'
     * <p>
     * 一個(gè)byte需要2個(gè)字符標(biāo)識(shí),外加一個(gè)空格字符
     *
     * @param bytes 需要格式化的byte
     * @return 字節(jié)數(shù)組 字符串
     */
    public static String printBytesByCharPool(byte[] bytes) {
        int byteLength = bytes.length;
        int charLength = byteLength * 3;


        char[] content = new char[charLength];
        int unsignedByte = 0;
        int startIndex = 0;
        for (int i = 0; i < byteLength; i++) {
            // 使byte變?yōu)闊o符號(hào)的byte
            // b2u
            unsignedByte = ((int) bytes[i]) & 0xFF;

            char[] chars = BYTE_CHARS[unsignedByte];
            startIndex = i * 3;
            // byte的第一位字符
            content[startIndex] = chars[0];
            // byte格式化的第二位字符
            content[startIndex + 1] = chars[1];
            // 尾隨的空格字符
            content[startIndex + 2] = WHITE_CHAR;
        }

        return new String(content);
    }

分別使用1024長(zhǎng)度的byteArray進(jìn)行循環(huán)1200次打印結(jié)果:

# 使用stringBuilder
printBytesByStringBuilder useTime=1070
# 使用字符池
printBytesByCharPool useTime=4

可以看出來速度差別在兩個(gè)數(shù)量級(jí)

其他開銷分析

分析這兩種方法存在的性能開銷點(diǎn)有:String.format的性能,StringBuilder的性能

String.format

String.format方法用來格式化代碼,進(jìn)入源碼可以看到,每次都會(huì)創(chuàng)建一個(gè)模板對(duì)象,不能復(fù)用,這個(gè)必然存在一定的性能開銷

 public static String format(String format, Object... args) {
        return new Formatter().format(format, args).toString();
    }

StringBuilder的性能

雖然StringBuilder 已經(jīng)是最常用的字符串拼接方法,如果頻繁調(diào)用也會(huì)發(fā)現(xiàn)內(nèi)部其實(shí)也有動(dòng)態(tài)擴(kuò)容的機(jī)制,類似于hasmap的擴(kuò)容。當(dāng)容量沒有預(yù)估時(shí),會(huì)發(fā)生多次動(dòng)態(tài)擴(kuò)容。

使用場(chǎng)景

在用于做Tcp服務(wù)器時(shí),客戶端與服務(wù)端使用Tcp長(zhǎng)連接進(jìn)行通訊,需要輸出原始報(bào)文進(jìn)行信息查看和追蹤,這個(gè)時(shí)候輸出報(bào)文的開銷就伴隨這設(shè)備增多會(huì)成倍放大,關(guān)于輸出Tcp中報(bào)文字節(jié)的開銷就不能小視。
如果是次場(chǎng)景優(yōu)化,其他可以采取的方法有:

  1. 盡量使用定長(zhǎng)協(xié)議,可以提前分割定長(zhǎng)協(xié)議的格式化所需要內(nèi)存空間
  2. 如果在輸出字節(jié)數(shù)組時(shí),需要增加前綴或者后綴,也可以在申請(qǐng)字節(jié)數(shù)組對(duì)應(yīng)的字符數(shù)組時(shí)提前把前后綴的內(nèi)容對(duì)應(yīng)的空間一起創(chuàng)建出來
  3. 如果消息量特別大時(shí)也可以考慮使用隊(duì)列進(jìn)行延遲或異步打印,不影響網(wǎng)絡(luò)吞吐
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.常量&變量 1.1.直接賦值常量值,禁止聲明新對(duì)象 直接賦值常量值,只是創(chuàng)建了一個(gè)對(duì)象引用,而這個(gè)對(duì)象引用指向...
    非著名程序員i閱讀 353評(píng)論 0 0
  • Java繼承關(guān)系初始化順序 父類的靜態(tài)變量-->父類的靜態(tài)代碼塊-->子類的靜態(tài)變量-->子類的靜態(tài)代碼快-->父...
    第六象限閱讀 2,261評(píng)論 0 9
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,876評(píng)論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn),但是人生放棄了冒險(xiǎn),也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 8,156評(píng)論 0 4

友情鏈接更多精彩內(nèi)容