一、问题

java中String的split()是我们经常使用的方法,用来按照特定字符分割字符串,那么我们看以下一段代码:

    public void splitTest() {
String str = "aaa|bbb|ccc";
String[] array = str.split("|");
System.out.println(Arrays.toString(array));
}

是不是感觉很简单,就是吧str按照"|"分割,结果就是[aaa,bbb,ccc]嘛。如果你这么想,那么以后在用这个方法时你可能会犯下大错,把程序跑起来,你会惊讶的发现程序输入如下结果:

[, a, a, a, |, b, b, b, |, c, c, c]

为什么会出现这种情况呢?我们再来运行一下str.split("");就会发现结果和之前的结果一样,也就是说我们使用"|"分割的时候其实split是按照空字符分割的。

二、探究

为了找到原因,我们在来使用其他几种字符测试一下,结果如下:

0:[aaa, bbb, ccc]
$:[aaa$bbb$ccc]
,:[aaa, bbb, ccc]
*:异常java.util.regex.PatternSyntaxException: Dangling meta character '*' near index 0
^:[aaa^bbb^ccc]

我们会发现对于数字,","结果是正确的,而对于特殊字符,结果都不正确,而且如果经验丰富,你可能已经发现,这些特殊字符都是正则表达式中的匹配符,而那个异常也很明确的说明了这一点。

因此,我们可以猜测在split内部使用了正则表达式来匹配并分割字符串。那么现在又有一个疑问:我们知道一般情况下String涉及的字符串处理多数使用indexOf()(可能是考虑效率问题吧),那么这里为什么使用正则呢?我们还是直接看看源码吧:

public String[] split(String regex, int limit) {
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,如果条件为true,那么就使用indexOf()判断后substring()截取,如果为false,则使用正则处理。那么我们就来分析下这个if的条件:

//第一步部分:当regex的长度为1且不是“.$|()[{^?*+\\”中的时,为真
(regex.value.length == 1 &&".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) //第二部分:当长度为2时且第一个字符为“\”转义字符,第二个字符不是字符0-9 a-z A-Z 以及utf-16之间的字符
(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)

从if可以看出如果regex内容为一个非正则匹配符或者是转以后的特殊字符时,采用indexOf()+substring()处理,否则使用正则表达式。
    那么为什么这么做呢,直接使用第一种方法不就行了?其实我们可以考虑一种复杂的情况:

 aaax111xbbbx222xcccx333xddd

如果我想分割出这样的结果 [aaa, bbb, ccc, ddd] 应该则么做呢?按照第一种方法,实现起来很麻烦,需要一大堆判断,反而不如正则方便,而String中的split()方法正是出于这样的考虑实现的,不信你用split("x.*?x")试试。

三、启示

这次这件事虽小,但是却让我收获不少。

  • 1、使用一个不了解的方法前,一定要看一眼提示,split方法说明里虽然没有明确的提示上述问题,但是多个地方都提到了正则,连参数名字都是regex,作为一个经验丰富的程序员,应该想到这一点。
  • 2、对于一些小的功能点(往往我们还胸有成竹)一定要写个Test测一下,其实把代码贴过去跑一下费不了多少劲,但是当他淹没在成百上千行的代码中时,想要在发现问题就很费劲了,特别是你不调试根本想不到这点会错。
  • 3、我们在多数项目中都要建个util工具包,封装各种辅助的操作类,然而这些类的功能往往只是满足当时的需求,之后用到类似但有变动的功能时往往习惯单独实现一个,而不是实现一个完善的覆盖原来的,从设计的角度会造成接口不一致:明明同样的需求、同样的参数,我却还要看看他的内部实现才能选择用哪个。对于这些工具类,我们不一定要非常完美,但是应该用心做到尽量完善,这也方便复用。

扯这么多,也不知道说了些什么,OVER,睡觉......

String.split()方法你可能不知道的一面的更多相关文章

  1. C# String.split()用法小结。String.Split 方法 (String[], StringSplitOptions)

    split()首先是一个分隔符,它会把字符串按照split(' 字符')里的字符把字符串分割成数组,然后存给一个数组对象. 输出数组对象经常使用foreach或者for循环. 第一种方法 string ...

  2. [Java] String.Split 方法的6个重载函数

    String.Split 方法有6个重载函数: 程序代码 1) public string[] Split(params char[] separator) 2) public string[] Sp ...

  3. java String.split方法是用注意点(转)

    转自:http://www.blogjava.net/fanyingjie/archive/2010/08/05/328059.html 在java.lang包中有String.split()方法,返 ...

  4. String split方法与Guava Splitter用法区别

    String split方法与Guava Splitter用法区别 今天同事写了一段使用String split方法的代码,如下所示,同事期望得到的是字符"1",但是没想到却得到空 ...

  5. as3 string split方法一些注意

    split () 方法 AS3 function split(delimiter:*, limit:Number = 0x7fffffff):Array 如果指定 limit 参数,返回的数组中具有的 ...

  6. java学习笔记之String.Split方法

    hello 大家好,好久不见,今天 我们要讨论的是java的split方法,或许你很早就知道了,但你真的知道吗? 我们来看看吧. 首先我们来看看我们最常用的split()方法也就是单个参数的方法 pu ...

  7. String.split()方法

    如果用“(”作为分隔的话,必须是如下写法,String.split("\\("),这样才能正确的分隔开,不能用String.split("("); 如果用“)” ...

  8. java中String对象的split方法

    在java.lang包中有String.split()方法,返回是一个String[]数组,今天碰到一个自己没注意的问题: 1.特殊分隔符 String str1 = "123|456|78 ...

  9. 分享一个 Java String split 快速分割的方法

    java中string.split() 方法比较强大,但是split()方法采用正则表达式,速度相对会慢一点, 其实大多数场景下并不需要使用正则表达式,下面分享一个不使用正则表达式分隔字符串的方法. ...

随机推荐

  1. LA3027 合作网络-并查集压缩路径

    有N个结点 一次 I u v 操作表示把结点u的父结点设为v,距离为|u-v|%1000.输入保证执行指令前u没有父结点 一次E u 操作表示询问u到根结点的距离 O操作表示结束 #include&l ...

  2. 视频运动motion vector获取。

    http://victor.csie.org/blog/archives/362------------------------------------------------------------ ...

  3. OS版本调研

    1引言 1.1 编写目的 本文的主要目的是通过对当前项目中使用的各种版本的操作系统进行比较,分析各自特性和稳定程度,最终推荐合适的版本作为当前的标准系统. 1.2 背景 当前,部门负责管理维护的现网使 ...

  4. 感受机房管理化繁为简-新款KVM使用心得

    感受机房管理化繁为简-新款KVM使用心得 一. 背景 随着网络应用的不断增多,各地机房服务器数量也随之增加,利用多传统主机切换器的方式已经无法满足目前这种区域广.设备多人员紧缺的现状,而且即使是使用了 ...

  5. Oracle笔记 十三、PL/SQL面向对象之package

    --将方法和过程用包定义 create or replace package pkg_emp as --输入员工编号查询出员工信息 procedure pro_findInfo( in_empno e ...

  6. SQL Server 数据库基础编程

    Ø Go批处理语句 用于同时执行多个语句 Ø 使用.切换数据库 use master go     Ø 创建.删除数据库   方法1.   --判断是否存在该数据库,存在就删除 if (exists ...

  7. JS日期(Date)处理函数总结

    获取日期 1.Date() ——返回当日的日期和时间. 2.getDate() ——从 Date 对象返回一个月中的某一天 (1 ~ 31). 3.getDay() ——从 Date 对象返回一周中的 ...

  8. 14种网页图片和文字特效的jQuery插件代码

    1.网页图片3d旋转jQuery代码 演示和下载地址 2.存css3实现的tabl选项卡代码 演示和下载地址 3.jQuery标签旋转代码 演示和下载地址 4.鼠标悬浮的图片选项卡代码 演示和下载地址 ...

  9. POJ C程序设计进阶 编程题#4:寻找平面上的极大点

    编程题#4:寻找平面上的极大点 来源: POJ (Coursera声明:在POJ上完成的习题将不会计入Coursera的最后成绩.) 注意: 总时间限制: 1000ms 内存限制: 65536kB 描 ...

  10. iOS Architecture Patterns

    By Bohdan Orlov on 21 Mar 2016 - 0 Comments iOS FYI: Slides from my presentation at NSLondon are ava ...