Java对于字符串split函数的源码笔记
之前对字符串的split函数都是一知半解,今天好好看了看源代码,对之前理解不到位的地方做一些笔记,加深下对split函数的理解
函数的定义
一般调用的是只有一个参数的
第一个参数为正则表达式,要分割的
第二个参数为限制的最大个数,limit小于等于0的话就是不限制
有一个实参的函数内部会调用有2个实参的split方法
public String[] split(String regex, int limit)
public String[] split(String regex)
函数的实现
首先上源码,源码其实不是很长,但是开始的if判断确实把我下了一跳.
代码开始还有注释
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.
我的翻译: 正则表达式只要满足下面其中一种情况使用就fastpath
// 也就是只有一个字符或者2个字符,但第一个字符是转移字符的话,就启用快速分割模式
(1): 如果只有一个字符并且这个字符不是正则表达式使用的超文本符号(meta characters)
(2): 如果是两个字符的字符串,并且第一个字符是一个反斜线,第二个不是一个ascii的大小写字母或者符号
否者使用正则表达式提供的split函数
*/
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))
{
// offset 偏移量
int off = 0;
// 下一个地址
int next = 0;
// 如果大于0,则限制,否者不限制
boolean limited = limit > 0;
// 找下一个ch
// 创建一个list来存放保存的结果
ArrayList<String> list = new ArrayList<>();
while ((next = indexOf(ch, off)) != -1) {
// 如果不限制长度,或者已经放入的长度小于要存放的长度 -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;
}
}
// 如果off为0,说明没有匹配到,那么把传入的字符串原原本本的放到一个字符串数组中返回
// 注意是原原本本哦
// 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
// 如果不限制长度,从后往前,把找到的元素遍历一遍
//
// 去掉结尾元素是空的元素(长度为0的)
// 但是无法去掉中间元素是空的!
// 注意注意!
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);
}
// 不满足如上条件,这使用正则表达式自带的split函数
return Pattern.compile(regex).split(this, limit);
}
if 在干嘛?
我们把if单独拆开来看,看这个看了半天,源码真的太强了
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
)
)
&&
// 判断不是unicode的高代理和低代理
( ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE
)
)
满足下面其中一种情况使用就fastpath
(1): 如果正则表达式只有一个字符并且这个字符不是正则表达式使用的超文本符号(meta characters)
(2): 如果正则表达式是两个字符的字符串,并且第一个字符是一个反斜线,第二个字符不是一个ascii的数字或者符号
其中比较有意思的就是判断是否是数字或者字符用了位运算,这优化
感觉这个运算原理如下
如果这个字符是数字或者大小写字母的话,那么这样一个操作下来,肯定有一项不会产生负数
这样条件就不成立,如果这个字符不是数字或者大小写字母的话,都是负数,条件满足