zoukankan      html  css  js  c++  java
  • String、StringBuffer、StringBuilder

    java基础解析系列--String、StringBuffer、StringBuilder

    前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bug的时候,才能处理更加从容。

    String

    ==问题

           String s6=new String("jiajun");
            String s1="jiajun";
            String s2="jiajun";
            System.out.println(s1==s2);//true
            System.out.println(s1==s6);//false
    • 看常量池中是否已有此字符串,如果有,将指针指向这个字符串
    • 如果使用new来创建字符串对象,那么这个字符串是存放在堆中,无论堆中是否已有这个对象

    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 concat(String str) {
            int otherLen = str.length();
            if (otherLen == 0) {
                return this;
            }
            int len = value.length;
            char buf[] = Arrays.copyOf(value, len + otherLen);
            str.getChars(buf, len);
            return new String(buf, true);
        }
     
    • 从源码可以看出,任何操作都是创建一个新的对象,不影响原对象

    StringBuffer和StringBuidler

    初始容量

    • StringBuilder和StringBuffer的构造参数来初始化容量
    public StringBuilder() {
            super(16);
        }
    AbstractStringBuilder(int capacity) {
            value = new char[capacity];
        }
     
    • 默认情况下容量为16
    public AbstractStringBuilder append(String str) {
            if (str == null)
                return appendNull();
            int len = str.length();
            ensureCapacityInternal(count + len);
            str.getChars(0, len, value, count);
            count += len;
            return this;
        }
        private void ensureCapacityInternal(int minimumCapacity) {
            // overflow-conscious code
            if (minimumCapacity - value.length > 0) {
                value = Arrays.copyOf(value,
                        newCapacity(minimumCapacity));
            }
        }
        public static char[] copyOf(char[] original, int newLength) {
            char[] copy = new char[newLength];
            System.arraycopy(original, 0, copy, 0,
                             Math.min(original.length, newLength));
            return copy;
        }
     
    • 从源码看出,在执行append方法的时候,会执行ensureCapacityInternal方法来保证容量,而如果超出容量的话,会重新创建一个char数组,并将旧的字符数组复制到新的字符数组

    线程安全

    public synchronized StringBuffer append(StringBuffer sb) {
            toStringCache = null;
            super.append(sb);
            return this;
        }
     public synchronized StringBuffer append(String str) {
            toStringCache = null;
            super.append(str);
            return this;
        }
        
    
     public StringBuilder append(StringBuffer sb) {
            super.append(sb);
            return this;
        }
        public StringBuilder append(String str) {
            super.append(str);
            return this;
        }
        public String toString() {
            // Create a copy, don't share the array
            return new String(value, 0, count);
        }public synchronized StringBuffer append(StringBuffer sb) {
            toStringCache = null;
            super.append(sb);
            return this;
        }
     public synchronized StringBuffer append(String str) {
            toStringCache = null;
            super.append(str);
            return this;
        }
        
    
     public StringBuilder append(StringBuffer sb) {
            super.append(sb);
            return this;
        }
        public StringBuilder append(String str) {
            super.append(str);
            return this;
        }
        public String toString() {
            // Create a copy, don't share the array
            return new String(value, 0, count);
        }
    • 可以看出,String的方法是加了synchronzied,也就加了锁,那么而在单线程的情况下或者不用考虑线程安全的情况下,那么StringBuilder的性能是更高的

    toString方法

     public synchronized String toString() {
            if (toStringCache == null) {
                toStringCache = Arrays.copyOfRange(value, 0, count);
            }
            return new String(toStringCache, true);
        }
    public String toString() {
            // Create a copy, don't share the array
            return new String(value, 0, count);
        }
     
    • 通过源码发现,toString方法会创建一个新的String对象

    性能试验

    间接相加和直接相加

    public class d {
        public static void main(String[] args) {
            String s="I"+"love"+"jiajun";
            String s1="I";
            String s2="love";
            String s3="jiajun";
            String s4=s1+s2+s3;
        }
    }
     

    • 通过反编译的结果可以看出,第一种方式字符串直接相加,在编译器就直接优化了”Ilovejiajun“
    • 而第二种方式间接相加,从结果可以看出,是先创建一个StringBuilder,然后再apend,最后再toString方法,可以发现性能比第一种低
    public class d {
        public static void main(String[] args) {
            String s="I"+"love"+"jiajun";
            String s1="I";
            String s2=s1+"lovejiajun";
            System.out.println(s==s2);
        }
    }
     

    • 同样从反编译的结果可以看出,第二种方式并没有被优化,也是通过StringBuilder来实现的,最后通过toString方法创建一个String对象,所以返回的false
    • 但是当s1是用final修饰的却是不一样的,虚拟机会对其进行优化,所以不会像之前一样创建一个StringBuilder,最后在堆中产生一个对象
    public class d {
        public static void main(String[] args) {
            
            final String s1="I";
            
            String s2=s1+"lovejiajun";
            String s3="Ilovejiajun";
            //s1==s3
        }
    }
     

    用+和用append

    public class Demo3 {
        public static void main(String[] args) {
            run1();
            run2();
        }
        
        public static void run1() {
            long start = System.currentTimeMillis();
            String result = "";
            for (int i = 0; i < 10000; i++) {
                result += i;
            }
            System.out.println(System.currentTimeMillis() - start);
        }
    
        public static void run2() {
             long start = System.currentTimeMillis();
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < 10000; i++) {
                builder.append(i);
            }
            System.out.println(System.currentTimeMillis() - start);
        }
    
        //输出:223 1
    
    • 从实验发现,用append效率更高,从实验一发现,当字符串相加的时候,实际上每次都会重新初始化StringBuilder然后执行相加,这样效率并不高

    初始化容量

    public class Demo3 {
        public static void main(String[] args) {
            test1();
            test2();
        }
    
        public static void test1() {
            StringBuilder sb = new StringBuilder(7000000);
            long start = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                sb.append("jiajun");
            }
            long end=System.currentTimeMillis()-start;
            System.out.println(end);
        }
    
        public static void test2() {
    
            StringBuilder sb = new StringBuilder();
            long start = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                sb.append("jiajun");
            }
            long end=System.currentTimeMillis()-start;
            System.out.println(end);
        }
        //输出:18 26
    }
     
    • 通过实验可以看出,适当的初始化容量可以提高性能,因为当不初始化容量的时候,如果此时append超出容量,那么将会从新创建一个char数组,并且进行复制

    总结

    • 用new创建对象的时候,会在堆中创建对象,而如果是直接用引号形式的话,会先看常量池是否有此字符串,有的话指向常量池的字符串
    • StringBuilder是非线程安全的,StringBuffer是线程安全的
    • 使用StringBuilder和StringBuffer的时候最好初始化一个合适的容量,因为如果默认容量不够的话,会重新创建一个char数组,再进行复制
    • 字符串相加的时候,直接相加的时候,编译器会进行优化,而如果是间接相加的时候,实际上会创建一个StringBuilder来进行append

    例如:

    StringTest.java

    public class StringTest {
    
        public static void main(String[] args) {
            String s = "111111" + "2222222" + "33333333333";
            String s2=s+"4444";
            s=s+"555";
        }
    }

    编译之后:StringTest.class

    public class StringTest
    {
      public static void main(String[] paramArrayOfString)
      {
        String str1 = "111111222222233333333333";
        String str2 = str1 + "4444";
        str1 = str1 + "555";
      }
    }

     JavaP反编译字节码文件:

    C:UsersliqiangDesktop>javap -c StringTest.class
    Compiled from "StringTest.java"
    public class StringTest {
      public StringTest();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":
    ()V
           4: return
    
      public static void main(java.lang.String[]);
        Code:
           0: ldc           #2                  // String 111111222222233333333333
           2: astore_1
           3: new           #3                  // class java/lang/StringBuilder
           6: dup
           7: invokespecial #4                  // Method java/lang/StringBuilder."<
    init>":()V
          10: aload_1
          11: invokevirtual #5                  // Method java/lang/StringBuilder.ap
    pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          14: ldc           #6                  // String 4444
          16: invokevirtual #5                  // Method java/lang/StringBuilder.ap
    pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          19: invokevirtual #7                  // Method java/lang/StringBuilder.to
    String:()Ljava/lang/String;
          22: astore_2
          23: new           #3                  // class java/lang/StringBuilder
          26: dup
          27: invokespecial #4                  // Method java/lang/StringBuilder."<
    init>":()V
          30: aload_1
          31: invokevirtual #5                  // Method java/lang/StringBuilder.ap
    pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          34: ldc           #8                  // String 555
          36: invokevirtual #5                  // Method java/lang/StringBuilder.ap
    pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          39: invokevirtual #7                  // Method java/lang/StringBuilder.to
    String:()Ljava/lang/String;
          42: astore_1
          43: return
    }
  • 相关阅读:
    man arch
    封装 pyinstaller -F -i b.ico excel.py
    Python比较两个excel文档内容的异同
    运维工具
    python封装成exe
    OCP内容
    OCP
    操作系统
    转:铁大树洞APP视频讲解和原型演示
    2020.3.31——针对超能陆战队铁大树洞项目的匿名特点分析
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/7492786.html
Copyright © 2011-2022 走看看