博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
敲代码非常难之去除字符串的空白字符
阅读量:4986 次
发布时间:2019-06-12

本文共 4243 字,大约阅读时间需要 14 分钟。

在做性能调优时,用JProfiler測试Web应用的性能。发现有个replaceBlank函数占用了10%的CPU时间。进去看了下,是个简单的用正则去除XML文档里空白字符串的功能。可是这个简单功能却消耗了10%的性能。

在Web应用里。去掉空白字符串,似乎是个简单的功能,可是真正写起来。却也有些麻烦事。

总结下。

方式一:正則表達式

有两种写法:

s.replaceAll("\\s+", "");s.replaceAll("\\s", "");
至于详细哪一种比較好。和详细的场景有有关。

有连续空白字符串的选择每一种,假设是空白字符串都仅仅有一个的话,就选择另外一种。个人倾向于第一种。

正則表達式是比較慢的。比以下的方法要慢3到4倍以上。

方式二:org.springframework.util.StringUtils.trimAllWhitespace

详细的实现代码例如以下:

public static String trimAllWhitespace(String str) {		if (!hasLength(str)) {			return str;		}		StringBuilder sb = new StringBuilder(str);		int index = 0;		while (sb.length() > index) {			if (Character.isWhitespace(sb.charAt(index))) {				sb.deleteCharAt(index);			}			else {				index++;			}		}		return sb.toString();	}
看起来,没有什么问题,可是程序猿的直觉:deleteCharAt函数是怎么实现的?应该不会有什么高效的算法能够实现这种。

果然,实现代码例如以下:

public AbstractStringBuilder deleteCharAt(int index) {        if ((index < 0) || (index >= count))            throw new StringIndexOutOfBoundsException(index);        System.arraycopy(value, index+1, value, index, count-index-1);        count--;        return this;    }
显然,过多地调用System.arraycopy会有性能问题。

方式三:改为调用StringBuilder.append 函数

static public String myTrimAllWhitespace(String str) {		if (str != null) {			int len = str.length();			if (len > 0) {				StringBuilder sb = new StringBuilder(len);				for (int i = 0; i < len; ++i) {					char c = str.charAt(i);					if (!Character.isWhitespace(c)) {						sb.append(c);					}				}				return sb.toString();			}		}		return str;	}
这个是最開始的思路。

实际測试了下,发现大部分情况上。要例如式二效率高。

可是在某些情况,比方"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa",这样的仅仅有一个空白字符的,效率要慢。

方式四:结合二,三。仅仅用System.arraycopy复制部分内存

另外一种方式,在调用deleteAt时。要整个拷贝后面的全部字符串。显然在字符串非常长的情况下。效率会减少。于是考虑仅仅复制部分内存。

用两种pos来标记哪一部分是连续的非空白字符串。

static public String myTrimAllWhitespace3(String str) {		if (str != null) {			int len = str.length();			if (len > 0) {				char[] src = str.toCharArray();				char[] dest = new char[src.length];				int destPos = 0;				for (int pos1 = 0, pos2 = 0; pos2 < src.length;) {					if (Character.isWhitespace(src[pos2])) {						if (pos1 == pos2) {							pos1++;							pos2++;						} else {							System.arraycopy(src, pos1, dest, destPos, pos2									- pos1);							destPos += (pos2 - pos1);							pos2++;							pos1 = pos2;						}					} else {						pos2++;					}					if (pos2 == src.length) {						if (pos1 != pos2) {							System.arraycopy(src, pos1, dest, destPos, pos2									- pos1);							destPos += (pos2 - pos1);						}						return new String(dest, 0, destPos);					}				}			}		}		return str;	}

方式五:去掉StringBuilder。直接操作char[]

在写完方式四。之后,測试发现效率在中间,和方式二,三相比,不好也不坏。似乎找到了一个平衡点。

可是忽然想到。既然在方式四中不直接操作char[]数组,为何不在方式二也这么做?于是有了:

static public String myTrimAllWhitespace2(String str) {		if (str != null) {			int len = str.length();			if (len > 0) {				char[] dest = new char[len];				int destPos = 0;				for (int i = 0; i < len; ++i) {					char c = str.charAt(i);					if (!Character.isWhitespace(c)) {						dest[destPos++] = c;					}				}				return new String(dest, 0, destPos);			}		}		return str;	}

第六点:Unicode

上面的几种方式都仅仅能处理大部分的情况,对于部分Unicode字符串。可能会有问题。

由于本人对这个比較敏感,最后写了个Unicode字符的处理:

static public String myTrimAllWhitespace3(String str) {		if (str != null) {			int len = str.length();			if (len > 0) {				char[] src = str.toCharArray();				char[] dest = new char[src.length];				int destPos = 0;				for (int pos1 = 0, pos2 = 0; pos2 < src.length;) {					if (Character.isWhitespace(src[pos2])) {						if (pos1 == pos2) {							pos1++;							pos2++;						} else {							System.arraycopy(src, pos1, dest, destPos, pos2									- pos1);							destPos += (pos2 - pos1);							pos2++;							pos1 = pos2;						}					} else {						pos2++;					}					if (pos2 == src.length) {						if (pos1 != pos2) {							System.arraycopy(src, pos1, dest, destPos, pos2									- pos1);							destPos += (pos2 - pos1);						}						return new String(dest, 0, destPos);					}				}			}		}		return str;	}
这个处理Unicode的非常慢。

。Java的String类并没有暴露足够多的函数来处理Unicode,所以处理起来非常蛋疼。

总结:

測试代码在:

我的电脑上測试最快的代码是方式五里的。

可能在某些特殊情况下,方式四中用System.arraycopy来复制标记两段内存会快点,但这个算法太复杂了,得不偿失。

本人倾向于符合直觉。并且效率线性的算法。

给Spring提了个path,一開始是方式三的代码,可是在某些情况下效率不高,导致周末心神不宁。。

于是就有了后面的几种方式。

一个简单的功能,直正实现起来却也不easy。所以我尽量避免写Util类和方式,由于保证代码的质量,性能,不是一件easy的事。

转载于:https://www.cnblogs.com/lxjshuju/p/6944326.html

你可能感兴趣的文章
Leetcode 904. Fruit Into Baskets
查看>>
怎样连接REDIS服务端
查看>>
ajax同步,加载loading的bug
查看>>
秒杀多线程第二篇 多线程第一次亲密接触 CreateThread与_beginthreadex本质区别
查看>>
div滚动条
查看>>
iOS越狱程序开发
查看>>
调用其他类的方法
查看>>
SQlite数据库
查看>>
一些常用的js,jquerry 样例
查看>>
Oracle PL/SQL 多重选择句
查看>>
tyvj1659中中救援队
查看>>
kubernetes学习:CKA考试题
查看>>
LINUX samba的安装使用
查看>>
CSS border 生成三角
查看>>
asp.net(c#)开发中的文件上传组件uploadify的使用方法(带进度条)
查看>>
7.STM32中GPIO理解
查看>>
base64 json
查看>>
排序算法(一) —— 冒泡排序
查看>>
No.026:Remove Duplicates from Sorted Array
查看>>
SpringBoot项目的几种创建方式,启动、和访问
查看>>