欢迎来到知了堂!
联系电话:知了堂-电话号码 028-62016472

解析JAVA字符串,以及字符串操作中常见优化方法-知了堂

解析JAVA字符串,以及字符串操作中常见优化方法

  String类型,是Java对char数组的进一步封装,占8个字节
  String类的实现主要由三部分组成:char数组,offset偏移量,String的长度。

  String类型有三个基本特点:

  1、不可变性
  不变性是指String对象一旦生成,则不能再对它进行改变。 在一个对象被多线程共享,而且被频繁的访问时,可以省略同步和锁的时间,从而提高性能。而String的不变性,可泛化为不变模式。
  不变性的作用在于当一个对象需要被多线程共享,并且频繁访问时,可以省略同步和锁等待的时间,从而大幅提高系统性能。
  2、针对常量池的优化
  当两个String对象拥有相同的值时,它们只引用常量池中的同一个拷贝。
  例如:
    String s1 = "123";
    String s2 = "123";
    String s3 = new String("123");
    System.out.println(s1 == s2); // true
    System.out.println(s1 == s3); // false
    System.out.println(s1 == s3.intern()); // true
 
  以上代码中,s1和s2引用的是相同的地址,故而第s1==s2是true;
  而s3虽然与s1,s2相等,但是s3时通过new String(“123”)创建的,重新开辟了内存空间,因引用的地址不同,
  所以第s1==s3是false;intern方法返回的是String对象在常量池中的引用,所以s1 == s3.intern()是true。
  3、被final修饰过
  作为final类的String对象在系统中不能有任何子类,正是因为这个,才保证了不可变。
  如下图,给一个已有字符串"abcd"第二次赋值成"abcedl",不是在原内存地址上修改数据,而是重新指向一个新对象,新地址。
给一个已有字符串"abcd"第二次赋值成"abcedl"
  为什么要被final修饰?
  1、不可变性支持线程安全。
  在并发场景下,多个线程同时读一个资源,是不会引发竟态条件的。只有对资源做写操作才有危险。不可变对象不能被写,所以线程安全。
  2、不可变性支持字符串常量池
  这样在大量使用字符串的情况下,可以节省内存空间,提高效率。但之所以能实现这个特性,String的不可变性是最基本的一个必要条件。要是内存里字符串内容能改来改去,这么做就完全没有意义。
String one = "someString";
String two = "someString";

 
  由于内存里面指向的是同一个地址,所以one==two 是true。
由于内存里面指向的是同一个地址,所以one==two 是true。

 

  2、字符串操作中的常见优化方法

  2.1 split()方法优化
  通常情况下,split()方法带给我们很大的方便,但是其性能不是很好。建议结合使用
  indexOf()和subString()方法进行自定义拆分,这样性能会有显著的提高。
  2.2 String常亮的累加操作优化方法
  示例代码:
String str = "";
           long strBeginTime = System.currentTimeMillis();
           for (int i = 0; i < 100000; i++) {
                str += "s";
           }
           long strEndTime = System.currentTimeMillis();
           System.out.println("str拼接100000遍s耗时: " +  (strEndTime - strBeginTime) + "ms");
           
           StringBuffer str1 = new StringBuffer();
           long str1BeginTime = System.currentTimeMillis();
           for (int i = 0; i < 100000; i++) {
                str1.append("s");
           }
           long str1EndTime = System.currentTimeMillis();
           System.out.println("str1拼接100000遍s耗时: " +  (str1EndTime - str1BeginTime) + "ms");
           
           StringBuilder str2 = new StringBuilder();
           long str2BeginTime = System.currentTimeMillis();
           for (int i = 0; i < 100000; i++) {
                str2.append("s");
           }
           long str2EndTime = System.currentTimeMillis();
           System.out.println("str2拼接100000遍s耗时: " +  (str2EndTime - str2BeginTime) + "ms");
  结果:
  str拼接100000遍s耗时: 3465ms
  str1拼接100000遍s耗时: 7ms
  str2拼接100000遍s耗时: 4ms
  所以,使用+号拼接字符串,其效率明显较低,而使用StringBuffer和StringBuilder的
  append()方法进行拼接,效率是使用+号拼接方式的百倍甚至千倍,而StringBuffer的效率
  比StringBuilder低些,这是由于StringBuffer实现了线程安全,效率较低也是不可避免的。
  所以在字符串的累加操作中,建议结合线程问题选择,应避免使用+号拼接字符串。
  2.3 StringBuffer和StringBuilder的选择
  上例中也使用过StringBuffer和StringBuilder了,两者只有线程安全方面的差别,所以呢,在无需考虑线程安全的情况下,建议使用性能相对较高的StringBuilder类,若系统要求线程安全,就选择StringBuffer类。
  2.4 基本数据类型转化为String类型的优化方案
  示例代码:
Integer num = 0;
           int count = 100000;
           long beginTime = System.currentTimeMillis();
           for (int i = 0; i < count; i++) {
                String s = num + "";
           }
           long endTime = System.currentTimeMillis();
           System.out.println("+号拼接的方式耗时: " + (endTime -  beginTime) + "ms");
           
           beginTime = System.currentTimeMillis();
           for (int i = 0; i < count; i++) {
                String s = String.valueOf(num);
           }
           endTime = System.currentTimeMillis();
           System.out.println("String.valueOf()的方式耗时: " +  (endTime - beginTime) + "ms");
           
           beginTime = System.currentTimeMillis();
           for (int i = 0; i < count; i++) {
                String s = num.toString();
           }
           endTime = System.currentTimeMillis();
           System.out.println("toString()的方式耗时: " + (endTime  - beginTime) + "ms");
  结果:
  +号拼接的方式耗时: 30ms
  String.valueOf()的方式耗时: 6ms
  toString()的方式耗时: 5ms
  以上示例中
  String.valueOf()直接调用了底层的Integer.toString()方法,会先判空;
  +号拼接由StringBuilder实现,先调用了append()方法,然后调用了toString()方法获取字符串;
  num.toString()直接调用了Integer.toString()方法,
  所以效率是:
  num.toString()方法最快,
  其次是String.valueOf(num),最后是加号拼接的方式。
  避免使用加号拼接的方式转换,最好是使用基本数据类型自带的toString()方法转换。
 
        版权声明:本文来源于网络,由知了堂搜集整理,仅供大家学习Java时使用