你只会用 split?试试 StringTokenizer,性能可以快 4 倍!!

简介: 我们都知道,分割字符串要使用 String 的 split() 方法,split 方法虽然深入人心,使用也简单,但效率太低!其实在 JDK 中,还有一个性能很强的纯字符串分割工具类:StringTokenizer。

我们都知道,分割字符串要使用 String 的 split() 方法,split 方法虽然深入人心,使用也简单,但效率太低!


其实在 JDK 中,还有一个性能很强的纯字符串分割工具类:StringTokenizer。


这个类在 JDK 1.0 中就推出来了,但在实际工作却发现很少有人使用,网上有人说不建议使用了,甚至还有人说已经废弃了,真的是这样吗?


StringTokenizer 被废弃了吗?

栈长翻阅了一些资料,原来在 Oracle JDK 官方文档中已经有了描述,这是最新的 Oracle JDK 15 的官方文档关于 StringTokenizer 的说明:


StringTokenizer is a legacy class that is retained for compatibility reasons although its use is discouraged in new code. It is recommended that anyone seeking this functionality use the split method of String or the java.util.regex package instead.


参考:https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/StringTokenizer.html


StringTokenizer 原来是一个遗留类,并未被废弃,只是出于兼容性原因而被保留,在新代码中已经不鼓励使用它了,建议使用 String 的 split 方法或 java.util.regex 包代替。


再来看 StringTokenizer 类的源码:




可以看到 StringTokenizer 类并未标识 @Deprecated,说明在后续的版本中也还可以继续使用,官方还会继续保留,并不会进行删除。


就像 JDK 集合中的 Vector 和 Hashtable 类一样,虽然它们略显笨重,但并不说明它们没有用了,另外,它们也不存在致命缺陷,所以一直保留到现在并未废除掉。


StringTokenizer 没人用了吗?

答案:非也!


栈长在最新的 Spring 5.x 框架 StringUtils 工具类中就发现了 StringTokenizer 的使用身影:


image.png


org.springframework.util.StringUtils#tokenizeToStringArray


另外,栈长还看到了一篇《Faster Input for Java》的文章,其中就介绍了他们是使用 StringTokenizer 来分割字符串的,其效率是 string.split() 的 4 倍:


We split the input line into string tokens, since one line may contain multiple values. To split the input, StringTokenizer is 4X faster than string.split().


参考:https://www.cpe.ku.ac.th/~jim/java-io.html


所以,即使 JDK 不鼓励使用它了,但它并未被废除,并且性能还这么强,在一些对性能比较敏感的系统中,或者对性能比较有要求的编程竞赛中,StringTokenizer 就能发挥重要作用。


所以,大胆用吧,StringTokenizer 还是可以用的,用的好还能出奇效!另外,往期 Java 技术系列文章我也已经整理好了,关注公众号Java技术栈,在后台回复:java,可以获取阅读,非常齐全。


StringTokenizer vs split

说了这么多,相信大部分人都只用过 split,而没用过 StringTokenizer,那么栈长今天就来对比下这两个字符串分割法的性能及利弊。


测试代码如下:

import java.util.Random;
import java.util.StringTokenizer;
/**
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class SplitTest {
    private static final int MAX_LOOP = 10000;
    /**
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        System.out.println(sb.toString());
        for (int i = 0; i < 1000; i++) {
            sb.append(new Random().nextInt()).append(" ");
        }
        split(sb.toString());
        stringTokenizer(sb.toString());
    }
    /**
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    private static void split(String str) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < MAX_LOOP; i++) {
            String[] arr = str.split(" ");
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < arr.length; j++) {
                sb.append(arr[j]);
            }
        }
        System.out.printf("split 耗时 %s ms\n", System.currentTimeMillis() - start);
    }
    /**
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    private static void stringTokenizer(String str) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < MAX_LOOP; i++) {
            StringTokenizer stringTokenizer = new StringTokenizer(str, " ");
            StringBuilder sb = new StringBuilder();
            while (stringTokenizer.hasMoreTokens()) {
                sb.append(stringTokenizer.nextToken());
            }
        }
        System.out.printf("StringTokenizer 耗时 %s ms", System.currentTimeMillis() - start);
    }
}

在我本机测试结果如下:

image.png

从测试数据看,虽然 StringTokenizer 有一点性能优势,但并不太明显,我并没有测试出有 4 倍的性能差距,可能和测试数据、测试方法、以及测试的 JDK 版本有关系。

然后,我再把 split 测试方法中的 " " 改成 "\\s"

image.png

把 split 方法改成正则表达式再测试,这下差距就明显了。


我们都知道解析正则表达式会比较慢一点,这很正常,但 StringTokenizer 并不支持传入正则表达式,只能使用字符串作为分隔符,所以这测试结果就没多大意义了,这就是症结了。。


总结

虽然 JDK 不鼓励使用 StringTokenizer 了,但并不说明它不能用了,相反,如果你的系统对性能有非常严格的要求,又不是很复杂的字符串分割,好好使用它反而可以带来高效。


但话又说回来,一般的应用程序用 split 也就够了,因为它够简单、又支持正则表达式,在一般的应用中也不会存在像文中测试的大批量的字符串循环分割,另外,StringTokenizer 在单次分割的性能上也没有性能优势。


最后,关于字符串的分割方法,我们除了字符串本身的 split 方法,我们还要知道 StringTokenizer 这个类,多知道点不是坏事。另外,在 Spring、Apache Commons 工具类中也都有封装好的 StringTokenizer 工具类,有兴趣的可以直接拿去用。


好了,今天的分享就到这里了,后面栈长我会更新更多好玩的 Java 技术文章,关注公众号Java技术栈第一时间推送,不要走开哦。


本节教程所有实战源码已上传到这个仓库:


https://github.com/javastacks/javastack


相关文章
|
机器学习/深度学习 人工智能
谷歌发AI足球教练模型TacticAI 登Nature子刊
【2月更文挑战第30天】谷歌DeepMind与利物浦FC合作开发的TacticAI是一款人工智能足球战术助手,能分析角球战术并提供建议,已在《Nature Communications》发表。利用图机器学习和几何深度学习,TacticAI预测角球战术并在90%情况下优于传统方法。该系统有助于教练制定精准战术,但实际比赛中的适应性和数据隐私限制了其潜力的完全发挥。
556 2
谷歌发AI足球教练模型TacticAI 登Nature子刊
|
算法 数据可视化 数据挖掘
Barnes-Hut t-SNE:大规模数据的高效降维算法
Barnes-Hut t-SNE是一种针对大规模数据集的高效降维算法,它是t-SNE的变体,用于高维数据可视化。t-SNE通过保持概率分布相似性将数据从高维降至2D或3D。Barnes-Hut算法采用天体物理中的方法,将时间复杂度从O(N²)降低到O(NlogN),通过构建空间索引树和近似远距离交互来加速计算。在scikit-learn中可用,代码示例展示了如何使用该算法进行聚类可视化,成功分离出不同簇并获得高轮廓分数,证明其在大數據集上的有效性。
483 1
|
存储 分布式计算 大数据
Flume+Hadoop:打造你的大数据处理流水线
本文介绍了如何使用Apache Flume采集日志数据并上传至Hadoop分布式文件系统(HDFS)。Flume是一个高可用、可靠的分布式系统,适用于大规模日志数据的采集和传输。文章详细描述了Flume的安装、配置及启动过程,并通过具体示例展示了如何将本地日志数据实时传输到HDFS中。同时,还提供了验证步骤,确保数据成功上传。最后,补充说明了使用文件模式作为channel以避免数据丢失的方法。
741 4
|
存储 数据采集 搜索推荐
商品比价系统实现
商品比价系统实现
1115 4
|
SQL 安全 关系型数据库
【SQL】已解决:MySQL 服务无法启动
【SQL】已解决:MySQL 服务无法启动
5219 1
|
SQL HIVE
Hive【基础知识 04】【Hive 属性配置的三种方式及配置的优先级说明】
【4月更文挑战第7天】Hive【基础知识 04】【Hive 属性配置的三种方式及配置的优先级说明】
595 0
|
存储 算法 Java
Java-数据结构(二)-Map:HashMap、TreeMap、LinkedHashMap
Java-数据结构(二)-Map:HashMap、TreeMap、LinkedHashMap
427 1
|
资源调度 分布式计算 大数据
大数据平台搭建(容器环境)——Flink on Yarn安装配置
大数据平台搭建(容器环境)——Flink on Yarn安装配置
|
缓存 负载均衡 算法
9.Linux文件管理命令---sort按顺序显示文件内容
9.Linux文件管理命令---sort按顺序显示文件内容
226 1

热门文章

最新文章