Java基础知识中字符串的使用场景的极其性能研究

Posted Ps_Q

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础知识中字符串的使用场景的极其性能研究相关的知识,希望对你有一定的参考价值。

今天闲来无事,来研究一些Java基础知识中字符串的使用场景的极其性能

先声明,这篇有点不适合新手,适合有基础的兄弟

首当其冲的是字符串类型String,StringBuffer,StringBuilder。

我们都知道String是不可变的StringBuffer和StringBuilder是可变的,但是其中的一些性能细节可能还没去测试过,我们可以看一下JDK里面三个类的构造方法
String定义属性和构造方法:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence 
    private final char value[];
 public String() 
        this.value = "".value;
        
    public String(String original) 
        this.value = original.value;
        this.hash = original.hash;
    
    public String(char value[]) 
        this.value = Arrays.copyOf(value, value.length);
    

StringBuilder源码:

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

    public StringBuilder() 
        super(16);
    
    public StringBuilder(int capacity) 
        super(capacity);
    
    public StringBuilder(String str) 
        super(str.length() + 16);
        append(str);
    

StringBuffer源码:

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

    private transient char[] toStringCache;
    public StringBuffer() 
        super(16);
    

从JDK源码看,String、StringBuilder、StringBuffer都是存放在char[] 数组字符串。但String 中定义的char[] 数组是用final 修饰,所以,String 是不可变字符序列,而StringBuilder和StringBuffer是可变字符序列;
所以Sting 需要改变则需要重新创建新对象;而StringBuffer 和 StringBuilder 都继承 AbstractStringBuilder类,他们在初始化时,都是调用父类的构造器。
那我们看一下AbstractStringBuilder类的源码:

abstract class AbstractStringBuilder implements Appendable, CharSequence 
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
    AbstractStringBuilder() 
    

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) 
        value = new char[capacity];
    

其实里面也是定义了一个char[]数组字符串。但AbstractStringBuilder 的char[]是可变的,所以StringBuffer和StringBuilder是可变的
网上有一张图,关于三种字符串类型的介绍,其实这些内容也可以在一些这几个类的常用方法中找出,有兴趣的话可以去看看这几个类的常用方法源码和扩展方法源码。

那么现在情况大概知道了,我们怎么去看到底这三个类是什么样的呢?是骡子是马拉出来溜溜。
这是我大晚上敲的还热乎着的代码。

public class StringTest 
 
  private static final int CODE = 100000;

  static private void basedOnString() 
    long beginTime = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < CODE; i++) 
      str = str + i;
    
    long endTime = System.currentTimeMillis();
    System.out.println("测试Sting执行时间:" + (endTime - beginTime));
  

  static private void baseOnStringBuffer() 
    long beginTime = System.currentTimeMillis();
    StringBuffer buffer = new StringBuffer("");
    for (int i = 0; i < CODE; i++) 
      buffer.append(i);
    
    long endTime = System.currentTimeMillis();
    System.out.println("测试StringBuffer执行时间:" + (endTime - beginTime));
  

  static private void baseOnStringBuilder() 
    long beginTime = System.currentTimeMillis();
    StringBuilder builder = new StringBuilder("");
    for (int i = 0; i < CODE; i++) 
      builder.append(i);
    
    long endTime = System.currentTimeMillis();
    System.out.println("测试StringBuilder执行时间:" + (endTime - beginTime));
  

  public static void main(String[] args) 
    System.out.println("测试字符类型赋值效率,遍历次数"+CODE+"次");
    basedOnString();
    baseOnStringBuffer();
    baseOnStringBuilder();
  

运行这段代码的最终结果是

测试字符类型赋值效率,遍历次数100000次
测试Sting执行时间:25390
测试StringBuffer执行时间:7
测试StringBuilder执行时间:5

总结:String是无法改变的,在更改值的时候会重新创建一个String, StringBuffer和StringBuilder是可以改变的,StringBuilder是最快的,但是StringBuffer线程是安全的,StringBuilder线程是不安全的,如果考虑到线程安全, 建议使用StringBuffer, 如果在单线程或者不考虑线程安全的情况下,可以使用StringBuilder追求效率。

但是有不少人发现在数量太少的情况下,StringBuilder 在 StringBuffer加锁的情况下,并没有体现出优势,反而StringBuffer 更胜一筹。
其实是因为StringBuffer比StringBuilder多了一个缓冲区,我们看下StringBuffer的toString方法:

@Override
    public synchronized String toString() 
        if (toStringCache == null) 
            toStringCache = Arrays.copyOfRange(value, 0, count);
        
        return new String(toStringCache, true);
    

再看看StringBuilder的toString方法:

 @Override
    public String toString() 
        // Create a copy, don't share the array
        return new String(value, 0, count);
    

其实可以发现StringBuffer的缓存有数据时,就直接在缓存区取,而StringBuilder每次都是直接copy。这样StringBuffer 相对StringBuilder来说其实是做了一个性能上的优化,所有只有当数量足够大,StringBuffer的缓冲区填补不了加锁影响的性能时,StringBuilder才在性能上展现出了它的优势。

差不多就是这么个情况了,白天测了测,晚上看源码不容易,早点睡觉,不然掉头发可就裂开了。

以上是关于Java基础知识中字符串的使用场景的极其性能研究的主要内容,如果未能解决你的问题,请参考以下文章

Java基础知识中字符串的使用场景的极其性能研究

Java基础知识中字符串的使用场景的极其性能研究

使用Swoole测试MySQL在特定SQL下的并发性能

JVM - 垃圾回收方式性能研究

JVM - 垃圾回收方式性能研究

JVM - 垃圾回收方式性能研究