平日のプロジェクトの論理的な実装で忙しかったです。土曜日に時間があったので、本棚からJavaでThinkingの厚い英語版を取り出して、文字列オブジェクトのスプライシングを読みました。この本を翻訳として参照し、あなたが考えていることを追加し、この記事を書いて記録してください。
不変の文字列オブジェクト
Javaでは、文字列オブジェクトは不変(不変)です。コードでは、文字列オブジェクトの複数のエイリアスを作成できます。しかし、これらのエイリアスの参照は同じです。
たとえば、S1とS2は「droidyue.com」オブジェクトのエイリアスであり、エイリアスは実際のオブジェクトへの参照に保存されます。したがって、S1 = S2
string s1 = "driodyue.com"; string s2 = s1; system.out.println( "s1とs2の参照=" +(s1 == s2));
Javaで唯一のオーバーロードされた演算子
Javaでは、唯一のオーバーロードされた演算子は弦のスプライシングに関連しています。 +、+=。それに加えて、Javaデザイナーは他のオペレーターの過負荷を許可していません。
スプライシング分析
本当にパフォーマンスコストはありますか?
上記の2つのポイントを理解した後、そのような考えがあるかもしれません。刺すオブジェクトは不変であるため、複数の(3つ以上の)文字列のスプライシングは、必然的に不必要な中間文字列オブジェクトを生成します。
string username = "andy"; string age = "24"; string job = "developer"; string info = username + age + job;
上記の情報を取得するために、ユーザー名と年齢をスプライシングして、ANDY24のコンテンツを使用して一時的な文字列オブジェクトT1を生成し、T1とジョブをスプライして、最終的に必要な情報オブジェクトを生成します。その中で、中間のT1が生成され、T1が作成された後、自動的にリサイクルされず、必然的に一定量のスペースを占有します。それが多くのスプライシングである場合(オブジェクトへのトストリングコールでは数百、より一般的であると仮定する)、コストは大きくなり、パフォーマンスははるかに低くなります。
コンパイラ最適化処理
本当に上記のパフォーマンスコストはありますか?文字列スプライシングは非常に一般的ですが、特別な処理の最適化はありませんか?答えは、コンパイラが.javaをbytecodeにコンパイルするときにこの最適化が実行されるということです。
Javaプログラムが実行されたい場合、時間と実行時間をコンパイルし、2つの期間がかかります。コンパイル時に、Javaコンパイラ(コンパイラ)がJavaファイルをByteCodeに変換します。実行時に、Java Virtual Machine(JVM)は、コンパイル時間生成バイトコードを実行します。これら2つの期間を通じて、Javaはいわゆるコンピレーションを達成し、どこでも実行しています。
コンピレーション期間中に行われた最適化を試してみましょう。パフォーマンスコストのあるコードを作成します。
public class concatenation {public static void main(string [] args){string username = "andy";文字列age = "24"; string job = "developer";文字列info = username + age + job; System.out.println(info); }}concatenation.javaをコンパイルします。 concatenation.classを取得します
Javac concatenation.java
次に、javapを使用して、コンパイルされたconcatenation.classファイルを逆コンパイルします。 Javap -C連結。 Javapコマンドが見つからない場合は、Javapが環境変数に配置されているディレクトリを追加するか、Javapのフルパスを使用することを検討してください。
17:22:04 -androidyue〜/workspace_adt/strings/src $ javap -c concatenation.java "public class concatenation {public concatenation();コード:0:ALOAD_0 1:Invokespecial#1 // Method Java/Lang/Object。 "<init>" :()v 4:public static void main(java.lang.string [])を返す;コード:0:LDC#2 // STRING ANDY 2:STORE_1 3:LDC#3 // STRING 24 5:STORE_2 6:LDC#4 // STRING DEVELOPER 8:STORE_3 9:NEW#5 // CLASS JAVA/LANG/STRINGBUILDER 12:DUP 13:Invokespecial#6 // Method/Lanan 17:InvokeVirtual#7 // Method Java/Lang/StringBuilder.Append :( ljava/lang/string;)ljava/lang/stringbuilder; 20:ALOAD_2 21:InvokeVirtual#7 // Method Java/Lang/StringBuilder.Append :( ljava/lang/string;)ljava/lang/stringbuilder; 24:Aload_3 25:InvokeVirtual#7 // Method Java/Lang/StringBuilder.Append :( Ljava/Lang/String;)ljava/lang/stringbuilder; 28:InvokeVirtual#8 // Method Java/Lang/StringBuilder.ToString :()Ljava/Lang/String; 31:Store 4 33:GetStatic#9 // Field Java/Lang/System.out:ljava/io/printstream; 36:Aload 4 38:InvokeVirtual#10 // Method Java/io/printstream.println :( ljava/lang/string;)v 41:return}その中で、LDC、ストアなどは、アセンブリ命令と同様のJavaバイトコード命令です。次のコメントは、説明のためにJava関連のコンテンツを使用します。その上には多くのStringBuildersがいることがわかりますが、Javaコードでは表示せずにそれらを呼び出します。これは、Javaコンパイラによって行われた最適化です。 Javaコンパイラが文字列のスプライシングに遭遇すると、StringBuilderオブジェクトが作成されます。その背後にあるスプライシングは、実際にStringBuilderオブジェクトの付録メソッドを呼び出すことです。このようにして、私たちが心配している問題はありません。
コンパイラの最適化だけですか?
コンパイラは私たちが最適化するのに役立っているので、コンパイラの最適化だけに頼るのに十分ですか?もちろん違います。
低パフォーマンスのために最適化されていないコードを見てみましょう
public void ImplicitusestringBuilder(string [] values){string result = ""; for(int i = 0; i <values.length; i ++){result += values [i]; } system.out.println(result);}Javacでコンパイルし、Javapで表示します
11:新しい#5 //クラスJava/Lang/StringBuilder 14:Dup 15:Invokespecial#6 // Method Java/Lang/StringBuilder。 "<init>" :()v 18:aload_2 19:Invokevirtual#7 // Method 31:Store_2 32:Iinc 3、135:Geto 5 38:Geto 5 38:Geto 5 38:Geto 5 38: java/lang/system.out:ljava/io/printstream; 41:Aload_2 42:InvokeVirtual#10 // Method Java/io/printStream.println :( ljava/lang/string;)v 45:return
その中に8:if_icmpge 38および35:goto 5がループを形成します。 8:if_icmpge 38は、JVMオペランドスタックの整数比較が(i <values.lengthの逆の結果が反対の結果)以上である場合、38(System.out)にジャンプすることを意味します。 35:GOTO 5は、5行目に直接ジャンプすることを意味します。
しかし、ここでは、stringbuilderオブジェクトの作成がループ間で発生するという非常に重要なことがあります。つまり、ループが数回StringBuilderオブジェクトを作成しますが、これは明らかに良くありません。裸の低レベルコード。
わずかに最適化して、品質を即座に改善します。
public void reblicityestringbuilder(string [] values){stringbuilder result = new StringBuilder(); for(int i = 0; i <values.length; i ++){result.append(values [i]); }}対応するコンパイルされた情報
public void reblicityestringbuider(java.lang.string []);コード:0:新しい#5 //クラスJava/Lang/StringBuilder 3:DUP 4:Invokespecial#6 // Method Java/Lang/StringBuilder。 "<init>" :()v 7:astore_2 8:iconst_0 9:istore_3 10:iload_3 11 17:ALOAD_1 18:ILOAD_3 19:AALOAD 20:InvokeVirtual#7 // Method Java/Lang/StringBuilder.Append:(Ljava/Lang/String;)ljava/lang/stringbuilder; 23:POP 24:IINC 3、1 27:goto 10 30:return
上記からわかるように、13:if_icmpge 30および27:goto 10はループループを形成しますが、0:新しい#5はループの外側にあるため、StringBuilderは複数回作成されません。
一般的に、ループ本体のストリングビルダーの暗黙的または明示的な作成を避けるようにする必要があります。したがって、コードがどのようにコンパイルされ、どのように内部で実行されるかを理解している人は、比較的高いコードレベルを持っています。
上記の記事にエラーがある場合は、それらを批判して修正してください。
上記は、Java文字列のスプライシングに関する情報を整理することであり、将来的には関連情報を追加し続けます。このウェブサイトへのご支援ありがとうございます!