【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站均有视频解说,欢迎来玩。

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

相关文章
|
6天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
31 7
|
17天前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
14天前
|
SQL Java 索引
java小工具util系列2:字符串工具
java小工具util系列2:字符串工具
132 83
|
14天前
|
Java 数据库
java小工具util系列1:日期和字符串转换工具
java小工具util系列1:日期和字符串转换工具
49 26
|
6天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
15天前
|
安全 Java 开发者
Java中WAIT和NOTIFY方法必须在同步块中调用的原因
在Java多线程编程中,`wait()`和`notify()`方法是实现线程间协作的关键。这两个方法必须在同步块或同步方法中调用,这一要求背后有着深刻的原因。本文将深入探讨为什么`wait()`和`notify()`方法必须在同步块中调用,以及这一机制如何确保线程安全和避免死锁。
29 4
|
15天前
|
Java
深入探讨Java中的中断机制:INTERRUPTED和ISINTERRUPTED方法详解
在Java多线程编程中,中断机制是协调线程行为的重要手段。了解和正确使用中断机制对于编写高效、可靠的并发程序至关重要。本文将深入探讨Java中的`Thread.interrupted()`和`Thread.isInterrupted()`方法的区别及其应用场景。
21 4
|
13天前
|
Java 数据处理 数据安全/隐私保护
Java处理数据接口方法
Java处理数据接口方法
20 1
|
7月前
|
存储 XML 缓存
Java字符串内幕:String、StringBuffer和StringBuilder的奥秘
Java字符串内幕:String、StringBuffer和StringBuilder的奥秘
72 0
|
4月前
|
安全 Java API
【Java字符串操作秘籍】StringBuffer与StringBuilder的终极对决!
【8月更文挑战第25天】在Java中处理字符串时,经常需要修改字符串,但由于`String`对象的不可变性,频繁修改会导致内存浪费和性能下降。为此,Java提供了`StringBuffer`和`StringBuilder`两个类来操作可变字符串序列。`StringBuffer`是线程安全的,适用于多线程环境,但性能略低;`StringBuilder`非线程安全,但在单线程环境中性能更优。两者基本用法相似,通过`append`等方法构建和修改字符串。
74 1