qt正则表达式截取字符串_Java性能调优002聊聊字符串

本文探讨Java中String对象的优化,包括历史优化过程、开发手册的建议,如避免split方法的潜在风险和使用StringBuilder。同时提及Qt中的正则表达式用于字符串截取,强调在特定场景下选择合适的数据结构和方法来提升性能。
摘要由CSDN通过智能技术生成

前言:


我们知道,String对象作为Java语言中重要的数据类型,是内存中占据空间最大的一个对象。高效地使用字符串,可以提升系统的整体性能。

今天就继续站在金山刘超大佬的肩膀上和大家分享一下,针对咱们日常开发过程中使用最频繁的String字符串,可以从哪些方面进行优化。

String对象的实现


String对象的历史优化过程:

40389a9469412231cbc968cced3776d3.png

  • Java6

1.在Java6以及之前的版本中,String对象是对char数组进行了封装实现的对象,主要有四个成员变量:char数组、偏移量offset、字符数量count、哈希值hash。String对象是通过offset和count两个属性来定位char[]数组,获取字符串。这么做可以高效、快速地共享数组对象,同时节省内存空间,但这种方式很有可能会导致内存泄漏。如果你有一个很长很长的字符串,但是当你使用substring进行切割的时候你只需要很短的一段。这可能导致性能问题,因为你需要的只是一小段字符序列,但是你却引用了整个字符串(因为这个非常长的字符数组一直在被引用,所以无法被回收,就可能导致内存泄露)。  在JDK 6中,一般用以下方式来解决该问题,原理其实就是生成一个新的字符串并引用他。  x = x.substring(x, y) + ""
  • Java7~Java8

2.从Java7版本开始到Java8版本,Java对String类做了一些改变。String类中不再有offset和count两个变量了。这样的好处是String对象占用的内存稍微少了些,同时,String.substring方法也不再共享char[],从而解决了使用该方法可能导致的内存泄漏问题。
  • Java9~

3.从Java9版本开始,工程师将char[]字段改为了byte[]字段,又维护了一个新的属性coder,它是一个编码格式的标识。工程师为什么这样修改呢?我们知道一个char字符占16位,2个字节。这个情况下,存储单字节编码内的字符(占一个字节的字符)就显得非常浪费。JDK1.9的String类为了节约内存空间,于是使用了占8位,1个字节的byte数组来存放字符串。而新属性coder的作用是,在计算字符串长度或者使用indexOf()函数时,我们需要根据这个字段,判断如何计算字符串长度。coder属性默认有0和1两个值,0代表Latin-1(单字节编码),1代表UTF-16。如果String判断字符串只包含了Latin-1,则coder属性值为0,反之则为1。

String对象的优化


说到String对象的优化,首先我们来看一下阿里巴巴Java开发手册中关于String字符串提到的两点建议:

  • a.当使用索引访问用String的split方法得到的数组时,需在最后一个分隔符后做有无内容的检查,否则会有抛出IndexOutOfBoundsException的风险。

说明:String str = "alb,c,,";String[] ary = str.split(",");//预期大于3,结果是3System.out.println(ary.length);

Split()方法使用了正则表达式实现了其强大的分割功能,而正则表达式的性能是非常不稳定的,使用不恰当会引起回溯问题,很可能导致CPU居高不下,所以,我们建议不要使用split()方法对字符串进行操作。

补充:split有两种情况不会使用正则表达式:第一种为传入的参数长度为1,且不包含“.$|()[{^?*+\\”regex元字符的情况下,不会使用正则表达式;第二种为传入的参数长度为2,第一个字符是反斜杠,并且第二个字符不是ASCII数字或ASCII字母的情况下,不会使用正则表达式。
  • b.在循环体内,字符串的连接方式使用StringBuilder的append方法进行扩展。

String str = "abcdef";for(int i=0; i<1000; i++) {      str = str + i;}说明:反编译的字节码文件显示每次循环都会new出一个StringBulider对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源的浪费。String str = "abcdef";for(int i=0; i<1000; i++) {            str = (new StringBuilder(String.valueOf(str))).append(i).toString();}
  • C.使用String.intern节省内存(慎重使用)

    参考:https://www.javazhiyin.com/41778.html  JVM 解剖公园(10): String.intern()

在字符串常量中,默认会将对象放入常量池;在字符串变量中,对象是会创建在堆内存中,同时也会在常量池中创建一个字符串对象,复制到堆内存对象中,并返回堆内存对象引用。如果调用intern方法,会去查看字符串常量池中是否有等于该对象的字符串,如果没有,就在常量池中新增该对象,并返回该对象引用;如果有,就返回常量池中的字符串引用。堆内存中原有的对象由于没有引用指向它,将会通过垃圾回收器回收。String a =new String("abc").intern();String b = new String("abc").intern();      if(a==b) {    System.out.print("a==b");//a==b}

Tips:


  • 字符串的比较

String中的equals()方法底层源码:public boolean equals(Object anObject) {   //调用该方法的对象与比较的参数相等,则返回true   if (this == anObject) {       return true;   }   //instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例   if (anObject instanceof String) {       String anotherString = (String)anObject;       int n = value.length;       //首先比较两个字符串实例长度是否相等       if (n == anotherString.value.length) {           char v1[] = value;           char v2[] = anotherString.value;           int i = 0;           //比较两个字符串对应位置的字符是否相等           while (n-- != 0) {               if (v1[i] != v2[i])                   return false;               i++;           }           return true;       }   }   return false;}“==”操作符用来比较操作数的值之间的关系,对于基本数据类型,变量存储的是值本身,而对于引用数据类型,变量存储的是对象的地址,比较的是两个对象的内存地址是否相等。
  • String、StringBuffer与StringBuilder

    • 如果要操作少量的数据用String

    • 单线程操作字符串缓冲区 下操作大量数据用StringBuilder,会抛出    ArrayIndexOutOfBoundsException异常

    • 多线程操作字符串缓冲区 下操作大量数据用StringBuffer

知识点:为什么会抛出ArrayIndexOutOfBoundsException异常?ensureCapacityInternal()方法是检查StringBuilder对象的原char数组的容量能不能盛下新的字符串,如果盛不下就调用expandCapacity()方法对char数组进行扩容。private void ensureCapacityInternal(int minimumCapacity) {        // overflow-conscious code    if (minimumCapacity - value.length > 0)        expandCapacity(minimumCapacity);}扩容的逻辑就是new一个新的char数组,新的char数组的容量是原来char数组的两倍再加2,再通过System.arryCopy()函数将原数组的内容复制到新数组,最后将指针指向新的char数组。void expandCapacity(int minimumCapacity) {    //计算新的容量    int newCapacity = value.length * 2 + 2;    //中间省略了一些检查逻辑    ...    value = Arrays.copyOf(value, newCapacity);}
  • String.trim()

参考:https://www.javazhiyin.com/43083.htmlString str = "ab";String str1 = (" a"+"b ").trim();String str2 = ("a"+"b").trim();System.out.println(str==str1);//str1因为两边有空格,所以调用trim()方法时,内部的substring()方法将会将截取的部分new成一个String对象,所以str==str1为false,因为一个指向常量池中的值,一个指向堆中的对象,地址不同System.out.println(str==str2);//而str2因为两边并没有空格,所以trim()方法直接返回原对象,所以str==str2为true,因为两个都是指向常量池中的值,且常量池中的值是唯一的,所以str和str2都指向常量池中ab的值,地址相同。

总结:


别人笑我太疯癫,我笑他人看不穿。一个人的旅行✈️是孤独的~09f3b3f4c3c36216c93e0a90126fd29d.png

周末去了一趟厦门,和大家分享一下,这两天的vlog~f672b3298ffb7215b40fc44e9622b85d.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值