zoukankan      html  css  js  c++  java
  • java中String StringBuilder StringBuffer比较和效率(性能)测试

    string stringbuilder stringbuffer三者的区别

    从JDK源码看,String、StringBuilder、StringBuffer都是存放在char[] 数组字符串。
    简单看下三者的部分源码:
    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);
        }
    
    

    比较明显的是:
    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];
        }
    
    

    可以看到 AbstractStringBuilder 其实也定义了char[] 数组,不同的是,AbstractStringBuilder 中的char[] 数组可以可变的,在细看一点,可以看到AbstractStringBuilder 有扩容的方法:

    private int newCapacity(int minCapacity) {
            // overflow-conscious code
            int newCapacity = (value.length << 1) + 2;
            if (newCapacity - minCapacity < 0) {
                newCapacity = minCapacity;
            }
            return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
                ? hugeCapacity(minCapacity)
                : newCapacity;
        }
    
        private int hugeCapacity(int minCapacity) {
            if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
                throw new OutOfMemoryError();
            }
            return (minCapacity > MAX_ARRAY_SIZE)
                ? minCapacity : MAX_ARRAY_SIZE;
        }
    
    

    接下来我们继续,看下String 、StringBuffer 和 StringBuilder的常用方法:
    String的常用方法:

    public String substring(int beginIndex) {
            if (beginIndex < 0) {
                throw new StringIndexOutOfBoundsException(beginIndex);
            }
            int subLen = value.length - beginIndex;
            if (subLen < 0) {
                throw new StringIndexOutOfBoundsException(subLen);
            }
            return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
        }
    
       
        public String substring(int beginIndex, int endIndex) {
            if (beginIndex < 0) {
                throw new StringIndexOutOfBoundsException(beginIndex);
            }
            if (endIndex > value.length) {
                throw new StringIndexOutOfBoundsException(endIndex);
            }
            int subLen = endIndex - beginIndex;
            if (subLen < 0) {
                throw new StringIndexOutOfBoundsException(subLen);
            }
            return ((beginIndex == 0) && (endIndex == value.length)) ? this
                    : new String(value, beginIndex, subLen);
        }
    

    StringBuilder的常用方法:

     @Override
        public StringBuilder append(int i) {
            super.append(i);
            return this;
        }
    
        @Override
        public StringBuilder append(long lng) {
            super.append(lng);
            return this;
        }
    
        @Override
        public StringBuilder append(float f) {
            super.append(f);
            return this;
        }
    

    StringBuffer的常用方法:

     @Override
        public synchronized StringBuffer append(CharSequence s, int start, int end)
        {
            toStringCache = null;
            super.append(s, start, end);
            return this;
        }
    
        @Override
        public synchronized StringBuffer append(char[] str) {
            toStringCache = null;
            super.append(str);
            return this;
        }
    
    

    从它们的常用方法可以看出:
    String 每次返回的都是新字符串,所以我们使用String的方法操作字符串后不影响原来的字符串;
    StringBuffer 和 StringBuilder 返回的都是this,也就是对象本身,所有我们可以在代码中连着写append(xx).append(xxx).append(xxx);
    不同的是StringBuffer的方法就加了synchronized 也就是我们说的线程安全。
    总结一下:

     

    String StringBuilder StringBuffer效率(性能)测试

    我们通过各自拼接10000字符串来比较一下三者在执行时对时间和对内存资源的占用。
    下面是测试代码:

    package com.xzlf.string;
    
    public class TestString {
    	public static void main(String[] args) {
    		// 使用 String 进行字符拼接
    		String str = "";
    		long num1 = Runtime.getRuntime().freeMemory();// 获取系统剩余内存空间
    		long time1 = System.currentTimeMillis();
    		for (int i = 0; i < 10000; i++) {
    			str += i; // 相当于产生了5000个对象
    		}
    		long num2 = Runtime.getRuntime().freeMemory();
    		long time2 = System.currentTimeMillis();
    		System.out.println("String 占用了内存:" + (num1 - num2));
    		System.out.println("String 占用了时间:" + (time2 - time1));
    		
    		// 使用 StringBuilder 进行字符串拼接
    		StringBuilder sb = new StringBuilder("");
    		long num3 = Runtime.getRuntime().freeMemory();
    		long time3 = System.currentTimeMillis();
    		for (int i = 0; i < 10000; i++) {
    			sb.append(i);
    		}
    		long num4 = Runtime.getRuntime().freeMemory();
    		long time4 = System.currentTimeMillis();
    		System.out.println("StringBuilder 占用了内存:" + (num3 - num4));
    		System.out.println("StringBuilder 占用了时间:" + (time4 - time3));
    		
    		// 使用 StringBuilder 进行字符串拼接
    		StringBuffer sb2 = new StringBuffer("");
    		long num5 = Runtime.getRuntime().freeMemory();
    		long time5 = System.currentTimeMillis();
    		for (int i = 0; i < 10000; i++) {
    			sb2.append(i);
    		}
    		long num6 = Runtime.getRuntime().freeMemory();
    		long time6 = System.currentTimeMillis();
    		System.out.println("StringBuffer 占用了内存:" + (num5 - num6));
    		System.out.println("StringBuffer 占用了时间:" + (time6 - time5));
    		
    	}
    }
    
    

    以上代码运行结果为:
    在这里插入图片描述
    可以看到,String创建了大量无用对象,消耗了大量内存耗时上大概是StringBuffer 和 builder的100倍。

    当然,我们只循环了10000次,StringBuilder的优势不是很明显,为了防止java 虚拟机 jvm 垃圾回收机制的干扰 我们我StringBuilder 和 StringBuffer 单独拿出来吧循环次数加到10万次、100万次和1000万次测试:
    代码吧String部分注释掉,由于循环次数较多,jvm 在运行时会有垃圾回收,内存对比会不正确,也先注释:

    package com.xzlf.string;
    
    public class TestString {
    	public static void main(String[] args) {
    		// 使用 String 进行字符拼接
    //		String str = "";
    //		long num1 = Runtime.getRuntime().freeMemory();// 获取系统剩余内存空间
    //		long time1 = System.currentTimeMillis();
    //		for (int i = 0; i < 10000; i++) {
    //			str += i; // 相当于产生了5000个对象
    //		}
    //		long num2 = Runtime.getRuntime().freeMemory();
    //		long time2 = System.currentTimeMillis();
    //		System.out.println("String 占用了内存:" + (num1 - num2));
    //		System.out.println("String 占用了时间:" + (time2 - time1));
    		
    		// 使用 StringBuilder 进行字符串拼接
    		StringBuilder sb = new StringBuilder("");
    		long num3 = Runtime.getRuntime().freeMemory();
    		long time3 = System.currentTimeMillis();
    		for (int i = 0; i < 10000000; i++) {
    			sb.append(i);
    		}
    		long num4 = Runtime.getRuntime().freeMemory();
    		long time4 = System.currentTimeMillis();
    //		System.out.println("StringBuilder 占用了内存:" + (num3 - num4));
    		System.out.println("StringBuilder 占用了时间:" + (time4 - time3));
    		
    		// 使用 StringBuilder 进行字符串拼接
    		StringBuffer sb2 = new StringBuffer("");
    		long num5 = Runtime.getRuntime().freeMemory();
    		long time5 = System.currentTimeMillis();
    		for (int i = 0; i < 10000000; i++) {
    			sb2.append(i);
    		}
    		long num6 = Runtime.getRuntime().freeMemory();
    		long time6 = System.currentTimeMillis();
    //		System.out.println("StringBuffer 占用了内存:" + (num5 - num6));
    		System.out.println("StringBuffer 占用了时间:" + (time6 - time5));
    		
    	}
    }
    
    

    我这边测试10万次结果为:
    在这里插入图片描述
    100万次结果为:
    在这里插入图片描述
    1000万次结果为:
    在这里插入图片描述
    在数量太少的情况下,StringBuilder 在StringBuffer加锁的情况下,并没有体现出优势,反而StringBuffer 更胜一筹。
    这种情况相信很多测试过的小伙伴也应该遇到过???

    对于这种情况,其实也不难理解,append的操作本质还是操作char[] 数组,我们还是继续看源码,
    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才在性能上展现出了它的优势

    重视基础,才能走的更远。
  • 相关阅读:
    ServletContext的应用
    behavior planning——11 create a cost function speed penalty
    behavior planning——10 behaior planning pseudocode
    behavior planning——inputs to transition functions
    卡尔曼滤波——20.卡尔曼预测
    卡尔曼滤波——19.卡尔曼滤波器代码
    卡尔曼滤波——18.预测函数
    卡尔曼滤波——17.高斯移动
    卡尔曼滤波——16.新的均值和方差
    卡尔曼滤波——14.—15分离的高斯分布
  • 原文地址:https://www.cnblogs.com/xzlf/p/12681547.html
Copyright © 2011-2022 走看看