1. 程式人生 > >Java split()方法簡析

Java split()方法簡析

厚顏打廣告,博主個人部落格地址傳送門

split方法的分類

關於Java中的split方法,這裡大致分為三種:
假定字串String = “1,2,,,,,”,使用不同的split方法的話,其效果如下面所示;

js中的split方法

使用的方法:

var string = “1,2,,,,,”;
var arr = [];
arr = value.split(",");
alert(arr.length);

可以得到此時的arr陣列的長度為6,也就是說,後面的那些逗號中間的空字串,並沒有trim掉;

Java中的使用方法

split(String regex)

String offerCodes = “1,2,,,,,”; 
String[] offerCodeString = offerCodes.split(",");
System.out.println("offerCodeString.length"+offerCodeString.length);

控制檯打印出來的陣列長度為2;
可以看到,單引數,按照指定字元進行分割的話,後面的那些空字串都預設去掉了,那如果我想要後面的這些空字串怎麼辦?看下面的這種split方法;

split(Sting regex,int limit)

 String offerCodes = “1,2,,,,,”; 
 String[] offerCodeString = offerCodes.split(",",-1);
 System.out.println("offerCodeString.length"+offerCodeString.length);

控制檯打印出來的陣列長度為6;
也就是說沒有把後面的空字串去掉,但是為什麼後面的引數填個-1呢?能不能換成其他的值?那這麼想的話就有必要走一波原始碼了;

Java jdk1.8 split原始碼簡析

原始碼註釋

split原始碼
string字串的split方法從JDK1.4中開始存在,來一波原始碼裡面的表格:
原始碼中給出了示例字串:“boo:and:foo”;然後,傳入不同的引數,得到的結果如下:

Regex Limit Result
: 2 "boo", "and:foo"
: 5 "boo", "and", "foo"
: -2 "boo", "and", "foo"
o 5 "b", "", ":and:f", "", ""
o -2 "b", "", ":and:f", "", ""
o 0 "b", "", ":and:f"

split原始碼

public String[] split(String regex, int limit) {
      /* fastpath if the regex is a
       (1)one-char String and this character is not one of the
          RegEx's meta characters ".$|()[{^?*+\\", or
       (2)two-char String and the first char is the backslash and
          the second is not the ascii digit or ascii letter.
       */
      char ch = 0;
      //這裡是一堆的正則校驗,大致是,傳入的分割符是單符號位的,才進行下面的分割,否則,return Pattern.compile(regex).split(this, limit)呼叫另一個分割方法進行字串分割位分割,文末會PO出此方法
      if (((regex.value.length == 1 &&
           ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
           (regex.length() == 2 &&
            regex.charAt(0) == '\\' &&
            (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
            ((ch-'a')|('z'-ch)) < 0 &&
            ((ch-'A')|('Z'-ch)) < 0)) &&
          (ch < Character.MIN_HIGH_SURROGATE ||
           ch > Character.MAX_LOW_SURROGATE))
      {
          int off = 0;
          int next = 0;
          //從這裡開始,進行limit值的入參及split邏輯
          //當傳進來的值是正數的時候,limit > 0 == true
          boolean limited = limit > 0;
          //宣告一個list集合對返回值結果進行儲存,用於最後給String[]賦值
          ArrayList<String> list = new ArrayList<>();
          //當沒有按照指定的字元分割到最後一位的時候,執行while迴圈進行判斷,然後使用substring(off, next)方法進行分割
          while ((next = indexOf(ch, off)) != -1) {
          //判斷limited 為FALSE,即limit<0,或者,list.size() < limit - 1是否成立
              if (!limited || list.size() < limit - 1) {
                  //若成立則使用substring(off, next)方法進行分割,並且加入到list中
                  list.add(substring(off, next));
                  //此時的初始識別符號off為next+1
                  off = next + 1;
              } else {    // last one
                  //assert (list.size() == limit - 1);
                  //不成立的話呼叫substring(off, value.length),此時value.length值為1
                  list.add(substring(off, value.length));
                  off = value.length;
                  break;
              }
          }
          // 如果不符合,則返回 this
          if (off == 0)
              return new String[]{this};

          // Add remaining segment
          if (!limited || list.size() < limit)
              list.add(substring(off, value.length));

          // Construct result
          int resultSize = list.size();
          if (limit == 0) {
              while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                  resultSize--;
              }
          }
          //將所得到的list集合進行擷取,使用toArray()方法賦值到String[] result中,所以這麼看來,split方法的效率,是略差的
          String[] result = new String[resultSize];
          return list.subList(0, resultSize).toArray(result);
      }
      return Pattern.compile(regex).split(this, limit);
  } 

總的來說:
limit 引數控制模式應用的次數,因此影響所得陣列的長度。如果該限制 n 大於 0,則模式將被最多應用 n - 1 次,陣列的長度將不會大於 n ,而且陣列的最後一項將包含所有超出最後匹配的定界符的輸入。如果 n 為非正,那麼模式將被應用盡可能多的次數,而且陣列可以是任何長度。如果 n 為 0,那麼模式將被應用盡可能多的次數,陣列可以是任何長度,並且結尾空字串將被丟棄。
下面的原始碼就不解析啦,有興趣的看官可以自行檢視:

public String[] split(CharSequence input, int limit) {
      int index = 0;
      boolean matchLimited = limit > 0;
      ArrayList<String> matchList = new ArrayList<>();
      Matcher m = matcher(input);

      // Add segments before each match found
      while(m.find()) {
          if (!matchLimited || matchList.size() < limit - 1) {
              if (index == 0 && index == m.start() && m.start() == m.end()) {
                  // no empty leading substring included for zero-width match
                  // at the beginning of the input char sequence.
                  continue;
              }
              String match = input.subSequence(index, m.start()).toString();
              matchList.add(match);
              index = m.end();
          } else if (matchList.size() == limit - 1) { // last one
              String match = input.subSequence(index,
                                               input.length()).toString();
              matchList.add(match);
              index = m.end();
          }
      }

      // If no match was found, return this
      if (index == 0)
          return new String[] {input.toString()};

      // Add remaining segment
      if (!matchLimited || matchList.size() < limit)
          matchList.add(input.subSequence(index, input.length()).toString());

      // Construct result
      int resultSize = matchList.size();
      if (limit == 0)
          while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
              resultSize--;
      String[] result = new String[resultSize];
      return matchList.subList(0, resultSize).toArray(result);
  }