目录
  1. 1. 引言
  2. 2. idea 上运行
    1. 2.1. 执行代码
    2. 2.2. 执行结果
  3. 3. Android 上运行
    1. 3.1. 执行代码
    2. 3.2. 执行结果
  4. 4. 分析
    1. 4.1. 代码
  5. 5. 2019-10-12 更新
String.split("")在Android和Java中不同的执行结果

引言

之前朋友让我本地计算一个值的方法给他,然后我就把我的Android代码给他了,然后他在idea上直接跑了那段Java代码,然后计算的结果一直和App上计算的不匹配。一直觉得同样都是Java怎么可能会跑出不一样的结果,所以以为是参数的问题过了好久通过断点发现问题出现在一句

1
String.split("")

idea 上运行

执行代码

1
2
3
4
5
6
String testStr = "1234567";
String[] res = testStr.split("");

for (String str: res){
Log.v("split_res", str);
}

执行结果

1
2
3
4
5
6
7
split_res: 1
split_res: 2
split_res: 3
split_res: 4
split_res: 5
split_res: 6
split_res: 7

成功的将”1234567”分割成了[“1”, “2”, “3”, “4”, “5”, “6”, “7”]

Android 上运行

执行代码

1
2
3
4
5
6
String testStr = "1234567";
String[] res = testStr.split("");

for (String str: res){
System.out.println("split_res: " + str);
}

注意,如果使用Log输出的话,在msg参数加上一个默认的字符串,Log在msg为””的时候是不会输出内容的。
Log的输出代码

1
Log.v("TAG", "res" + str);

执行结果

1
2
3
4
5
6
7
8
System.out: split_res: 
System.out: split_res: 1
System.out: split_res: 2
System.out: split_res: 3
System.out: split_res: 4
System.out: split_res: 5
System.out: split_res: 6
System.out: split_res: 7

“1234567”分割成了[“”, “1”, “2”, “3”, “4”, “5”, “6”, “7”]
可以看到和idea上不一样的是Android上的执行结果多了一个 “” 空字符串

分析

本地JDK 环境为1.8, 两边代码相同,不同的地方的就是IDE不同,然后一个用的Android SDK一个用的JDK。
Android -> android-28\java\lang\String.java
Idea -> jdk1.8.0_144\src.zip!\java\lang\String.java
本来想着都是java开发的String.split还能不一样,结果一看源码,呵呵哒。

代码

下面是相关的核心代码

两边split(“”)本质都是跳转到split的一个重载方法

1
2
3
public String[] split(String regex) {
return split(regex, 0);
}

从这个重载方法开始两边就开始不一样的。不过的是,我们的regex = “”的条件,这个俩方法最终都只进了最后的

1
Pattern.compile(regex).split(this, limit);

Android端的代码

1
2
3
4
5
6
7
8
9
10
public String[] split(String regex, int limit) {
// 这个fastSplit 也就不看了。 里面限制了 regex.length > 0 所以不会进入 fastSqlit里面
// 对fastSplit关心的可以看看,主要是针对一些匹配正则做了处理
String[] fast = Pattern.fastSplit(regex, this, limit);
if (fast != null) {
return fast;
}
// END Android-changed: Replace custom fast-path with use of new Pattern.fastSplit method.
return Pattern.compile(regex).split(this, limit);
}

Java端的代码

1
2
3
4
5
6
7
8
9
10
11
public String[] split(String regex, int limit) {
char ch = 0;
if (一堆判断)
{
.....
这里和fastSplit一样的作用加快特殊正则的处理
因为条件不会进到这里所以这里的代码就不贴出来了
.....
}
return Pattern.compile(regex).split(this, limit);
}

Pattern中的split,对比了两边的代码发现JDK1.8里面多了一个判断条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 public String[] split(CharSequence input, int limit) {
...
// 前面也是一样的 省略了
// Add segments before each match found
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
// 就是这个判断Android SDK里面是没有的。 就是因为这个判断。
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
// 一致 省略
}
}

// 剩下的两边都一样的就省略了
}

核心区分逻辑

1
2
3
4
5
6
// 就是这个判断Android SDK里面是没有的。 就是因为这个判断。
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;
}

这里把首位的””给过滤了, 而Android_SDK中是没有这段逻辑的所以就导致了Android中执行split(“”)会多出一个””了

2019-10-12 更新

今天刚好有后台的同学要算一个东西直接把线上的代码考下来的,然后发现怎么执行结果都不一样,后来发现和我之前用的是同一个类,所以问题也就同样处在了split(“”)这个的问题上。但是线上是正常的很奇怪,后来发现线上的服务器的JDK环境是1.7的,又去翻了一下1.7的String.split方法发现最终的Pattern.split和Android一样的没有index==0的逻辑的。

文章作者: sovwcwsfm
文章链接: http://sovwcwsfm.com/blog/page/split.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 _L