String 中 split 方法的效率问题

问:String 中 split 方法使用时有什么效率问题吗?

答:String 的 split 分割字符串函数我们一般会如下方式使用。

String[] arr = "a,b,c".split(",");

上面代码非常简洁, 也没什么问题。不过一旦我们进行如下方式使用就可能会有问题了。

for (String line : lines) {
    line.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.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))
    {
        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);
                list.add(substring(off, value.length));
                off = value.length;
                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, value.length));

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

通过上面源码可以看出在大部分情况下 split(String regex) 函数实际上是新建了一个 Pattern 对象,再去调用它的 split(CharSequence input, int limit) 函数,仅有两种情况例外:

  1. 传入的 regex 参数仅有一个字符,且非正则表达式中的 “.$|()[{^?*+” 字符。
  2. 传入的 regex 参数仅有两个字符,且第一个字符为反斜杠,第二个字符不是数字或字母。

这样事情就很明朗了,我们在分词的时候调用了多少次 split 函数就等于新建了多少 Pattern 对象,自然会慢。因此只要对原来的实现稍加改动就能解决这个问题:

Pattern pattern = Pattern.compile("[,.!+@#$%^&*()\\- ]");
for (String line : lines) {
    pattern.split(line);
}

需要注意的是 String 中除了 split 还有一些函数也会在内部生成 Pattern 对象,包括:

  • matches(String regex)
  • replaceFirst(String regex, String replacement)
  • replaceAll(String regex, String replacement)
  • split(String regex, int limit)

所以使用这些函数的时候就要小心了,如果是被反复调用的情况,最好是声明成一个 Pattern 常量,再去调用对应的函数。

上面的内容摘自公众号《码农每日一题》

我的理解:

如果是很常规的用法,只是按照逗号后者是冒号或者是空格之类的,这个是不影响效率的,但若是需要按照很复杂的正则表达式去分的话,就会每次都新建一个Pattern对象,影响效率。所以我们直接新建好对象,调用方法,这样就节省了系统去区分标志是否是复杂正则表达式的过程,省了新建对象的时间,节省了时间。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++标准库没有提供std::stringsplit功能的官方答案尚未被确定。这可能是因为C++标准库的设计理念是尽量保持轻量级和高效率,避免引入不必要的功能和复杂性。由于split功能可以通过其他方法实现,例如使用循环和字符串处理函数,因此可能被认为不是必需的。 在C++,可以使用其他方法来实现字符串的分割操作。一种常见的方法是使用C语言的strtok方法,这是一种在C++使用的方式。可以通过将字符串转换为C风格的字符串,并使用strtok函数来分割字符串。这个函数将字符串拆分为指定的分隔符字符,并返回一个指向下一个分隔符之后的字符串的指针。可以使用循环来重复调用strtok函数,直到字符串被完全分割为止。每次调用strtok函数时,可以将分割得到的子串存储到一个容器(如vector)。这样就可以实现类似split功能的效果。 下面是一个使用C语言的strtok方法来实现字符串分割的示例代码: ```cpp std::vector<std::string> stringSplit(const std::string& strIn, char delim) { char* str = const_cast<char*>(strIn.c_str()); std::string s; s.append(1, delim); std::vector<std::string> elems; char* splitted = strtok(str, s.c_str()); while (splitted != NULL) { elems.push_back(std::string(splitted)); splitted = strtok(NULL, s.c_str()); } return elems; } ``` 这个函数接受一个字符串和一个分隔符作为参数,并返回一个存储了分割后子串的vector。 虽然C++标准库没有提供split方法,但通过使用其他方法,如上述所示的使用C语言的strtok方法,我们仍然可以实现类似的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++string字符串split的6种方法](https://blog.csdn.net/whl0071/article/details/128722931)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值