最近生产环境遇到一个奇怪的数组下标越界报错,如下图代码所示,咱们能够必定的是 fieldName 变量不为空(不是空字符串,也不是 null),然而代码执行到读取 names[0] 变量的时候,抛出了一个 数组下标越界java.lang.ArrayIndexOutOfBoundsException) 的异样。

异样信息如下图所示

问题很简略,咱们对一个字符串执行 split 办法之后,以过往其它编程语言(Go、PHP、Javascript、Dart 等)的应用教训来看,即便字符串为空,即便没有匹配到分隔符,在返回值数组中也会蕴含一个以后字符串的值。然而这里却抛出了 ArrayIndexOutOfBoundsException,难道 split 办法的返回值可能为空数组?

最终通过排查发现,在上述代码段中,当 fieldName 的值为 "~" 的时候,咱们拜访 names[0] 就会抛出 ArrayIndexOutOfBoundsException,为什么会这样呢?

本文将会继续修改和更新,最新内容请参考我的 GITHUB 上的 程序猿成长打算 我的项目,欢送 Star,更多精彩内容请 follow me。

问题

在 Java 中,如果执行上面这段代码,直觉上你认为会输入什么?

String str = "~";String []arr = str.split("~");System.out.println(arr.length);

如果你有其余编程语言的教训,可能直觉上会感觉这里输入的应该是 2,然而遗憾的是,这里输入的是 0,变量 arr 是个空数组。

这里不禁狐疑本人之前的记忆是不是有偏差,于是我又应用其它语言来尝试复现这个问题。

不同语言中 split 的行为

我总结了一个表格,阐明了不必语言不同的行为,这里比照的是执行 split 函数/办法后返回数组的长度:

语言\函数"".split("")"~".split("~")"~~".split("~")"".split("~")"~123".split("~")
Javascript02312
PHP02312
Dart02312
Golang02312
Scala10012
Java10012

Javascript

首先是 Javascript,在浏览器的管制台上间接执行,失去了上面的后果

"".split("")"~".split("~")"~~".split("~")"".split("~")"~123".split("~")

执行后果

跟我的直觉是统一的,同样的状况,这里返回的是 2

PHP

在 PHP 中,我应用了 mb_split 函数,该函数用于对多字节字符串进行宰割

执行后果如下

执行后果跟我的直觉也是统一的,同样的状况,这里返回的是 2

Dart

而后是 Google 的 Dart,这是一门次要用于应用 Flutter 来开发跨平台利用的编程语言,代码如下

void main() {    print("".split('').length); // 0    print("~".split('~').length); // 2    print("~~".split('~').length); // 3    print("".split('~').length); // 1    print("~123".split('~').length); // 2}

执行后果

同样,"~".split("~") 也是返回了两个值。

Golang

在 Golang 中,执行后果仍旧是合乎直觉的,返回的是 2

package mainimport(    "strings"    "fmt")func main() {    printStrs(strings.Split("", "")) // 0 []    printStrs(strings.Split("~", "~")) // 2 ["", "", ]    printStrs(strings.Split("~~", "~")) // 3 ["", "", "", ]    printStrs(strings.Split("", "~")) // 1 ["", ]    printStrs(strings.Split("~123", "~")) // 2 ["", "123", ]}func printStrs(s []string) {    fmt.Print(len(s), " [")    for _, item := range s {        fmt.Printf(`"%s", `, item)    }    fmt.Print("]\n")}

执行后果

Scala

而后,我又尝试了 Scala,发现在 Scala 中, split 的行为有些不一样了。

"".split("").length"~".split("~").length"~~".split("~").length"".split("~").length"~123".split("~").length

代码 "~".split("~") 返回的是 空数组,与在 Java 中咱们遇到的问题一模一样。

Java

最初,我又用 Java 执行了同样的代码

package example;import org.junit.Test;public class ExampleTest {  @Test  public void testSplit() {    printStrings("".split("")); // 1 ["", ]    printStrings("~".split("~")); // 0 []    printStrings("~~".split("~")); // 0 []    printStrings("".split("~")); // 1 ["", ]    printStrings("~123".split("~")); // 2 ["", "123", ]  }    private void printStrings(String[] strings) {    System.out.print(strings.length + " [");    for (String str : strings) {      System.out.printf("\"%s\", ", str);    }    System.out.println("]");  }}

执行后果

后果与 Scala 是统一的,同时也解释了为什么咱们会遇到 ArrayIndexOutOfBoundsException 的问题。

起因

翻阅了 Java 的 API 文档,发现原来 Java 中的 split 办法的确跟其它语言是不一样的,这一点咱们特地容易疏忽

如果分隔符表达式与字符串不匹配,则返回原始字符串作为数组的惟一值,这也就解释了

"".split("") // 1 [""]"".split("~") // 1 [""]

如果分隔符表单式与字符串的开始字符就曾经匹配了,则返回值中第一个元素会被设置为 ""

"~123".split("~") // 2 ["", "123"]

如果 limit 参数为 0,也就是 split(String regex) 办法,则匹配后果开端的所有空字符串 "" 都会被抛弃,也就解释了上面两段代码

"~".split("~") // 0 []"~~".split("~") // 0 []

而后我又翻阅了 Scala 的官网文档,Scala 和 Java 的行为是统一的。

总结

在 Java 中应用字符串的 split 办法,个别状况下的行为是和其余编程语言是统一的,但在一些边界条件下,也有一些不统一的中央,这一点是咱们应该留神的,这也揭示了咱们,不要想当然的认为不同语言,同名函数(办法)的性能是完全一致的,当咱们遇到一些奇奇怪怪的问题时,多看官网文档才是硬道理。

本文将会继续修改和更新,最新内容请参考我的 GITHUB 上的 程序猿成长打算 我的项目,欢送 Star,更多精彩内容请 follow me。