高性能字符串判空

最近看内网看到一位大神写的关于几篇高性能处理字符串的帖子,自己总结了一下,主要是围绕如何进行高性能处理字符串判空的
如何高性能的进行字符串判空,这里面涉及两个会影响性能的问题

  1. 如何遍历字符串数组中的每个value
  2. 如何判断char中是否存在空格

问题一

基于JDK8源码中,如果使用charAt去遍历那么看charAt中的源码实现如下

public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

其中的每次会进行边界判断,这种效率肯定不高,那么还有如下两种方法可以采纳,反射或者Unsafe

反射

static Field valueField;
static {
    try {
        valueField = String.class.getDeclaredField("value");
        valueField.setAccessible(true);
    } catch (NoSuchFieldException ignored) {}
}
​
char[] chars = (char[]) valueField.get(str);

Unsafe

static long valueFieldOffset;
static {
    try {
        Field valueField = String.class.getDeclaredField("value");
        valueFieldOffset = UNSAFE.objectFieldOffset(valueField);
    } catch (NoSuchFieldException ignored) {}
}
​
char[] chars = (char[]) UNSAFE.getObject(str, valueFieldOffset);

经过对比发现Unsafe的效率要比Reflect好上3倍左右

问题二

如何判断char的值是否为’ ',\n,\r,\f,\t,\b,比较常见的处理方式就是下面几种
每次都要经过6次判断,性能不好

boolean space = ch == ' '
    || ch == '\n'
    || ch == '\r'
    || ch == '\f'
    || ch == '\t'
    || ch == '\b';

采用switch的方式,在JDK高版本中性能较好,但是在大量空字符串中,性能下降的很厉害

boolean space;
switch (ch) {
    case ' ':
    case '\n':
    case '\r':
    case '\t':
    case '\b':
    case '\f':
        space = true;
        break;
    default:
        space = false;
        break;
}

最终给出了一个采用位运算的方式进行判断,经过评测效率最好

public static void main(String[] args) throws Exception
	{
		char ch = '1';
		long SPACE = (1L << ' ') | (1L << '\n') | (1L << '\r') | (1L << '\f') | (1L << '\t') | (1L << '\b');
		boolean space = ch <= ' ' && ((1L << ch) & SPACE) != 0;
		System.out.println(space);

		char ch1 = ' ';
		boolean space1 = ch1 <= ' ' && ((1L << ch1) & SPACE) != 0;
		System.out.println(space1);
	}

之前在位运算涉及的不深,蒙的一看这段逻辑还是看不太懂,后面好好做了一下功课,下面解读一下这里面的几个小技巧
1.(1L << ' ')
细节一: 选择常量1
位运算中逻辑左移,转换为2进制的话就是常量✖️2的位移次方,那么位移本身其实并不重要,重要的是前面选择的常量,因为后面不管是2的几次方,那么在二进制的话,都只有一位为1的,那么前面选择常量1可以保证不管这个字符是什么,都只会在一个位置上出现
细节二:1为Long型
可以极大的保证位移后,二进制的位数
下面是\n的二进制数和用1左移后的二进制数

\n的二进制位===========
10 binary value is
0000000000000000000000000000000000000000000000000000000000001010
1024 binary value is
0000000000000000000000000000000000000000000000000000010000000000

2.(1L << ' ') | (1L << '\n') | (1L << '\r') | (1L << '\f') | (1L << '\t') | (1L << '\b')
每个字符在经过逻辑左移后,那么在按位与,可以保证最后的结果中,每个字符都只占一个二进制位,这种方式可以供后面的逻辑判断

' '的二进制位===========
32 binary value is
0000000000000000000000000000000000000000000000000000000000100000
4294967296 binary value is
0000000000000000000000000000000100000000000000000000000000000000
\n的二进制位===========
10 binary value is
0000000000000000000000000000000000000000000000000000000000001010
1024 binary value is
0000000000000000000000000000000000000000000000000000010000000000
\r的二进制位===========
13 binary value is
0000000000000000000000000000000000000000000000000000000000001101
8192 binary value is
0000000000000000000000000000000000000000000000000010000000000000
\f的二进制位===========
12 binary value is
0000000000000000000000000000000000000000000000000000000000001100
4096 binary value is
0000000000000000000000000000000000000000000000000001000000000000
\t的二进制位===========
9 binary value is
0000000000000000000000000000000000000000000000000000000000001001
512 binary value is
0000000000000000000000000000000000000000000000000000001000000000
\b的二进制位===========
8 binary value is
0000000000000000000000000000000000000000000000000000000000001000
256 binary value is
0000000000000000000000000000000000000000000000000000000100000000
按位与后的二进制数
4294981376 binary value is
0000000000000000000000000000000100000000000000000011011100000000
  1. ((1L << ch) & SPACE) != 0
    将要判断的字符使用同一个常量进行位移操作,然后按位与之前计算好的二进制位,按位与返回的结果为,匹配到位的和,那么不等于0就是匹配到了任意一个字符

  2. ch <= ' '
    一种小优化ascii码中空字符串的十进制最小既为空字符串,这样可以减少一些无意义的判断

最后要说位运算还是性能最高的,虽然看起来代码晦涩了一些

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值