混淆来自String.split的输出
我不明白这个代码的输出:
public class StringDemo{ public static void main(String args[]) { String blank = ""; String comma = ","; System.out.println("Output1: "+blank.split(",").length); System.out.println("Output2: "+comma.split(",").length); } } 并得到以下输出:
 Output1: 1 Output2: 0 
	
文档:
 对于: System.out.println("Output1: "+blank.split(",").length); 
此方法返回的数组包含此string的每个子string,该string由与给定expression式匹配的另一个子string终止,或者由string的末尾终止。 数组中的子string按照它们在此string中出现的顺序排列。 如果expression式不匹配input的任何部分,那么结果数组只有一个元素,即这个string 。
它将简单地返回整个string,这就是为什么它返回1。
 对于第二种情况, String.split将放弃,所以结果将是空的。 
 String.split silently discards trailing separators 
请参阅番石榴StringsExplained也
一切都按计划进行,但我们一步一步做(我希望你有一些时间)。
 根据split(String regex)方法的文档 (和源代码 ): 
此方法的工作原理与通过调用给定expression式和极限参数为零的双参数拆分方法一样。
所以当你调用
 split(String regex) 
 你实际上是从split(String regex, int limit)方法调用的方式: 
 split(regex, 0) 
 所以这里limit设置为0 。 
你需要知道关于这个参数的一些事情:
-  如果limit是肯定的,你将结果数组的长度限制为你指定的正数,所以"axaxaxaxa".split("x",2)将返回一个数组["a", "axaxaxa"],而不是["a","a","a","a","a"]。
- 
如果 limit是0那么你不限制结果数组的长度。 但是这也意味着任何尾随的空string都将被删除。 例如:"fooXbarX".split("X")将在开始时生成一个数组,如下所示: ["foo", "bar", ""]( "barX"在"X"上分割会生成"bar"和""),但是由于split会删除所有尾随的空string,它将返回["foo", "bar"]
- 
limit的负值的行为类似于限制设置为0行为(它不会限制结果数组的长度)。 唯一的区别是它不会从结果数组的末尾删除空string。 换一种说法"fooXbarX".split("X",-1)
 将返回["foo", "bar", ""] 
让我们来看看这个案子,
 ",".split(",").length 
(如前所述)与之相同
 ",".split(",", 0).length 
 这意味着我们正在使用一个不会限制结果数组长度的拆分版本,但会删除所有尾随的空string "" 。 你需要明白,当我们分裂一件事时,我们总是得到两件事情。 
 换句话说,如果我们拆分"abc"来代替b ,我们会得到"a"和"c" 。 
 棘手的部分是要明白,如果我们在c分割"abc" ,我们将得到"ab"和"" (空string)。 
 使用这个逻辑,如果我们将","分开,我们将得到""和"" (两个空string)。 
 您可以使用负极限split来检查它: 
 for (String s: ",".split(",", -1)){ System.out.println("\""+s+"\""); } 
将打印
 "" "" 
 所以我们看到结果数组在这里首先是["", ""] 。 
 但是由于默认情况下我们使用limit设置为0 ,所有尾随的空string将被删除。 在这种情况下,结果数组只包含尾随的空string ,因此所有这些都将被删除 ,留下长度为0空数组[] 。 
用来回答这个问题
 "".split(",").length 
 你需要明白, 只有当这样的尾随空string是分裂结果的时候 , 删除尾随的空string才有意义(而且很可能不需要) 。 
 所以如果没有可以拆分的地方,就不可能创build空的string,所以运行这个“清理”过程没有任何意义。 
 在split(String regex, int limit)方法的文档中提到了这个信息,你可以阅读: 
如果expression式不匹配input的任何部分,那么结果数组只有一个元素,即这个string 。
你也可以在这个方法的源代码中看到这个行为(来自Java 8):
  2316 public String [] split( String regex, int limit){ 
  2317 / *快速path,如果正则expression式是 
  2318 (1)单string和这个字符不是其中之一 
  2319 RegEx的元字符“。$ |()[{^?* + \\”,或者 
  2320 (2)两个string和第一个字符是反斜杠和 
  2321第二个不是ascii数字或ascii字母。 
  2322 * / 
  2323 char ch = 0; 
  2324 if (((regex.value.length == 1 && 
  2325 “。$ |()[{^?* + \\”。  indexOf (ch = regex。charAt (0))== -1)|| 
  2326 (正则expression式。length ()== 2 && 
  2327正则expression式。  charAt (0)=='\\'&& 
  2328 (((ch = regex。charAt (1)) - '0')|('9'-ch))<0 && 
  2329 ((ch-'a')|('z'-ch))<0 && 
  2330 ((ch -'A')|('Z'-ch))<0))&& 
  2331 (ch <Character.MIN_HIGH_SURROGATE || 
  2332 ch> Character.MAX_LOW_SURROGATE)) 
  2333 { 
  2334 int off = 0; 
  2335 int next = 0; 
  2336 布尔限制=限制> 0; 
  2337 ArrayList < String > list = new ArrayList <>(); 
  ((next = indexOf (ch,off))!= -1){ 
  2339 if (!limited || list。size ()<limit  -  1){ 
  2340名单。  add ( substring (off,next)); 
  2341 off = next + 1; 
  2342 } 其他 {//最后一个 
  2343 // assert(list.size()== limit  -  1); 
  2344名单。  add ( substring (off,value.length)); 
  2345 off = value.length; 
  2346 打破 ; 
  2347 } 
  2348 } 
  2349 //如果找不到匹配, 请将其返回 
  2350 if (off == 0) 
  2351 return new String [] { this }; 
  2353 //添加剩余的段 
  2354 if (!limited || list。size ()<limit) 
  2355名单。  add ( substring (off,value.length)); 
  2357 //构造结果 
  2358 int resultSize = list。  size (); 
  2359 if (limit == 0){ 
  2360 while (resultSize> 0 && list。get(resultSize  -  1)。length()== 0){ 
  2361结果大小 - ; 
  2362 } 
  2363 } 
  2364 String [] result = new String [resultSize]; 
  2365 退货清单。  subList (0,resultSize)。  toArray (result); 
  2366 } 
  2367 返回模式。  编译 (正则expression式)。  分裂 ( 这个 ,限制); 
  2368 } 
在那里你可以find
 if (off == 0) return new String[]{this}; 
这意味着片段
-   if (off == 0)– 如果off(从哪个方法开始search下一个可能匹配的正则expression式作为splitparameter passing的位置)在迭代整个string后仍然为0,我们没有find任何匹配,所以string是不分裂
-  return new String[]{this};– 在这种情况下,让我们只返回一个原始string的数组(this表示)。
 由于","甚至一次都不能在""find, "".split(",")必须返回一个包含一个元素(调用split空string"".split(",")的数组。 这意味着这个数组的长度是1 。 
  BTW。  Java 8引入了另一种机制。 如果我们使用零长度正则expression式 (比如""或环视(?<!x) )分割,它将删除前导空string(如果它们是在分割过程中创build的 )。 更多信息: 为什么在Java 8中,split有时会在结果数组开始时删除空string? 
从Java 1.7文档
将string拆分为给定正则expression式的匹配。
split()方法的工作原理就好像通过调用具有给定expression式和极限参数为零的双参数拆分方法。 尾随的空string因此不包括在结果数组中。
 在情况1中, blank.split(",") does not match any part of the input then the resulting array has just one element, namely this String. 
 It will return entire String. 所以,长度将是1 。 
 在情况2中, comma.split(",") will return empty. 
  split()期待一个正则expression式作为参数,返回结果数组来匹配那个正则expression式。 
 所以长度是0 
例如 ( 文档 )
string“boo:and:foo”用这些expression式产生以下结果:
 Regex Result : { "boo", "and", "foo" } o { "b", "", ":and:f" } 
参数: regex – 分隔正则expression式
返回:通过将该string分割为给定正则expression式的匹配而计算的string数组
抛出: PatternSyntaxException – 如果正则expression式的语法无效
 从String类的javadoc中为public String[] split (String regex)方法: 
将该string拆分为给定正则expression式的匹配。
此方法的工作原理与通过调用给定expression式和极限参数为零的双参数拆分方法一样。 尾随的空string因此不包括在结果数组中。
在第一种情况下,expression式不匹配input的任何部分,所以我们得到一个只有一个元素的数组 – input。
在第二种情况下,匹配input和split的expression式应该返回两个空string; 但根据javadoc,他们被丢弃(因为他们是尾随和空的)。
 我们可以看看String.split后面的java.util.regex.Pattern的源代码 。 一路顺着兔子洞的方法 
 public String[] split(CharSequence input, int limit) 
被调用。
 input"" 
 对于input""这个方法被称为 
 String[] parts = split("", 0); 
这种方法的中间部分是 :
  int index = 0; boolean matchLimited = limit > 0; ArrayList<String> matchList = new ArrayList<>(); Matcher m = matcher(input); while(m.find()) { // Tichodroma: this will not happen for our input } // If no match was found, return this if (index == 0) return new String[] {input.toString()}; 
 这就是发生的事情:返回new String[] {input.toString()} 。 
 input"," 
 对于“input","input"," 部分是 
  // 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); 
 这里的resultSize == 0和limit == 0所以返回new String[0] 。 
从JDK 1.7
  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; if (((regex.count == 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; boolean limited = limit > 0; ArrayList<String> list = new ArrayList<>(); while ((next = indexOf(ch, off)) != -1) { if (!limited || list.size() < limit - 1) { list.add(substring(off, next)); off = next + 1; } else { // last one //assert (list.size() == limit - 1); list.add(substring(off, count)); off = count; break; } } // If no match was found, return this if (off == 0) return new String[] { this }; // Add remaining segment if (!limited || list.size() < limit) list.add(substring(off, count)); // Construct result int resultSize = list.size(); if (limit == 0) while (resultSize > 0 && list.get(resultSize-1).length() == 0) resultSize--; String[] result = new String[resultSize]; return list.subList(0, resultSize).toArray(result); } return Pattern.compile(regex).split(this, limit); } 
 所以对于这种情况,正则expression式将由第一个if处理。 
 对于第一种情况, blank.split(",") 
 // If no match was found, return this if (off == 0) return new String[] { this }; 
所以,如果没有匹配,这个函数将返回一个包含一个元素的数组。
 对于第二种情况comma.split(",") 
 List<String> list = new ArrayList<>(); //... int resultSize = list.size(); if (limit == 0) while (resultSize > 0 && list.get(resultSize-1).length() == 0) resultSize--; String[] result = new String[resultSize]; return list.subList(0, resultSize).toArray(result); 
 正如你注意到的,最后一个while循环已经移除了列表末尾的所有空元素,所以resultSize是0 。 
 String blank = ""; String comma = ","; System.out.println("Output1: "+blank.split(",").length); // case 1 System.out.println("Output2: "+comma.split(",").length); // case 2 
 情况1  – 这里blank.split(",")将返回""因为没有,在blank你得到相同的,所以长度将是1 
 情况2-这里comma.split(",")将返回空数组,你必须scape ,如果你想计数长度为1 comma ,否则长度将为0 
 再次comma.split(",")拆分()期待一个regex作为参数它将返回结果数组匹配与该regex 。 
此方法返回的数组包含此string的每个子string,该string由与给定expression式匹配的另一个子string终止,或者由string的末尾终止。
其他
如果expression式不匹配input的任何部分,那么结果数组只有一个元素,即这个string。
split方法的API声明:“如果expression式不匹配input的任何部分,那么结果数组只有一个元素,即这个string。
因此,由于String空白不包含“,”,所以返回一个带有一个元素(即空白本身)的String []。
对于string逗号,原始string的“无”是空的,因此返回一个空数组。
这似乎是最好的解决scheme,如果你想处理返回的结果,例如
 String[] splits = aString.split(","); for(String split: splits) { // do something }