点赞再看,动力无限 。微信搜「程序猿阿朗」 。
本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章 。
文章插图
在日常的 Java 开发中,由于 JDK 未能提供足够的常用的操作类库,通常我们会引入 Apache Commons Lang 工具库或者 Google Guava 工具库简化开发过程 。两个类库都为
java.lang
API 提供了很多实用工具,比如经常使用的字符串操作,基本数值操作、时间操作、对象反射以及并发操作等 。<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency>
但是,最近在使用 Apache Commons Lang 工具库时踩了一个坑,导致程序出现了意料之外的结果 。StringUtils.split 的坑也是因为踩了这个坑,索性写下一篇文章好好介绍下Apache Commons Lang 工具库中字符串操作相关 API 。
先说坑是什么,我们都知道 String 类中到的
split
方法可以分割字符串,比如字符串 aabbccdd
根据 bc
分割的结果应该是 aab
和 cdd
才对,这样的结果也很容易验证 。String str = "aabbccdd";for (String s : str.split("bc")) {System.out.println(s);}// 结果aabcdd
可能是因为 String 类中的 split
方法的影响,我一直以为 StringUtils.split
的效果应该相同,但其实完全不同,可以试着分析下面的三个方法输出结果是什么 , StringUtils 是 Commons Lang 类库中的字符串工具类 。 public static void testA() {String str = "aabbccdd";String[] resultArray = StringUtils.split(str, "bc");for (String s : resultArray) {System.out.println(s);}}
我对上面 testA 方法的预期是 aab
和 cdd
,但是实际上这个方法的运行结果是:// testA 输出aadd
可以看到 b
和 c
字母都不见了,只剩下了 a
和 b
,这是已经发现问题了,查看源码后发现 StringUtils.split
方法其实是按字符进行操作的 , 不会把分割字符串作为一个整体来看 , 返回的结果中不也会包含用于分割的字符 。验证代码:
public static void testB() {String str = "abc";String[] resultArray = StringUtils.split(str, "ac");for (String s : resultArray) {System.out.println(s);}}// testB 输出bpublic static void testC() {String str = "abcd";String[] resultArray = StringUtils.split(str, "ac");for (String s : resultArray) {System.out.println(s);}}// testC 输出bd
输出结果和预期的一致了 。StringUtils.split 源码分析点开源码一眼看下去,发现在方法注释中就已经进行提示了:返回的字符串数组中不包含分隔符 。
The separator is not included in the returned String array. Adjacent separators are treated as one separator. For more control over the split use the StrTokenizer class....继续追踪源码,可以看到最终 split 分割字符串时入参有四个 。
private static String[] splitWorker(final String str, // 原字符串final String separatorChars,// 分隔符final int max,// 分割后返回前多少个结果,-1 为所有final boolean preserveAllTokens // 暂不关注) {}
根据分隔符的不同又分了三种情况 。1. 分隔符为 null
final int len = str.length();if (len == 0) {return ArrayUtils.EMPTY_STRING_ARRAY;}final List<String> list = new ArrayList<>();int sizePlus1 = 1;int i = 0;int start = 0;boolean match = false;boolean lastMatch = false;if (separatorChars == null) {// Null separator means use whitespacewhile (i < len) {if (Character.isWhitespace(str.charAt(i))) {if (match || preserveAllTokens) {lastMatch = true;if (sizePlus1++ == max) {i = len;lastMatch = false;}list.add(str.substring(start, i));match = false;}start = ++i;continue;}lastMatch = false;match = true;i++;}}// ...if (match || preserveAllTokens && lastMatch) {list.add(str.substring(start, i));}
可以看到如果分隔符为 null
,是按照空白字符 Character.isWhitespace()
分割字符串的 。分割的算法逻辑为:a. 用于截取的开始下标置为0 ,逐字符读取字符串 。b. 碰到分割的目标字符 , 把截取的开始下标到当前字符之前的字符串截取出来 。c. 然后用于截取的开始下标置为下一个字符,等到下一次使用 。d. 继续逐字符读取字符串、
2. 分隔符为单个字符
逻辑同上,只是判断逻辑
Character.isWhitespace()
变为了指定字符判断 。
推荐阅读
- 书名号使用方法 书名号用法
- Java Timer使用介绍
- lol手游戏命师的大招怎么使用(lol手游技能释放技巧)
- SpringBoot 常用注解的原理和使用
- 眼霜的正确使用顺序,眼霜的正确使用方法
- 漫步者funbuds怎么配对_漫步者funbuds使用说明
- 四 【单片机入门】应用层软件开发的单片机学习之路-----ESP32开发板PWM控制电机以及中断的使用
- 含源码 【YOLOv5】手把手教你使用LabVIEW ONNX Runtime部署 TensorRT加速,实现YOLOv5实时物体识别
- 【日志系统】Loki日志监控 - 入门初体验
- 华为watch3可以使用微信吗_华为watch3有微信吗