split 陷阱分析

  1.   System.out.println(":ab:cd:ef::".split(":").length);//末尾分隔符全部忽略    
  2.   System.out.println(":ab:cd:ef::".split(":",-1).length);//不忽略任何一個分隔符    
  3.   System.out.println(StringUtils.split(":ab:cd:ef::",":").length);//最前面的和末尾的分隔符全部都忽略,apache commons    
  4.   System.out.println(StringUtils.splitPreserveAllTokens(":ab:cd:ef::",":").length);//不忽略任何一個分隔符 apache commons     
  5. 輸出:    
  6. 4    
  7. 6    
  8. 3    
  9. 6    


看了下jdk裏String類的public String[] split(String regex,int limit)方法,感覺平時不太會用這方法,以爲在用正則表達式來拆分時候,如果匹配到的字符是最後一個字符時,會拆分出兩個空字符串,例如"o"split("o",5) or "o"split("o",-2)時候 結果是"" "" 也就是下圖中紅框裏的內容,所以平時一般都用split(String regex) 方法,其實也就等同於split(String regex,0)方法,把結尾的空字符串丟棄! 




String的split方法用到的參數是一個正則式,雖然強大,但是有時候容易出錯。而且string並沒有提供簡化版本。org.apache.commons.lang.StringUtils提供的split改變了這一狀況,開始使用完整的字符串作爲參數,而不是regex。同時,對類似功能的jdk版本的StringTokenizer,在內部方法splitWorker中有段註釋:Direct code is quicker than StringTokenizer.也就是說,這個是更快的一個工具了~~ 

StringUtils裏的split和splitPreserveAllTokens 底層都是調用splitWorker方法實現的 
下面分別來理解下兩個私有的splitWorker方法:
 

Java代碼  收藏代碼
  1. private static String[] splitWorker(String str, char separatorChar, boolean preserveAllTokens)  
  2. {  
  3.         // Performance tuned for 2.0 (JDK1.4)  
  4.   
  5.         if (str == null) {  
  6.             return null;  
  7.         }  
  8.         int len = str.length();  
  9.         if (len == 0) {  
  10.             return ArrayUtils.EMPTY_STRING_ARRAY;  
  11.         }  
  12.         List list = new ArrayList();  
  13.         int i = 0, start = 0;  
  14.         boolean match = false;  
  15.         boolean lastMatch = false;  
  16.         while (i < len) {  
  17.             if (str.charAt(i) == separatorChar) {  
  18.                 if (match || preserveAllTokens) {  
  19.                     list.add(str.substring(start, i));  
  20.                     match = false;  
  21.                     lastMatch = true;  
  22.                 }  
  23.                 start = ++i;  
  24.                 continue;  
  25.             }  
  26.             lastMatch = false;  
  27.             match = true;  
  28.             i++;  
  29.         }  
  30.         if (match || (preserveAllTokens && lastMatch)) {  
  31.             list.add(str.substring(start, i));  
  32.         }  
  33.         return (String[]) list.toArray(new String[list.size()]);  
  34.     }  
是一個核心方法,用於拆分字符串,其中字符c表示分隔符,另外布爾變量b表示c在首尾的不同處理方式。爲真,則在首位留一個""的字符串。但是在中間是沒有作用的。該方法執行如下操作: 
  如果字符串爲null,則返回null。 
  如果字符串爲"",則返回""。 
  用i作爲指針遍歷字符串,match和lastMatch分別表示遇到和最後遇到可分割的內容。 
    如果字符串中第一個就遇到c,則看b的值,如果爲真,則會在結果數組中存入一個""。如果沒遇到,match置真,lastMatch置假,表示有要分割的內容。 
    一旦遇到c,則在結果數組中輸出字符串在i之前的子字符串,並把起始點調整到i之後。且match置假,lastMatch置真。 
  遍歷結束,如果match爲真(到最後也沒有遇到c),或者lastMatch和b同爲真(最後一個字符是c),則輸出最後的部分(如果是後者,則會輸出一個"")。
 


Java代碼  收藏代碼
  1. private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens)  
  2. {  
  3.         // Performance tuned for 2.0 (JDK1.4)  
  4.         // Direct code is quicker than StringTokenizer.  
  5.         // Also, StringTokenizer uses isSpace() not isWhitespace()  
  6.   
  7.         if (str == null) {  
  8.             return null;  
  9.         }  
  10.         int len = str.length();  
  11.         if (len == 0) {  
  12.             return ArrayUtils.EMPTY_STRING_ARRAY;  
  13.         }  
  14.         List list = new ArrayList();  
  15.         int sizePlus1 = 1;  
  16.         int i = 0, start = 0;  
  17.         boolean match = false;  
  18.         boolean lastMatch = false;  
  19.         if (separatorChars == null) {  
  20.             // Null separator means use whitespace  
  21.             while (i < len) {  
  22.                 if (Character.isWhitespace(str.charAt(i))) {  
  23.                     if (match || preserveAllTokens) {  
  24.                         lastMatch = true;  
  25.                         if (sizePlus1++ == max) {  
  26.                             i = len;  
  27.                             lastMatch = false;  
  28.                         }  
  29.                         list.add(str.substring(start, i));  
  30.                         match = false;  
  31.                     }  
  32.                     start = ++i;  
  33.                     continue;  
  34.                 }  
  35.                 lastMatch = false;  
  36.                 match = true;  
  37.                 i++;  
  38.             }  
  39.         } else if (separatorChars.length() == 1) {  
  40.             // Optimise 1 character case  
  41.             char sep = separatorChars.charAt(0);  
  42.             while (i < len) {  
  43.                 if (str.charAt(i) == sep) {  
  44.                     if (match || preserveAllTokens) {  
  45.                         lastMatch = true;  
  46.                         if (sizePlus1++ == max) {  
  47.                             i = len;  
  48.                             lastMatch = false;  
  49.                         }  
  50.                         list.add(str.substring(start, i));  
  51.                         match = false;  
  52.                     }  
  53.                     start = ++i;  
  54.                     continue;  
  55.                 }  
  56.                 lastMatch = false;  
  57.                 match = true;  
  58.                 i++;  
  59.             }  
  60.         } else {  
  61.             // standard case  
  62.             while (i < len) {  
  63.                 if (separatorChars.indexOf(str.charAt(i)) >= 0) {  
  64.                     if (match || preserveAllTokens) {  
  65.                         lastMatch = true;  
  66.                         if (sizePlus1++ == max) {  
  67.                             i = len;  
  68.                             lastMatch = false;  
  69.                         }  
  70.                         list.add(str.substring(start, i));  
  71.                         match = false;  
  72.                     }  
  73.                     start = ++i;  
  74.                     continue;  
  75.                 }  
  76.                 lastMatch = false;  
  77.                 match = true;  
  78.                 i++;  
  79.             }  
  80.         }  
  81.         if (match || (preserveAllTokens && lastMatch)) {  
  82.             list.add(str.substring(start, i));  
  83.         }  
  84.         return (String[]) list.toArray(new String[list.size()]);  
  85.     }  
也是一個核心方法,用於拆分字符串,其與上一個方法的不同之處在於其分隔符用字符串表示一組字符,且增加一個max變量,表示輸出的字符串數組的最大長度。另外注意該方法的b如果爲真,會在首尾及中間起作用,且如果分隔符字符串長度大於1,則數組中的""會更多(根據分隔符字符的數量)。該方法執行如下操作: 
  如果字符串爲null,則返回null。 
  如果字符串爲"",則返回""。 
  之後的處理分三種情況,分別是分隔符字符串爲null,則默認爲" ";分割符字符串長度爲1;分割符字符串爲普通字符串。這三種處理的不同只是在當前遍歷中的字符的判斷問題。 
    1.利用Character.isWhitespace方法判斷每個字符是否爲" "。 
    2.先把字符串轉化爲一個char,然後就和前一個splitWorker方法類似。 
    3.利用indexOf方法查找當前字符是否在分隔符字符串中,然後就和前一個splitWorker方法類似。 
    需要注意的是,如果輸出的數組的數量已經等於max的值,則把指針直接挪到最後,等待下次遍歷的時候直接跳出。同時由於lastMatch和match都置爲假,最後也不會輸出""了。 
   遍歷結束,如果match爲真(到最後也沒有遇到c),或者lastMatch和b同爲真(最後一個字符在分隔符字符串中),則輸出最後的部分(如果是後者,則會輸出一個"")。
 
轉載 自:http://yinny.iteye.com/blog/1750210
發佈了42 篇原創文章 · 獲贊 6 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章