String、StringBuilder文字列の接続効率とStringBuilderとStringBufferスレッドのセキュリティの比較

8184 ワード

一、文字列接続の効率問題
Stringを使用して文字列を接続する場合、なぜ遅いですか?
小知識点
JAvaで配列を初期化すると、その配列が占めるメモリ空間、配列長は可変ではありません.
文字列を作成し、文字列オブジェクトにメモリ空間を割り当てると、一定の時間(CPU)と空間(メモリ)の代価がかかり、最も基礎的なデータ型として文字列が大量に頻繁に作成され、プログラムの性能に大きく影響します.
無駄な中間オブジェクトが多すぎる
文字列を接続するたびに新しいStringオブジェクトが作成され、接合回数が増えるにつれてこのオブジェクトはますます大きくなります.例えば、100回の接合を行うには、100個のStringオブジェクトを作成する必要があります.
StringBuilderは接続時になぜ効率が高いのですか?
文字配列の拡張メカニズム:
private void ensureCapacityInternal(int minimumCapacity) {
         //       minimumCapacity          
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
    
private int newCapacity(int minCapacity) {
         //          newCapacity
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        //              
        if (newCapacity - minCapacity < 0) {
            //             minimumCapacity
            newCapacity = minCapacity;
        }
        // newCapacity    ,newCapacity               MAX_ARRAY_SIZE    。
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

    private int hugeCapacity(int minCapacity) {
        //       minCapacity  Integer.MAX_VALUE         
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        //   minCapacity  MAX_ARRAY_SIZE Integer.MAX_VALUE  ,      minCapacity,      MAX_ARRAY_SIZE      。
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }

元のStringBuilderオブジェクトに文字列を追加する場合:
1.追加対象strがnullの場合は'null'文字を追加
2.拡張操作が必要か確認する
2.1       minimumCapacity          ,                        。  
2.2          newCapacity,newCapacity = (value.length * 2) + 2。  
2.3                ,                   minimumCapacity。  
2.4 newCapacity    ,newCapacity               MAX_ARRAY_SIZE    。        ,      minCapacity  Integer.MAX_VALUE         ,  minCapacity  MAX_ARRAY_SIZE Integer.MAX_VALUE  ,      minCapacity,      MAX_ARRAY_SIZE      。  

3.str.getChars()strをvalueの末尾に追加
効率が高い理由
  • 拡張メカニズムは、拡張条件minimumCapacity - value.length > 0を満たす場合にのみ拡張を行い、新しい配列を生成することを保証しているため、ほとんどの場合、元の配列を操作し、不要なchar[]オブジェクトの発生を回避し、システムリソースのオーバーヘッドを節約する.

  • コード#コード#
    /**
     *          
     *
     * @Author: lingyejun
     * @Date: 2019/8/17
     * @Describe:
     * @Modified By:
     */
    public class LinkCompare {
    
        /**
         *        
         *
         * @param times
         */
        public static void linkByString(int times) {
    
            Long startTime = System.currentTimeMillis();
    
            String initStr = "";
            for (int i = 0; i < times; i++) {
                initStr = initStr + i;
            }
    
            Long endTime = System.currentTimeMillis();
    
            System.out.println("String    " + times + "     :" + (endTime - startTime) + "ms");
        }
    
        /**
         *   StringBuilder     
         *
         * @param times
         */
        public static void linkByStringBuilder(int times) {
    
            Long startTime = System.currentTimeMillis();
    
            StringBuilder initStr = new StringBuilder();
            for (int i = 0; i < times; i++) {
                initStr.append(i);
            }
    
            Long endTime = System.currentTimeMillis();
    
            System.out.println("StringBuilder    " + times + "     :" + (endTime - startTime) + "ms");
        }
    
    
        /**
         *   StringBuffer     
         *
         * @param times
         */
        public static void linkByStringBuffer(int times) {
    
            Long startTime = System.currentTimeMillis();
    
            StringBuffer initStr = new StringBuffer();
            for (int i = 0; i < times; i++) {
                initStr.append(i);
            }
    
            Long endTime = System.currentTimeMillis();
    
            System.out.println("StringBuffer    " + times + "     :" + (endTime - startTime) + "ms");
        }
    
    
        public static void main(String[] args) {
    
            // 100000000
            linkByStringBuilder(40000);
            //-XX:+PrintGCDetails
            //linkByString(40000);
    
        }
    }
    

    二、StringBuilderとString Bufferのスレッド安全比較
    StringBufferのスレッドセキュリティの検証
    スレッドが安全でない理由
    public StringBuilder append(String str) {
            super.append(str);
            return this;
        }
        
    public synchronized StringBuffer append(String str) {
            toStringCache = null;
            super.append(str);
            return this;
        }

    テストコード
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * StringBuilder StringBuffer     
     *
     * @Author: lingyejun
     * @Date: 2019/8/17
     * @Describe:
     * @Modified By:
     */
    public class SecurityCompare {
    
        public void stringBuilderTest() {
    
            //    StringBuilder
            StringBuilder stringBuilder = new StringBuilder();
    
            // joinList
            List joinList = new ArrayList<>();
    
            //       
            for (int i = 0; i < 1000; i++) {
                StringBuilderThread sbt = new StringBuilderThread(stringBuilder);
                sbt.start();
                joinList.add(sbt);
            }
    
            //   append             
            for (StringBuilderThread thread : joinList) {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            //        
            System.out.println("StringBuilder   append   : " + stringBuilder.length());
        }
    
        public void stringBufferTest() {
    
            //    StringBuffer
            StringBuffer stringBuffer = new StringBuffer();
    
            // joinList
            List joinList = new ArrayList<>();
    
            //       
            for (int i = 0; i < 1000; i++) {
                StringBufferThread sbf = new StringBufferThread(stringBuffer);
                sbf.start();
                joinList.add(sbf);
            }
    
            //   append             
            for (StringBufferThread thread : joinList) {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            //        
            System.out.println("StringBuffer   append   : " + stringBuffer.length());
        }
    
    
        public static void main(String[] args) {
    
            SecurityCompare securityCompare = new SecurityCompare();
    
            securityCompare.stringBuilderTest();
            securityCompare.stringBufferTest();
    
        }
    
        public static class StringBuilderThread extends Thread {
    
            private StringBuilder stringBuilder;
    
            public StringBuilderThread(StringBuilder stringBuilder) {
                this.stringBuilder = stringBuilder;
            }
    
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                stringBuilder.append("a");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    
        private static class StringBufferThread extends Thread {
    
            private StringBuffer stringBuffer;
    
            public StringBufferThread(StringBuffer stringBuffer) {
                this.stringBuffer = stringBuffer;
            }
    
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                stringBuffer.append("a");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    }
    

    三、結論
    1.Stringは固定長の文字列であり、StringBuilderとStringBufferは長くなる文字列である.2.StringBufferはスレッドが安全で、StringBuilderは非スレッドが安全です.3.StringBuilderとStringBufferのデフォルトの初期容量は16で、文字列の長さを事前に見積もることができ、拡張による追加のオーバーヘッドをさらに削減することができます.