Java中split()切割导致出现空字符串

针对Leetcode中的一个题目151、反转字符串中的单词,如果不使用trim()处理可能产生空字符串结果,针对这个查看了一下split源码并提出自己的看法

split()函数源码

 public String[] split(String regex, int limit) {
        /* fastpath if the regex is a
         * (1) one-char String and this character is not one of the
         *     RegEx's meta characters ".$|()[{^?*+\\", or
         * (2) two-char String and the first char is the backslash and
         *     the second is not the ascii digit or ascii letter.
         */
        char ch = 0;
        if (((regex.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);
                    int last = length();
                    list.add(substring(off, last));
                    off = last;
                    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, length()));

            // Construct result
            int resultSize = list.size();
            if (limit == 0) {
                while (resultSize > 0 && list.get(resultSize - 1).isEmpty()) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];
            return list.subList(0, resultSize).toArray(result);
        }
        return Pattern.compile(regex).split(this, limit);
    }

首先针对快速路径查询,这里指的是regex较为简单的情况,他给定的是长度为1且不是正则符号或者长度为2,第一个字符是反斜杠 \,第二个字符不是 ASCII 数字、ASCII 字母,且不是代理字符。

        if (((regex.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))

如果上述判断为true(说明匹配规则可以按照字符匹配),那么可以使用indexOf进行匹配,程序会把regedx的值赋给ch,并通过indexOf和substring截取字符串。
此处的off是遍历的开始地址,next是通过substring截取到的匹配regex的字符所在的位置,limit则是分割限制参数,如果是0的话则是无上限。(所有!limited代表无切割限制),list是返回结果

int off = 0;
int next = 0;
boolean limited = limit > 0;
ArrayList<String> list = new ArrayList<>();

以下是切割字符串具体过程,通过indexOf,从off开始找到ch匹配,如果值不为-1,则将[off,next)处字符串截取添加,后续off更新为next+1继续遍历。
但是这会导致一个情况:如果string的off处对于的字符就等于ch,那么substring返回的就是[off,off),会返回空字符串加入list

            while ((next = indexOf(ch, off)) != -1) {
                if (!limited || list.size() < limit - 1) {
                    //如果off==next substring会返回 "";
                    list.add(substring(off, next));
                    off = next + 1;
                } else {    // last one
                    //assert (list.size() == limit - 1);
                    int last = length();
                    list.add(substring(off, last));
                    off = last;
                    break;
                }
            }

同时因为!limited代表为无上限限制,因此最后对list的结尾处的空字符串进行删除处理

            // Construct result
            //注意,在limit==0(没有提取数量上限的时候),会把最后结果中末尾的空字符串减去
            int resultSize = list.size();
            if (limit == 0) {
                while (resultSize > 0 && list.get(resultSize - 1).isEmpty()) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];
            //剪去空字符串
            return list.subList(0, resultSize).toArray(result);

例如对于下面代码(代码中a的数量均为11)

String a = "aaaaaaaaaaabc";
String[] res = a.split("a");

最后res中的结果是

#11个空字符串
["","","","","","","","","","","","bc"] 

对于下面的代码

String a = "baaaaaaaaaaac";
String[] res = a.split("a");

最后的结果是

#10个空字符串,因为第一个a被定位的时候输出的字符串是b,然后next更新到下一个a
["b","","","","","","","","","","",“c”]

对于下面的代码

String a = "bcaaaaaaaaaaa";
String[] res = a.split("a");

最后的结果是

#0个空字符串(均在末尾被处理掉)
["bc"]
### Java `split` 方法避免生成空字符的最佳实践 当处理可能包含多个连续分隔符或结尾处有多余分隔符的情况时,可以通过设置 `limit` 参数为负数来确保不会丢失任何部分。然而,为了完全避免生成空字符,在使用 `split()` 函数前应先检查输入字符串的内容。 对于希望去除所有可能出现的多余白项(即由连续分隔符产生的),可以在调用 `split()` 后过滤掉这些元素: ```java String input = "a,,b,c,,,d"; // 使用正则表达式匹配逗号作为分隔符,并通过限定最大切割次数为 -1 来保留所有的分割片段 String[] result = Arrays.stream(input.split(",", -1)) .filter(s -> !s.trim().isEmpty()) .toArray(String[]::new); ``` 此代码段会移除因连续分隔符而形成的空字符[^2]。另外一种做法是在实际执行 `split()` 前检测并清理原始字符串中的冗余字符,比如去掉开头和结尾位置不必要的分隔符号以及压缩中间重复出现的部分。 如果目标仅仅是防止由于字符串末尾存在分隔符而导致的结果集中含有额外的空字符,则可以直接利用带有适当 `limit` 参数值的单参数版本 `split()` 或者双参数版 `split(regex, limit)` ,其中 `limit=0` 表示尽可能多地进行分割并且忽略最后面可能出现的一个或几个字段[^3]。 综上所述,最佳实践中推荐的方式是结合预处理阶段对源数据做必要的清洗工作加上合理配置 `split()` 的参数选项以达到预期效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值