见微知著: StringUtils.split

news2024/11/24 13:39:03

作者:明明如月学长, CSDN 博客专家,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《EffectiveJava》独家解析》专栏作者。

热门文章推荐

  • (1)《AI 时代,程序员的出路在何方?》
  • (2)《超全人工智能 AI工具导航网站合集》
  • (3)《如何写出高质量的文章:从战略到战术》
  • (4)《我的技术学习方法论》
  • (5)《什么? 你还没用过 Cursor? 智能 AI 代码生成工具 Cursor 安装和使用介绍》
  • (6)《我的性能方法论》
  • (7)《AI 时代的学习方式: 和文档对话》
  • (8)《人工智能终端来了,你还在用过时的 iterm?》
  • (9)《无需魔法打开即用的 AI 工具集锦》

image.png

一、背景

前一段时间,身边有个同事使用 org.apache.commons.lang3.StringUtils#split(java.lang.String, java.lang.String)对字符串进行切割,发现完全和预期不符。
本文将对这个简单的问题进行分析,并思考通过这个问题我们可以学到什么。

二、问题分析

2.1 情景再现

下面是模拟代码

 public static void main(String[] args) {
        String input = "this is a demo, this \",\"is a \"demo";
        String[] split = StringUtils.split(input,"\",\"");
        for(String str: split){
            System.out.println(str);
        }
    }

预期是使用 "," 切割字符串,因此应该被切割成两部分。
但是输出结果为:

this is a demo, this 
is a 
demo

这是怎么回事?

2.2 源码分析

org.apache.commons.lang3.StringUtils#split(java.lang.String, java.lang.String)

 /**
     * <p>Splits the provided text into an array, separators specified.
     * This is an alternative to using StringTokenizer.</p>
     *
     * <p>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.</p>
     *
     * <p>A {@code null} input String returns {@code null}.
     * A {@code null} separatorChars splits on whitespace.</p>
     *
     * <pre>
     * StringUtils.split(null, *)         = null
     * StringUtils.split("", *)           = []
     * StringUtils.split("abc def", null) = ["abc", "def"]
     * StringUtils.split("abc def", " ")  = ["abc", "def"]
     * StringUtils.split("abc  def", " ") = ["abc", "def"]
     * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
     * </pre>
     *
     * @param str  the String to parse, may be null
     * @param separatorChars  the characters used as the delimiters,
     *  {@code null} splits on whitespace
     * @return an array of parsed Strings, {@code null} if null String input
     */
    public static String[] split(final String str, final String separatorChars) {
        return splitWorker(str, separatorChars, -1, false);
    }

进入源码发现和最初现象的差不多,第一个参数是字符串,第二个是分隔符。
关键函数上的示例,都是单个分隔符,并没有多分隔符的例子。

再观察一下参数名称,第二个参数名称为 separatorChars 即为分割的字符(复数)!!
因此怀疑,这里的 "," 会被分视作三个分割字符,分别为 ","
继续往底层分析,发现果然如此: org.apache.commons.lang3.StringUtils#splitWorker(java.lang.String, java.lang.String, int, boolean)

 /**
     * Performs the logic for the {@code split} and
     * {@code splitPreserveAllTokens} methods that return a maximum array
     * length.
     *
     * @param str  the String to parse, may be {@code null}
     * @param separatorChars the separate character
     * @param max  the maximum number of elements to include in the
     *  array. A zero or negative value implies no limit.
     * @param preserveAllTokens if {@code true}, adjacent separators are
     * treated as empty token separators; if {@code false}, adjacent
     * separators are treated as one separator.
     * @return an array of parsed Strings, {@code null} if null String input
     */
    private static String[] splitWorker(final String str, final String separatorChars, final int max, final boolean preserveAllTokens) {
        // Performance tuned for 2.0 (JDK1.4)
        // Direct code is quicker than StringTokenizer.
        // Also, StringTokenizer uses isSpace() not isWhitespace()

        if (str == null) {
            return 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) {
            // 省略
        } else if (separatorChars.length() == 1) {
            // 省略
        } else {
            // standard case
            while (i < len) {
                if (separatorChars.indexOf(str.charAt(i)) >= 0) {
                    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));
        }
        return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
    }


而且通过调试发现的确走到这里。
image.png

2.3、解决办法

解决办法很简单,使用 String 的 split 方法:

   public static void main(String[] args) {
        String input = "this is a demo, this \",\"is a \"demo";
        String[] split = input.split("\",\"");
        for(String str: split){
            System.out.println(str);
        }
    }

而且看源码可知,这里的参数是正则表达式。

    /**
     * Splits this string around matches of the given <a
     * href="../util/regex/Pattern.html#sum">regular expression</a>.
     *
     * <p> This method works as if by invoking the two-argument {@link
     * #split(String, int) split} method with the given expression and a limit
     * argument of zero.  Trailing empty strings are therefore not included in
     * the resulting array.
     *
     * <p> The string {@code "boo:and:foo"}, for example, yields the following
     * results with these expressions:
     *
     * <blockquote><table cellpadding=1 cellspacing=0 summary="Split examples showing regex and result">
     * <tr>
     *  <th>Regex</th>
     *  <th>Result</th>
     * </tr>
     * <tr><td align=center>:</td>
     *     <td>{@code { "boo", "and", "foo" }}</td></tr>
     * <tr><td align=center>o</td>
     *     <td>{@code { "b", "", ":and:f" }}</td></tr>
     * </table></blockquote>
     *
     *
     * @param  regex
     *         the delimiting regular expression
     *
     * @return  the array of strings computed by splitting this string
     *          around matches of the given regular expression
     *
     * @throws  PatternSyntaxException
     *          if the regular expression's syntax is invalid
     *
     * @see java.util.regex.Pattern
     *
     * @since 1.4
     * @spec JSR-51
     */
    public String[] split(String regex) {
        return split(regex, 0);
    }


三、启发

3.1 多看源码、多调试

当发现有些行为反直觉,不太对劲时,优先去查看底层源码,如果源码看不出来,可以进行调试。
另外,建议工作之余,在不是很忙的时候,可以主动看一些自己调用类的源码,一方面可以防止误用,另外一方面可以学习优秀源码的设计。

3.2 注释的规范性

在这个例子中大家可以看到,虽然 StringUtils.split 这个函数支持传入多个分割字符,但是并没有真正覆盖到多分割字符构成的字符串的情况,这是一大败笔。
这也给我们编写注释带来一些启发,首先工具类的测试,注释中可以给出常见调用示例和结果的对应关系,方便大家使用。但是注释中的示例要覆盖常见的输入和输出,至少不能出现令人误会的情况。

3.3 对面试的作用

如果有一天面试官问题,你对 JDK 或者你工作中用到的工具类库如 commons 和 guava 等,你能不能说一说他们有哪些不好的设计?有哪些 BUG ?
我相信大多数人是懵逼的,平时自己或者帮助身边人查问题的时候,可以适当留意一下这类问题。

3.4 及时请教

遇到问题首先自己排查,如果自己排除不出来可以优先问 AI,如果AI还是解决不了尽早问同事。
工作中遇到很多次,一些并不是很难的问题,因为“当局者迷”导致浪费很多时间,问身边的同事可能瞬间解决。

创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/879459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenAI 或于 2024 年底破产?外媒也这么爱标题党

“ OpenAI是否走向破产&#xff1f;外媒标题炒作的典型&#xff0c;细看财务实况才能了解真相。” 01 — 今天看到有文章说 OpenAI 正在以迅猛的烧钱的速度前进&#xff0c;可能在2024年底底前面临破产。翻了一下&#xff0c;源于外媒一些报道&#xff1a; 《OpenAI may go ban…

Ubuntu-Server 22.04安装详细过程-图文版

一.下载Ubuntu Server镜像&#xff0c;官方地址下载即可 https://ubuntu.com/download/server 乌班图镜像网址&#xff0c;点击下载即可 二.安装乌班图镜像&#xff0c;最好自己准备u盘在ISO软件内制作完成 1.选择 Install Ubuntu Server 2.选择安装语言为英语 3.安装程序更新选…

【数据结构与算法——TypeScript】图结构(Graph)

【数据结构与算法——TypeScript】 图结构(Graph) 认识图结构以及特性 什么是图? 在计算机程序设计中&#xff0c;图结构 也是一种非常常见的数据结构。 但是&#xff0c;图论其实是一个非常大的话题 认识一下关于图的一些内容 图的抽象数据类型一些算法实现。 什么是图?…

C# OpenCvSharp 颜色反转

效果 灰度图 黑白色反转 彩色反转 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp; using Ope…

Docker部署ES服务,全量同步的时候内存爆炸,ES自动关闭,CPU100%

问题 使用canal-adapter全量同步&#xff08;参考Canal Adapter1.1.5版本API操作服务&#xff0c;手动同步数据&#xff08;4&#xff09;&#xff09;的时候 小批量数据可以正常运行&#xff08;几千条&#xff09;只要数据量一大&#xff08;上万条&#xff09;&#xff0c…

合同管理是什么,合同管理怎么做

阅读本文&#xff0c; 您可以了解&#xff1a;1、合同管理是什么&#xff1b;2、合同管理怎么做 一、合同管理是什么 合同管理是指对合同的有效执行、监督和维护过程的管理。合同是一种法律文件&#xff0c;用于规定各方之间的权利和义务。在商业和法律交易中&#xff0c;合同…

WinRAR的使用:格式转换、自动加密、自动关机

WinRAR大家应该都不陌生&#xff0c;用于将文件压缩成rar格式的压缩包&#xff0c;那么除了可以压缩文件以外&#xff0c;还有其他功能其实也很方便&#xff0c;超人今天给大家分享几个WinRAR使用小技巧。 压缩后自动关机 也许文件过大&#xff0c;那么我们压缩文件的时间就会…

MongoDB(三十九)

目录 一、概述 &#xff08;一&#xff09;相关概念 &#xff08;二&#xff09;特性 二、应用场景 三、安装 &#xff08;一&#xff09;编译安装 &#xff08;二&#xff09;yum安装 1、首先制作repo源 2、软件包名&#xff1a;mongodb-org 3、启动服务&#xff1a…

软考高级架构师下篇-11信息系统架构设计理论与实践

目录 1. 考情分析2. 基本概念3. 信息系统架构风格与分类4. 信息系统常用架构模型5. 企业信息系统总体框架6. 信息系统架构设计方法7. 前文回顾1. 考情分析 下半年软考要改成机考了,已经有几个省份确认了机考信息,虽然解决了论文手写不好修改的问题,但是考试的难度肯定加大了…

【Linux操作系统】编译过程中遇到的问题-为什么加-c?执行文件提示无法执行二进制文件?main函数参数argc和*argv[]的作用和理解?

在使用GCC编译器进行程序开发时&#xff0c;我们经常会遇到一些编译过程中的问题&#xff0c; 比如为什么要加上"-c"选项&#xff0c;以及为什么生成的可执行文件无法执行等问题。 本篇博客将详细介绍这些问题&#xff0c;并给出相应的代码和解释&#xff0c;帮助读者…

Vue编写表单常用操作(过滤和排序)

目录 HTML代码&#xff1a; js代码&#xff1a; 效果展示&#xff1a; 此次的编写代码可以直接使用 HTML代码&#xff1a; <body><div id"app"><div v-for"(value,key) in person">{{key}}--{{value}}</div><div>商品名…

NXP的官方提供的EB工具license到期后怎么续期

前安装NXP官方提供的EB工具&#xff0c;license到期了。本以为不能在试用了&#xff0c;后来在官网找了一下&#xff0c;原来是可以继续的。 小记如下&#xff1a; 我使用的是29.0版本&#xff0c; 在官网找到这个版本的EB下载位置&#xff0c;红框框里就是可以使用的…

C++ primer 1.2 练习

练习1.3:编写程序&#xff0c;在标准输出上打印Hello,World。 C语言&#xff1a; #include <iostream> using namespace std; int main() {cout << "Hello World!"; } 练习1.4:我们的程序使用加法运算符来将两个数相加。编写程序使用乘法运算符*,来打印…

vue 10 axios,promise

01.axios是干啥的 说到axios我们就不得不说下Ajax。 在旧浏览器页面在向服务器请求数据时&#xff0c;因为返回的是整个页面的数据&#xff0c;页面都会强制刷新一下&#xff0c;这对于用户来讲并不是很友好。并且我们只是需要修改页面的部分数据&#xff0c;但是从服务器端发送…

HTTP--Request详解

请求消息数据格式 请求行 请求方式 请求url 请求协议/版本 GET /login.html HTTP/1.1 请求头 客户端浏览器告诉服务器一些信息 请求头名称: 请求头值 常见的请求头&#xff1a; User-Agent&#xff1a;浏览器告诉服务器&#xff0c;我访问你使用的浏览器版本信息 可…

Python学习笔记_基础篇(一)_初识python

Python简介 python的创始人为吉多范罗苏姆&#xff08;Guido van Rossum&#xff09;。1989年的圣诞节期间&#xff0c;吉多范罗苏姆为了在阿姆斯特丹打发时间&#xff0c;决心开发一个新的脚本解释程序&#xff0c;作为ABC语言的一种继承。 Python和其他语言的对比&#xff…

【左神算法刷题班】第17节:在有序二维数组中查找目标值、等于目标字符串的子序列个数

第17节 题目1&#xff1a;在有序二维数组中查找目标值 给定一个每一行有序、每一列也有序&#xff0c;整体可能无序的二维数组 再给定一个数num&#xff0c; 返回二维数组中有没有num这个数 例子 数组如下&#xff0c;找 6 是否存在。 1 3 5 7 2 4 6 13 3 9 14 …

穿越数字奇境:探寻元宇宙中的科技奇迹

随着科技的迅速发展&#xff0c;元宇宙正逐渐成为一个备受关注的话题&#xff0c;它不仅是虚拟现实的延伸&#xff0c;更是将现实世界与数字世界融合的未来典范。在这个神秘而充满活力的数字奇境中&#xff0c;涉及了众多领域和技术&#xff0c;为我们呈现出了一个无限的创新和…

【LeetCode75】第二十九题 删除链表的中间节点

目录 题目&#xff1a; 示例; 分析: 代码: 题目&#xff1a; 示例; 分析: 给我们一个链表&#xff0c;让我们把链表中间的节点删了。 那么最直观最基础的办法是遍历两边链表&#xff0c;第一遍拿到链表长度&#xff0c;第二次把链表中间节点删了。 这个暴力做法我没事过…

深入探究QCheckBox的三种状态及其用法

文章目录 引言&#xff1a;三种状态一、未选中状态&#xff08;0&#xff09;&#xff1a;二、选中状态&#xff08;2&#xff09;&#xff1a;三、部分选中状态&#xff08;1&#xff09;&#xff1a; 判断方法结论&#xff1a; 引言&#xff1a; QCheckBox是Qt框架中常用的复…