Java最佳实践–字符串性能和精确字符串匹配

在使用Java编程语言时,我们将继续讨论与建议的实践有关的系列文章,我们将讨论String性能调优。 我们将专注于如何有效地处理字符串创建, 字符串更改和字符串匹配操作。 此外,我们将提供我们自己的用于精确字符串匹配的最常用算法的实现。 与Java开发工具包中提供的用于精确字符串匹配的幼稚方法相比,这些算法中的许多算法都可以实现更好的性能。 本文以上述精确字符串匹配算法之间的性能比较作为结束。

所有讨论的主题均基于用例,这些用例来自于电信行业的关键任务超高性能生产系统的开发。

在阅读本文的每个部分之前,强烈建议您参考相关的Java API文档以获取详细信息和代码示例。

所有测试均针对具有以下特征的Sony Vaio进行:

  • 系统:openSUSE 11.1(x86_64)
  • 处理器(CPU):Intel(R)Core(TM)2 Duo CPU T6670 @ 2.20GHz
  • 处理器速度:1,200.00 MHz
  • 总内存(RAM):2.8 GB
  • Java:OpenJDK 1.6.0_0 64位

应用以下测试配置:

  • 并发工作者线程数:1
  • 每个工作人员重复测试的次数:1000
  • 整体测试次数:100

字符串性能调优

许多人在使用String对象时并没有考虑性能。 但是,滥用String类可能会严重降低应用程序的性能。 您应该记住的最重要的事情是:

  1. 字符串对象是不可变的。 一旦创建了String对象,就无法更改它。 每个更改String的操作都会导致至少创建一个新的对象实例。 例如,使用串联运算符(+)串联两个字符串会导致创建两个新对象,一个用于实际串联的临时StringBuffer对象,一个指向串联结果的新String实例( StringBuffer “ toString()”操作为用于实例化生成的String )。 另一方面,与串联运算符(+)相比,使用String “ concat(String ..)”操作执行String串联将提供更好的性能结果。 在后台, 字符串 “ concat(String ..)”操作利用本机“ System.arrayCopy”操作来准备一个带有两个串联字符串内容的字符数组。 最后,创建一个新的String实例,该实例指向连接的结果
  2. 字符串引用是指向实际String对象实例的指针。 因此,如果实际的String对象不同,则使用“ ==”运算符比较表示相同文字内容的两个String实例将返回“ false”。 此外,使用String “ equals(String ..)”或String “ equalsIgnoreCase(String ..)”操作比较两个String实例可提供有效的结果,但如果两个比较的String分别由不同的实例表示,则执行字符与字符的比较。它们的文字内容具有相同的长度。 可以想象,“ equals(String ..)”和“ equalsIgnoreCase(String ..)”操作比“ ==”运算符要昂贵得多,后者在实例级别比较字符串。 然而,上述操作在所有文字内容检查之前执行实例相等性检查( this == obj )。 为了能够在比较String实例时受益于“ this == obj”的相等性检查,应将String值定义为文字字符串和/或字符串值常量表达式。 这样做,您的String实例将被JVM自动插入。 另一种但不受欢​​迎的方法是使用字符串 “ intern()”操作,以便手动插入字符串 。 正如Java文档中针对“ intern()”方法明确指出的那样,

    “最初为空的字符串池由String类私下维护。

    调用intern方法时,如果池已经包含等于equals(Object)方法确定的此String对象的字符串,则返回池中的字符串。

    否则,将此String对象添加到池中,并返回对此String对象的引用。

    因此,对于任何两个字符串s和t,当且仅当s.equals(t)为true时,s.intern()== t.intern()为true。

    所有文字字符串和字符串值常量表达式都将被嵌入。”

我们建议在处理String类时的最佳做法如下:

  1. 赞成创建文字字符串和字符串值常量表达式,而不是使用String构造函数方法之一创建新的String对象
  2. 利用字符数组执行String转换操作可获得最佳性能结果,但灵活性较差
  3. 在执行String转换操作(例如,删除,插入,替换或附加字符,连接或拆分String)时,请使用StringBuilder或StringBuffer类。 StringBuilder类在Java 1.5中引入,并且是StringBuffer类的非同步对等形式。 因此,如果只有一个线程将执行String转换操作,则倾向于使用StringBuilder类,因为它是性能最好的

模式优先精确字符串匹配

Java语言缺少快速的字符串搜索算法。 字符串 “ indexOf(…)”和“ lastIndexOf(…)”操作针对源文本对所提供的模式执行幼稚搜索。 天真的搜索基于“强力”模式第一个精确的字符串匹配算法。 “强力”算法包括检查文本中所有位置的模式是否从那里开始。 然后,每次尝试后,它都会将图案恰好向右移动一个位置。 但是,仍然存在其他几种算法,它们在速度和效率上都远胜过“蛮力”算法。

应用程序需要两种解决方案,具体取决于首先给出哪个字符串,模式或文本。 在我们的例子中,模式是预先提供的,这意味着我们总是针对未知文本搜索提供的模式。 对于需要全文搜索(文本优先精确字符串匹配)的所有应用程序,需要一套不同的算法来提供索引扫描。 Apache Lucene是实现后一种算法家族的最受欢迎的文本搜索引擎库之一。 尽管如此,本文只会研究第一种算法。

感谢这项伟大的工作,来自鲁昂大学信息学院的鲁昂大学Christian CharrasThierry Lecroq的一本书名为“ 精确字符串匹配算法 ”,我们得以用Java实现最精确的字符串匹配算法。 ,先给出模式。 下面的列表显示Christian CharrasThierry Lecroq所提供的算法名称,并在括号中显示我们的算法实现“代码名称”。 有关每种算法的更多信息,请单击相应的链接,以便重定向到“精确字符串匹配算法”一书的相关部分。

  • 蛮力算法 (BF)
  • 确定性有限自动机算法 (DFA)
  • Karp-Rabin算法 (KR)
  • 移位或算法 (SO)
  • Morris-Pratt算法 (MP)
  • Knuth-Morris-Pratt算法 (KMP)
  • 西蒙算法 (SMN)
  • Colussi算法 (CLS)
  • Galil-Giancarlo算法 (GG)
  • Apostolico-Crochemore算法 (AC)
  • 不太天真算法 (NSN)
  • Boyer-Moore算法 (BM)
  • Turbo BM算法 (TBM)
  • Apostolico-Giancarlo算法 (AG)
  • 反向Colussi算法 (RC)
  • Horspool算法 (HP)
  • 快速搜索算法 (QS)
  • 调优的Boyer-Moore算法 (BMT)
  • Zhu-Takaoka算法 (ZT)
  • Berry-Ravindran算法 (BR)
  • 史密斯算法 (SMT)
  • Raita算法 (RT)
  • 逆因子算法 (RF)
  • Turbo逆因子算法 (TRF)
  • 前向Dawg匹配算法 (FDM)
  • 后向不确定Dawg匹配算法 (BNDM)
  • 向后Oracle匹配算法 (BOM)
  • Galil-Seiferas算法 (GS)
  • 双向算法 (TW)
  • 有序字母算法中的字符串匹配 (SMOA)
  • 最佳失配算法 (OM)
  • 最大移位算法 (MS)
  • 跳过搜索算法 (SS)
  • KMP跳过搜索算法 (KPMSS)

在“精确字符串搜索算法”套件的初始版本(1.0.0)中,对于每种算法,我们都实现了三个实用程序操作:

  • compile(String pattern)–基于提供的模式执行所有必要预处理的静态操作
  • findAll(String source)–返回包含所有索引的列表,其中搜索算法指示有效的模式匹配
  • findAll(String pattern,String source)–这是一个辅助静态操作,封装了上述两个操作的功能

下面是使用Boyer-Moore算法(BM)的示例:

情况1

 BM bm = BM.compile(pattern);  List<Integer> idx = bm.findAll(source);  List<Integer> idx2 = bm.findAll(source2);  List<Integer> idx3 = bm.findAll(source3); 

情况#2

 List<Integer> idx = BM.findAll(pattern, source); 

在第一种情况下,我们编译模式并以两个不同的步骤执行搜索。 当我们必须在多个源文本中搜索同一模式时,此方法是合适的。 通过编译模式,由于预处理通常是繁重的操作,因此我们可以最大化性能结果。 另一方面,对于一次搜索,第二种方法提供了更方便的API。

我们必须指出我们提供的实现是线程安全的,并且当前我们不支持模式中的正则表达式。

以下是我们的精确字符串搜索算法套件的算法实现之间的示例性能比较。 我们使用65535个字符的完整字母大小搜索了1150000个字符的文本,以查找故意不存在的37个字符的短语。 请不要忘记这是一个相对的性能比较。 绝大多数提供的搜索算法的性能结果在很大程度上取决于提供的文本,提供的模式和字母大小。 因此,您应该只将String搜索算法之间的所有性能比较视为相对的。

在本节的开头,我们已经声明Java语言缺少快速的String搜索算法。 但是,与我们的算法套件相比,标准的Java天真的实现有多慢? 为了回答上述问题,我们实现了两种方法,以便使用标准Java API检索潜在模式匹配的所有索引值:

方法1 – indexOf()方法

 public static List<Integer> findAll(String pattern, String source) { List<Integer> idx = new ArrayList&ltInteger>(); int id = - 1 ; int shift = pattern.length(); int scnIdx = -shift; while (scnIdx != - 1 || id == - 1 ) { idx.add(scnIdx); id = scnIdx + shift; scnIdx = source.indexOf(pattern, id); } idx.remove( 0 ); return idx; } 

方法2 – Matcher find()方法

 public static List<Integer> findAll(String pattern, String source) { List<Integer> idx = new ArrayList&ltInteger>(); Pattern ptrn = Pattern.compile(pattern); Matcher mtch = ptrn.matcher(source); while (mtch.find()) idx.add(mtch.start());   return idx; } 

下面我们给出上述搜索算法之间的性能比较表

水平轴表示每种算法进行预处理和解析提供的文本所需的平均时间(以毫秒为单位)。 因此,较低的值更好。 如您所见,Java天真实现(indexOf()方法)以及几乎所有我们的搜索算法实现都优于Java Matcher“ find()”方法。 换句话说,当您处理中小型字符串搜索时,最好实现类似于我们上面提供的代码片段之类的东西,而不要使用更复杂的字符串搜索算法。 另一方面,处理大型文档时,我们套件中最快的算法之一肯定会派上用场!

您可以在此处下载精确字符串搜索算法套件发行版的1.0.0版

快乐编码

贾斯汀

相关文章 :
  • Java最佳实践–多线程环境中的DateFormat
  • Java最佳实践–高性能序列化
  • Java最佳实践– Vector vs ArrayList vs HashSet
  • Java最佳实践–队列之战和链接的ConcurrentHashMap
  • Java最佳实践– Char到Byte和Byte到Char的转换
相关片段:
  • 将String转换为字节数组UTF编码
  • 将字符串转换为字节数组ASCII编码
  • 使用indexOf方法搜索字符串
  • StringBuffer追加方法
  • StringTokenizer计数令牌
  • 使用StringTokenizer反转字符串

翻译自: https://www.javacodegeeks.com/2010/09/string-performance-exact-string.html

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

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

相关文章

keil里面填数据

编译后寄存器和堆栈的内存数据可以直接写进去的。 寄存器&#xff0c;双击就可以&#xff0c;注意里面是十六进制 堆栈&#xff0c;也是十六进制&#xff0c;八位的 00 00 00 00 &#xff0c;但这个是从右到左的&#xff0c;比如0x00000006 应该填 06 00 00 00 把数据取出来 取…

调试生产服务器– Eclipse和JBoss展示

您是否编写有错误的代码&#xff1f; 不&#xff0c;当然不。 对于我们其余的确实会编写带有错误的代码的凡人&#xff0c;我想解决一个非常敏感的问题&#xff1a;调试在生产服务器上运行的应用程序。 因此&#xff0c;您的应用程序已准备好进行部署。 单元测试全部成功&…

力扣151. 翻转字符串里的单词

给你一个字符串 s &#xff0c;逐个翻转字符串中的所有 单词 。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。 没思路&#xff0c;看到的官方给的&#xff0c;简洁明了&…

B/S和C/S架构图解

软件&#xff1a;B/S和C/S两种架构模式。接下来用三张图片解释&#xff0c;什么是B/S什么是C/S。 图片一&#xff1a;软件架构模式 图片二&#xff1a;C/S结构模式 图片三&#xff1a;B/S结构模式 相信图解胜过冗长文字的解释&#xff0c;什么是B/S什么是C/S一目了然。 转载于:…

HDU 5371 Hotaru's problem (Manacher,回文串)

题意&#xff1a;给一个序列&#xff0c;找出1个连续子序列&#xff0c;将其平分成前&#xff0c;中&#xff0c;后等长的3段子序列&#xff0c;要求【前】和【中】是回文&#xff0c;【中】和【后】是回文。求3段最长为多少&#xff1f;由于平分的关系&#xff0c;所以答案应该…

bash 与 dash

Ubuntu 的 bash和dash的区别 什么是bash &#xff1f; Bash(GNU Bourne-Again Shell)是许多Linux平台的内定Shell&#xff0c;事实上&#xff0c;还有许多传统UNIX上用的Shell&#xff0c;像tcsh、csh、ash、bsh、ksh等 等&#xff0c;Shell Script大致都类同&#xff0c;当您学…

Eclipse:如何附加Java源代码

在Eclipse中&#xff0c;当您按Ctrl按钮并单击任何类名称时&#xff0c;IDE会将您带到该类的源文件。 这是项目中具有的类的正常行为。 但是&#xff0c;如果您也希望Java核心类具有相同的行为&#xff0c;则可以通过将Java源代码附加到Eclipse IDE来实现。 一旦附加了源代码&a…

h.264 去块滤波

块效应及其产生原因 我们在观看视频的时候&#xff0c;在运动剧烈的场景常能观察到图像出现小方块&#xff0c;小方块在边界处呈现不连续的效果&#xff08;如下图&#xff09;&#xff0c;这种现象被称为块效应&#xff08;blocking artifact&#xff09;。 首先我们需要搞清楚…

DataNucleus 3.0与Hibernate 3.5

如官方产品站点所述&#xff0c; DataNucleus Access Platform是现有的最符合标准的开源Java持久性产品。 它完全符合JDO1 &#xff0c; JDO2 &#xff0c; JDO2.1 &#xff0c; JDO2.2 &#xff0c; JDO3 &#xff0c; JPA1和JPA2 Java标准。 它还符合OGC简单功能规范&#xf…

AngularJS 的常用特性(二)

3、列表、表格以及其他迭代型元素 ng-repeat可能是最有用的 Angular 指令了&#xff0c;它可以根据集合中的项目一次创建一组元素的多份拷贝。 比如一个学生名册系统需要从服务器上获取学生信息&#xff0c;目前先把模型之间定义在 JavaScript 代码里面&#xff1a; 1 var stud…

Ruby,Python和Java中的Web服务

今天&#xff0c;我不得不准备一些示例来说明Web服务是可互操作的。 因此&#xff0c;我已经使用Metro使用Java创建了一个简单的Web服务&#xff0c;并在Tomcat上启动了它。 然后尝试使用Python和Ruby消耗它们。 这是全部完成的过程… Java中的Web服务 我从Java中的简单Web服…

用动画切换按钮的状态

用动画切换按钮的状态 效果 源码 https://github.com/YouXianMing/UI-Component-Collection // // BaseControl.h // BaseButton // // Created by YouXianMing on 15/8/27. // Copyright (c) 2015年 YouXianMing. All rights reserved. //#import <UIKit/UIKit.h> c…

Java泛型快速教程

泛型是Java SE 5.0引入的一种Java功能&#xff0c;在其发布几年后&#xff0c;我发誓那里的每个Java程序员不仅听说过它&#xff0c;而且已经使用过它。 关于Java泛型&#xff0c;有很多免费和商业资源&#xff0c;而我使用的最佳资源是&#xff1a; Java教程 Java泛型和集合…

876. 链表的中间结点

给定一个头结点为 head 的非空单链表&#xff0c;返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点 代码一&#xff1a; 自己想的一个方法 class Solution {public ListNode middleNode(ListNode head) {ListNode p1 head;ListNode p2 head;//i,j…

Spark系列—02 Spark程序牛刀小试

一、执行第一个Spark程序 1、执行程序 我们执行一下Spark自带的一个例子&#xff0c;利用蒙特卡罗算法求PI&#xff1a; 启动Spark集群后&#xff0c;可以在集群的任何一台机器上执行一下命令&#xff1a; /home/spark/spark-1.6.1-bin-hadoop2.6/bin/spark-submit \ --class o…

JVM选项:-client vs -server

您是否曾经在运行Java应用程序时想知道-client或-server开关是什么&#xff1f; 例如&#xff1a; javaw.exe -client com.blogspot.sdoulger.LoopTest也显示在java.exe的“帮助”中&#xff0c;例如&#xff0c;其中的选项包括&#xff1a; -client选择“客户端” VM -serv…

3.1存储管理操作系统

存储器管理的对象是主存&#xff08;内存&#xff09;。其主要功能包含分配和回收主存空间、提高主存的利用率、扩充主存、对主存信息实现有效保护。存储器的结构为&#xff1a;寄存去、缓存、主存、外存。逻辑地址&#xff08;对用户角度。程序存放的位置&#xff09;、物理地…

了解和扩展Java ClassLoader

Java ClassLoader是项目开发中Java的关键但很少使用的组件之一。 就我个人而言&#xff0c;我从未在任何项目中扩展ClassLoader&#xff0c;但是拥有自己的可以自定义Java类加载的ClassLoader的想法让我感到很兴奋。 本文将概述Java类加载&#xff0c;然后继续创建自定义ClassL…

CAD教程-AL对其命令

AL可以实现不规则的对其功能 1.第一步按下AL&#xff0c;按下Enter 2.选择第一个源点 3.选择第一个目标点 4.选择第二个源点 5.选择第二个目标点 6.按下Enter&#xff0c;完成移位 转载于:https://www.cnblogs.com/weloveshare/p/4739873.html

(一二四)tableView的多组数据展示和手动排序

最近在写一个轻量级的网络游戏&#xff0c;遇到了技能优先顺序手动排序的需求&#xff0c;我就想到了iOS自带的tableView编辑功能&#xff0c;对其进行了初步探索&#xff0c;最后做出的效果如下图所示&#xff1a; 点击左边可以删除&#xff0c;拖住右边可以手动排序&#xff…