【Java实用技术】字符串的拆分用什么方法好?有一半程序员都掉过split坑

简介: 【Java实用技术】字符串的拆分用什么方法好?有一半程序员都掉过split坑

必备字符串操作

我们将字符串操作分为下面6种:

  1. 基本操作方法
  2. 字符串判空、比较
  3. 字符串截取和拆分
  4. 字符串查找和替换
  5. 字符串和其他类型数据的转换
  6. 字符串拼接和格式化

今天我们来讲解第3.2节。


1、字符串拆分原生方法

对于字符串拆分的原生操作

//字符串拆分原生方法:publicString[] String.split(Stringregex)

问题:对字符串中含有“|”分割符部分进行拆分,怎么写?

直接str.split("|")可以吗?当然可以,但是结果是不是你想要的呢?

Stringstr="a.|b.";
***原生String.split方法结果*****[a, ., |, b, .]

答案:如果只是单纯想按照“|”分割,正确的写法是ss.split("\\|"),因为这里碰到烦人的正则表达式。

Stringstr="a.|b."; // 使用“|”拆分System.out.println("*** 原生 String.split 方法 *****");
if (StringUtils.isNotEmpty(str)) {
String[] s1=str.split("\\|");
System.out.println(Arrays.toString(s1));
}

split源码分析

// 选取源码中关键的分析/**围绕给定正则表达式的匹配拆分此字符串。此方法的工作方式就像通过使用给定表达式和零限制参数调用双参数split方法一样。 因此,结果数组中不包含尾随空字符串。参数:regex – 定界正则表达式返回:通过围绕给定正则表达式的匹配拆分此字符串计算出的字符串数组抛出:PatternSyntaxException – 如果正则表达式的语法无效*/publicString[] split(Stringregex) {
returnsplit(regex, 0);
}
/**围绕给定正则表达式的匹配拆分此字符串。以str形式调用此方法。 split(regex , n)产生与表达式相同的结果:Pattern.compile(regex).split(str , n)*/publicString[] split(Stringregex, intlimit) {
/* 下面情况使用快速模式:(1)单个字符但是不能是正则中的元字符,比如".$|()[{^?*+\\"(2)两个字符,第一个字符是\,第二个字符不能是数字或者字母[0-9a-zA-Z]*/charch=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))
        {
//省略 使用substring拆分  过程String[] result=newString[resultSize];
returnlist.subList(0, resultSize).toArray(result);
        }
// 其他场景使用正则表达式拆分returnPattern.compile(regex).split(this, limit);
    }


源码不复杂,核心内容是:

(1)不是正则表达式的元字符".$|()[{^?*+\\"\开头但后面不是[0-9a-zA-Z]的使用快速拆分模式

(2)其他情况使用正则表达式匹配模式拆分

这里涉及到另外一个知识点,正则表达式,如果对正则表达式感兴趣的可以参考其他博文。

当然我认为你是没有兴趣的,而且很多程序员也很讨厌正则表达的规则,需要用的时候再翻阅。

对于(1),如果你十分精通正则表达式,可以很快知道哪些是正则元字符,你可以尝试使用原生的方法,毕竟使用方便且拆分速度确实快。

对于(2),因为正则表达式需要编译再匹配,比较耗时,所以不建议使用正则表达式。至于为什么正则表达式会慢,你就想你只见过一个姑娘一面就想找到她,和你知道她的名字地址具体信息比,哪个找到她更快?

综上所述,原生方法存在以下问题:

  • 使用前要判空
  • 小心正则表达式里面特殊字符
  • 性能不佳

为什么不推荐StringTokenizer

当然有同学可能会说jdk还有一个用于拆分字符串的类:StringTokenizer,它不是基于正则表达式进行拆分,性能应该高。但是笔者并不推荐使用此方法进行字符串拆分。我们先看源码:

 

/**StringTokenizer is a legacy class that is retained for compatibility reasons although its use is discouragedin new code. It is recommended that anyone seeking this functionality use the split method of String or the java.util.regex package instead.Since: JDK1.0*/publicclassStringTokenizerimplementsEnumeration<Object> {
//...}


只看关键单词discouraged=不推荐。知道它是一个为了兼容而遗留下的类,官方不推荐使用就足够的。

另外一个不推荐的原因是:本来很简单的一个拆分,使用它需要搭配while循环,索性就不要用它了。


2、推荐字符串拆分操作

善用StringUtils.splitXX()

推荐使用Apache工具类StringUtils.splitXX()

优势:不用担心字符串为null(空),方法名直白。

推荐常用方法:

// 按照指定分割符拆分字符串publicstaticString[] split(Stringstr, Stringseparator)
// 按照完整分割符拆分字符串publicstaticString[] splitByWholeSeparator(Stringstr, Stringseparator)
// 拆分字符串,保留所有分割符位置 --较少使用publicstaticString[] splitPreserveAllTokens(Stringstr, StringseparatorChars)
// 获取分割符的前部分publicstaticString[] substringBefore(Stringstr, StringseparatorChars)
//获取分割符的后面部分publicstaticString[] substringAfterLast(Stringstr, StringseparatorChars)


对于上面问题按照“|”拆分,可以一行代码搞定。

Stringstr="a.|b.";
String[] s2=StringUtils.split(str, "|");
/* 输出:[a., b.] */

开发中比较常见的需求是对", . | * : "等进行拆分。如果你对正则元字符不熟悉,很容易写出错误的拆分。

所以还是建议直接使用StringUtils工具类进行拆分,减少写代码时的思考成本。

StringUtils.split也有坑


还是上述例子,如果对字符串"a.|b.c"按照".|"进行拆分,使用StringUtils.split可以吗?

// 问题:请使用“.|”拆分Stringstr="a.|b.c";
String[] s2=StringUtils.split(str, ".|");
/* 运行结果:[a, b, c]并不是期望的[a, b.c] */

答案是不行,拆分的结果是[a, b, c],并不是期望的[a, b.c], 这是因为StringUtils.split方法是将多字符分割符拆成单个分割符进行拆分,再将非分割符部分返回。

如果希望对多字符分割符拆分,请使用StringUtils.splitByWholeSeparator()方法。

这个小细节,希望大家能够避坑。


3、demo用例

importorg.apache.commons.lang3.StringUtils;
importjava.util.Arrays;
importjava.util.StringTokenizer;
/*** Java实用技术课程 By Pandas.* 公众号:Java实用技术手册* JDK版本:jdk1.8.0_66** @author Pandas* @date 2021/10/30*/publicclassStringSplitDemo {
/*** 字符串拆分方法用哪个方法好?*/publicstaticvoidmain(String[] args) {
// 问题:请使用“|”拆分Stringstr="a.|b.c";
System.out.println("*** 原生 String.split 方法 *****");
if (StringUtils.isNotEmpty(str)) {
String[] s1=str.split("\\|");
System.out.println(Arrays.toString(s1));
        }
String[] s11=StringUtils.split(str, "|");
System.out.println(Arrays.toString(s11));
System.out.println("*** 原生 StringTokenizer 方法 *****");
StringTokenizertokenizer=newStringTokenizer(str, "|");
while (tokenizer.hasMoreTokens()) {
System.out.println(tokenizer.nextToken());
        }
// "a.|b.c"str="a.|b.c";
System.out.println("*** StringUtils 方法 *****");
String[] s2=StringUtils.split(str, ".|");
System.out.println(Arrays.toString(s2));
String[] s3=StringUtils.splitByWholeSeparator(str, ".|");
System.out.println(Arrays.toString(s3));
str="a.|b...";
String[] s4=StringUtils.splitPreserveAllTokens(str, ".");
System.out.println(Arrays.toString(s4));
    }
}
/** 运行结果===>*** 原生 String.split 方法 *****[a., b.c][a., b.c]*** 原生 StringTokenizer 方法 *****a.b.c*** StringUtils 方法 *****[a, b, c][a, b.c][a, |b, , , ]*/


4、总结

  • 直接使用StringUtils最省事(注意上面写的避坑)。
  • 如果是对非特殊字符拆分,比如对字母数字拆分,可以使用原生的split方法。
  • 不要使用StringTokenizer。

感谢阅读本期内容,希望对新入行的你有帮助。

如果你意犹未尽,后面有一个字符串拆分姊妹篇,欢迎继续查看。


往期内容:


我是Pandas,专注Java编程实用技术分享,公众号Java实用技术手册和B站均有视频解说,欢迎来玩。

如果你觉得这篇文章有用,别忘了点赞+关注,一起进步!

相关文章
|
2月前
|
Java
Java语言实现字母大小写转换的方法
Java提供了多种灵活的方法来处理字符串中的字母大小写转换。根据具体需求,可以选择适合的方法来实现。在大多数情况下,使用 String类或 Character类的方法已经足够。但是,在需要更复杂的逻辑或处理非常规字符集时,可以通过字符流或手动遍历字符串来实现更精细的控制。
253 18
|
2月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
213 4
|
3月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
154 11
|
2月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
313 5
|
存储 XML 缓存
Java字符串内幕:String、StringBuffer和StringBuilder的奥秘
Java字符串内幕:String、StringBuffer和StringBuilder的奥秘
238 0
|
安全 Java API
【Java字符串操作秘籍】StringBuffer与StringBuilder的终极对决!
【8月更文挑战第25天】在Java中处理字符串时,经常需要修改字符串,但由于`String`对象的不可变性,频繁修改会导致内存浪费和性能下降。为此,Java提供了`StringBuffer`和`StringBuilder`两个类来操作可变字符串序列。`StringBuffer`是线程安全的,适用于多线程环境,但性能略低;`StringBuilder`非线程安全,但在单线程环境中性能更优。两者基本用法相似,通过`append`等方法构建和修改字符串。
259 1
|
API C# 开发者
WPF图形绘制大师指南:GDI+与Direct2D完美融合,带你玩转高性能图形处理秘籍!
【8月更文挑战第31天】GDI+与Direct2D的结合为WPF图形绘制提供了强大的工具集。通过合理地使用这两种技术,开发者可以创造出性能优异且视觉效果丰富的WPF应用程序。在实际应用中,开发者应根据项目需求和技术背景,权衡利弊,选择最合适的技术方案。
1005 1
|
存储 Java
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
|
存储 Java