之前對于string.spilt()的用法沒有做過仔細(xì)研究,線上的項目已經(jīng)上線很久,查看error日志,發(fā)現(xiàn)了ArrayIndexOutOfBoundsException(數(shù)組越界).百思不得其解,追蹤相應(yīng)代碼也沒有發(fā)現(xiàn)有明顯異常,spilt()用法看著沒有明顯錯誤,所有的報文解析中,在一個小時內(nèi)只出現(xiàn)了這么兩筆訂單查詢報錯.很是詭異.于是拿著訂單號,追蹤全部日志.
根據(jù)正常和異常返回日志,進行現(xiàn)場復(fù)原.debug時,發(fā)現(xiàn)了異常.
自己寫了一個小demo復(fù)原:
字符串中包含編程語言信息,其中每個語言通過"|",進行分割.形如"java|C|C#|C++|Python"
,spilt數(shù)組應(yīng)該有5個對象.我們對字符串進行稍加改動,形如java|C||C#|C++|Python
,java|C||C#|C++|Python||
,spitl在對字符串進行分割的時候就有些許不同了.
String str0 = "java|C|C#|C++|Python";
String str1 = "java|C||C#|C++|Python";
String str2 = "java|C||C#|C++|Python||";
String[] lans1 = str0.split("\\|");
String[] lans2 = str1.split("\\|");
String[] lans3 = str2.split("\\|");
System.out.println("lans1.length: "+lans1.length);
System.out.println("lans2.length: "+lans2.length);
System.out.println("lans3.length: "+lans3.length);
System.out.println("Spilt的坑");
我們預(yù)估lans1.length = 5;lans2.length =6;lans3.length =8
可以看到實際輸出并不是如我們預(yù)計.為什么呢?半路出家學(xué)java,這個地方咋整呢,ctrl + click 看下源碼吧.
public String[] split(String regex) {
return split(regex, 0); //這個地方的"0",作為默認(rèn)參數(shù)傳入,此事必有蹊蹺.
}
regex不用解釋,就是進行分割的正則表達.
我們深入public String[] split(String regex, int limit)
看下第二個參數(shù)有什么作用.
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.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;
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, value.length));
off = value.length;
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, value.length));
// 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);
}
先不看if條件語句,我們的重點是判斷一下第二參數(shù)的作用,(if條件判斷很復(fù)雜,沒看懂呀)
結(jié)論就是:
- imit > 0 ,模式最多使用n-1次撑瞧,數(shù)組長度不會大于n棵譬,而且數(shù)組的最后一項將包含所有超出最后匹配的定界符的輸入。
- limit <=0, 模式會使用盡可能多的次數(shù)预伺,而且數(shù)組是任意長度订咸。如果limit = 0,那么最后的空字符串會被丟棄
建議使用Guava中的Spilter中的方法扭屁。
List<String> params = Splitter.on("|").splitToList(response);