源碼淺談(一):java中的 toString()方法


前言:

      toString()方法 相信大家都用到過,一般用於以字符串的形式返回對象的相關數據。

  

  最近項目中需要對一個ArrayList<ArrayList<Integer>> datas  形式的集合處理。

  處理要求把集合數據轉換成字符串形式,格式為 :子集合1數據+"#"+子集合2數據+"#"+....+子集合n數據。

  舉例: 集合數據 :[[1,2,3],[2,3,5]]  要求轉成為 "[1,2,3]#[2,3,5]" 形式的字符串

  

  第一次是這樣處理的:

        ArrayList<ArrayList<Object>> a = new ArrayList<>();    // 打造這樣一個數據的集合 [[1,2],[2,3]] 要求 生成字符串 [1,2]#[2,3]
        for (int i = 0; i < 2; i++) { 
            ArrayList<Object> c = new ArrayList<>();
            c.add(i+1);
            c.add(i+2);
            a.add(c);
            //打印單個子集合的字符串形式數據
            Log.i("myinfo",c.toString());
        }
        StringBuilder builder = new StringBuilder();
        builder.append(a.get(0).toString()+"#"+a.get(1).toString());
        //打印該集合的字符串形式數據
        Log.i("myinfo",builder.toString());

  

  然后看該處理下的Log日志:

05-12 10:29:18.485 9565-9565/com.xxx.aaa I/myinfo: [1, 2]
05-12 10:29:18.485 9565-9565/com.xxx.aaa I/myinfo: [2, 3]
05-12 10:29:18.495 9565-9565/com.xxx.aaa I/myinfo: [1, 2]#[2, 3]

   我們會發現我們想要的是[1,2]#[2,3]形式的字符串,但是結果是[1, 2]#[2, 3]  ,在第二個值開始往后,前面都多了一個空格

  

   接下來我們查看 集合下的.toString()方法的源碼:

   翻譯一下官方解釋:

   1、返回這個Collection類(Set和List的父類) 的字符串表現形式

       2、這個表現形式有一個規定的格式,被矩形括號"[]"包含

       3、里面的子元素被“, ”(逗號和空格)分割 (這是重點)

    /**
     * Returns the string representation of this {@code Collection}. The presentation
     * has a specific format. It is enclosed by square brackets ("[]"). Elements
     * are separated by ', ' (comma and space).
     *
     * @return the string representation of this {@code Collection}.
     */
    @Override
    public String toString() {
        if (isEmpty()) {
            return "[]";
        }

        StringBuilder buffer = new StringBuilder(size() * 16);
        buffer.append('[');
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            Object next = it.next();
            if (next != this) {
                buffer.append(next);
            } else {
                buffer.append("(this Collection)");
            }
            if (it.hasNext()) {
                buffer.append(", ");
            }
        }
        buffer.append(']');
        return buffer.toString();
    }

   

  分析這個Collection下的.toString()方法源碼,分為幾個部分:

  1、判斷集合是不是空(empty),即集合內有沒有數據。如果是空值(沒有數據)的話,直接返回字符串 "[]"

      2、如果集合不是空值,說明有數據

    ①、迭代取下一個子元素(Object next = it.next()),如果這個子元素是集合本身,添加"(this Collection)"到StringBuffer類的buffer對象中

    ②、如果這個子元素不是集合本身,添加到buffer對象中

    ③、如果這個子元素下面還有子元素,則添加", "到buffer對象中去,用於分割兩個相鄰子元素

  3、返回StringBuffer.toString()字符串

  

  由此可見,返回[1, 2]#[2, 3]是官方正確的返回形式,那么對於這個問題,其實在改不了源碼的情況下 給得到的字符串后面使用.replaceAll(" ",""); 把字符串中的空格都去掉

  

  注意:源碼中有一段代碼:

            if (next != this) {
                buffer.append(next);
            } else {
                buffer.append("(this Collection)");
            }         

  這里可能有些同學看不懂,這里舉個例子,還是上面的那個,我們在子集合里面 添加代碼 c.add(c); 將集合本身添加到集合中去,看看打印結果

ArrayList<ArrayList<Object>> a = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            ArrayList<Object> c = new ArrayList<>();
            c.add(i+1);
            c.add(i+2);
            c.add(c);
            //打印單個子集合的字符串形式數據
            Log.i("myinfo",c.toString());
        }

看日志結果中紅色部分,是不是看懂了,如果集合中的子元素是集合本身,就將
"(this Collection)" 添加到返回集合中

05-12 10:58:00.615 8424-8424/com.maiji.magkarepatient I/myinfo: [1, 2, (this Collection)]
05-12 10:58:00.615 8424-8424/com.maiji.magkarepatient I/myinfo: [2, 3, (this Collection)]

  

  至此,上面這個問題解決了,下面我們看下其他類下的.toString()源碼。

  

---------------------------------------------------------------------------------------------------------------

 

一、Object

 /**
     * Returns a string containing a concise, human-readable description of this
     * object. Subclasses are encouraged to override this method and provide an
     * implementation that takes into account the object's type and data. The
     * default implementation is equivalent to the following expression:
     * <pre>
     *   getClass().getName() + '@' + Integer.toHexString(hashCode())</pre>
     * <p>See <a href="{@docRoot}reference/java/lang/Object.html#writing_toString">Writing a useful
     * {@code toString} method</a>
     * if you intend implementing your own {@code toString} method.
     *
     * @return a printable representation of this object.
     */
    public String toString() {
        return getClass().getName() + '@' + Integer.toHexString(hashCode());
    }

  翻譯一下官方解釋:

  1、返回一個對於這個Object 簡明的、可讀的 的字符串

  2、Object類的子類被鼓勵去重寫這個方法來提供一個實現用於描述對象的類型和數據

  3、默認的執行形式和下面這個例子一致

getClass().getName() + '@' + Integer.toHexString(hashCode())</pre>

  綜上:當你的一個類中沒有重寫.toString()方法的時候就會執行根類Object的這個.toString()方法。

     返回形式:對象的類名+@+哈希值的16進制

getClass().getName()返回對象所屬類的類名
hashCode()返回該對象的哈希值
Integer.toHexString(hashCode())將對象的哈希值用16進制表示

 舉例:

Object d = new Object();
Log.i("myinfo",d.toString());

05-12 11:23:00.758 17406-17406/com.maiji.magkarepatient I/myinfo: java.lang.Object@e23e786

 

二、String,StringBuilder,StringBuffer

  三個都是字符串的表現形式,但是有區別的

  ①、String.toString()  , 直接返回本身

    /**
     * Returns this string.
     */
    @Override
    public String toString() {
        return this;
    }

  ②、StringBuilder

   官方解釋:以字符串的形式 返回這個builder對象的內容

/**
     * Returns the contents of this builder.
     *
     * @return the string representation of the data in this builder.
     */
    @Override
    public String toString() {
        /* Note: This method is required to workaround a compiler bug
         * in the RI javac (at least in 1.5.0_06) that will generate a
         * reference to the non-public AbstractStringBuilder if we don't
         * override it here.
         */
        return super.toString();
    }

  追溯到super.toString()實現

 /**
     * Returns the current String representation.
     *
     * @return a String containing the characters in this instance.
     */
    @Override
    public String toString() {
        if (count == 0) {
            return "";
        }
        return StringFactory.newStringFromChars(0, count, value);
    }

  

  ③、StringBuffer

@Override
    public synchronized String toString() {
        return super.toString();
    }

  

  追溯到super.toString()

 /**
     * Returns the current String representation.
     *
     * @return a String containing the characters in this instance.
     */
    @Override
    public String toString() { if (count == 0) { return ""; } return StringFactory.newStringFromChars(0, count, value); }

    

  綜上我們發現,StringBuffer和StringBuilder最終都調用了父級  “AbstractStringBuilder” 中的toString()方法

  但是他們本身的toString()卻有所不同,我們由此可以總結

  1、StringBuilder:線程非安全的

     StringBuffer:線程安全的

      2、StringBuilder 處理速度要比 StringBudiler 快的多

  3、單線程大量數據操作,用StringBuilder  ,因為 StringBuilder速度快 , 因為單線程所以不考慮安全性

      多線程大量數據操作,用StringBuffer   ,  因為StringBuffer安全

 

三、Map

  先看源碼:

  可以看到返回的形式是{key1=value1, key2=value2}

  注意   1、當Map集合中沒有數據的時候 返回{}

     2、每兩個數據之前用", "分割,和Collection一致,一個逗號、一個空格

       3、當鍵值是集合本身的時候,添加  (this Map)

public String toString() {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (! i.hasNext())
            return "{}";

        StringBuilder sb = new StringBuilder();
        sb.append('{');
        for (;;) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key);
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value);
            if (! i.hasNext())
                return sb.append('}').toString();
            sb.append(',').append(' ');
        }
    }

  舉例:

       Map<String,String> map = new HashMap<>();
        map.put("keyA","valueA");
        map.put("keyB","valueB");
        map.put("keyC","valueC");
        Log.i("myinfo",map.toString());

打印結果:

05-12 11:41:30.898 4490-4490/com.maiji.magkarepatient I/myinfo: {keyA=valueA, keyB=valueB, keyC=valueC}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM