Java Challengers # 2: String-Vergleich

Java Challengers # 2: String-Vergleich


Wie immer haben wir viel Verspätung für den Beginn des Kurses, so dass erst gestern eine zweite Lektion unter dem neuen Thread "Java Developer" abgehalten wurde. Aber das ist so, kleine Dinge im Leben, aber im Moment veröffentlichen wir weiterhin eine Reihe von Java Challengers-Artikeln, deren Übersetzung für Sie vorbereitet wurde.


In Java kapselt die String Klasse ein char Array ( Anmerkung des Übersetzers - mit Java 9 ist es bereits ein byte Array, siehe Compact Strings in Java 9 ). In einfachen Worten ist String ein Array von Zeichen, mit denen Wörter, Sätze oder andere Konstrukte zusammengesetzt werden.


Die Kapselung ist eines der mächtigsten Konzepte in der objektorientierten Programmierung. Dank der Kapselung müssen Sie nicht wissen, wie die String Klasse funktioniert. Sie müssen nur die Methoden der Schnittstelle kennen.



Wenn Sie sich die String Klasse in Java ansehen, können Sie sehen, wie das char Array gekapselt ist:


 public String(char value[]) { this(value, 0, value.length, null); } 

Stellen Sie sich zum besseren Verständnis der Kapselung ein physisches Objekt vor: eine Maschine. Müssen Sie wissen, wie ein Auto unter der Motorhaube funktioniert, um es zu fahren? Natürlich nicht, aber Sie sollten wissen, was die Schnittstellen des Autos bewirken: das Gaspedal, die Bremsen und das Lenkrad. Jede dieser Schnittstellen unterstützt bestimmte Aktionen: Beschleunigen, Bremsen, Linksabbiegen, Rechtsabbiegen. Gleiches gilt für die objektorientierte Programmierung.


Der erste Artikel in der Java Challengers-Reihe befasste sich mit der Methodenüberladung, die in der String Klasse weit verbreitet ist. Überladen kann Ihre Klassen wirklich flexibel machen:


 public String(String original) {} public String(char value[], int offset, int count) {} public String(int[] codePoints, int offset, int count) {} public String(byte bytes[], int offset, int length, String charsetName) {} //    ... 

Anstatt zu verstehen, wie die String Klasse funktioniert, hilft Ihnen dieser Artikel zu verstehen, was sie tut und wie Sie sie in Ihrem Code verwenden.


Was ist ein String-Pool?


Die String Klasse ist wohl die am häufigsten verwendete Klasse in Java. Wenn wir jedes Mal, wenn wir String , ein neues Objekt im dynamischen Speicher (Speicherheap) erstellen, verschwenden wir viel Speicher. Der String-Pool löst dieses Problem, indem nur ein Objekt für jeden Zeilenwert gespeichert wird.


Strings-in-the-String-Pool


Linien in einer Reihe Pool


Obwohl wir mehrere String Variablen mit den Werten Duke und Juggy , werden nur zwei Objekte erstellt und im dynamischen Speicher (Heap) gespeichert. Siehe das folgende Codebeispiel zum Beweis. (Denken Sie daran, dass in Java der Operator " == " verwendet wird, um zwei Objekte zu vergleichen und festzustellen, ob dasselbe Objekt dasselbe ist oder nicht.)


 String juggy = "Juggy"; String anotherJuggy = "Juggy"; System.out.println(juggy == anotherJuggy); 

Dieser Code gibt true da die beiden String Variablen auf dasselbe Objekt im String-Pool verweisen. Ihre Bedeutungen sind die gleichen.


Die Ausnahme ist der new Operator.


Schauen Sie sich jetzt diesen Code an - er sieht ähnlich aus wie im vorherigen Beispiel, aber es gibt einen Unterschied.


 String duke = new String("duke"); String anotherDuke = new String("duke"); System.out.println(duke == anotherDuke); 

Basierend auf dem vorherigen Beispiel könnten Sie denken, dass dieser Code true , dies ist jedoch nicht der true . Durch Hinzufügen eines new Operators wird ein neues String Objekt im Speicher erstellt. Somit erstellt die JVM zwei verschiedene Objekte.


Native Methoden

Native Methoden in Java sind Methoden, die in der Sprache C kompiliert werden, normalerweise mit dem Ziel, den Speicher zu verwalten und die Leistung zu optimieren.

String-Pools und intern() -Methode


Zum Speichern von Zeichenfolgen in einem Pool wird eine Methode namens String-Internierung verwendet.


Folgendes erzählt uns Javadoc über die intern() -Methode:


  /** *      . * *   ( )   {@code String}. * *    intern,     , *    {@code String},   *  {@link #equals(Object)},     . * ,   {@code String}   *        {@code String}. * *   ,      {@code s}  {@code t}, * {@code s.intern() == t.intern()}  {@code true} *    ,  {@code s.equals(t)}  {@code true}. * *       . *      3.10.5 The Java™ Language Specification. * * @returns ,         , * , ,       . * * @jls 3.10.5 String Literals */ public native String intern(); 

Die intern() -Methode wird zum Speichern von Zeichenfolgen in einem Zeichenfolgenpool verwendet. Zunächst wird überprüft, ob eine bereits erstellte Zeile im Pool vorhanden ist. Wenn nicht, wird eine neue Zeile im Pool erstellt. Die Zeilenpoollogik basiert auf dem Flyweight- Muster.


Beachten Sie nun, was passiert, wenn wir mit new zwei Zeilen erstellen:


 String duke = new String("duke"); String duke2 = new String("duke"); System.out.println(duke == duke2); //    false System.out.println(duke.intern() == duke2.intern()); //    true 

Im Gegensatz zum vorherigen Beispiel mit dem new Schlüsselwort gibt der Vergleich in diesem Fall true . Dies liegt daran, dass durch die Verwendung der intern() -Methode sichergestellt wird, dass sich die Zeichenfolge im Pool befindet.


equals Methode in der String Klasse


Mit der Methode equals() wird überprüft, ob zwei Klassen gleich sind oder nicht. Da sich equals() in der Object Klasse befindet, erbt jede Java-Klasse diese. Die Methode equals() muss jedoch überschrieben werden, damit sie ordnungsgemäß funktioniert. String überschreibt natürlich equals() .


Schauen Sie mal rein:


 public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String aString = (String)anObject; if (coder() == aString.coder()) { return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); } } return false; } 

Wie Sie sehen können, wird der Wert der String Klasse durch equals() und nicht durch eine Objektreferenz verglichen. Es spielt keine Rolle, ob die Verweise auf die Objekte unterschiedlich sind; Bedingungen werden verglichen.


Allgemeine Zeichenfolgenmethoden


Bevor Sie das Problem mit dem Zeichenfolgenvergleich lösen, müssen Sie noch eines wissen.


Betrachten Sie die gängigsten Methoden der String Klasse:


 //         trim() //     substring(int beginIndex, int endIndex) //    length() //  ,     replaceAll(String regex, String replacement) // ,     CharSequence   contains(CharSequences) 

Lösen Sie das Problem mit dem Zeichenfolgenvergleich


Lassen Sie uns überprüfen, was Sie über die String Klasse gelernt haben, indem Sie ein kleines Rätsel lösen.


In dieser Aufgabe vergleichen Sie mehrere Zeilen mit den erlernten Konzepten. Können Sie anhand des folgenden Codes den Wert jedes variablen result bestimmen?


 public class ComparisonStringChallenge { public static void main(String... doYourBest) { String result = ""; result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1"; result += "flexibleCode" == "flexibleCode" ? "2" : "3"; result += new String("doYourBest") == new String("doYourBest") ? "4" : "5"; result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7"; result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9"; System.out.println(result); } } 

Was wird die Schlussfolgerung sein?


  • A: 02468
  • B: 12469
  • C: 12579
  • D: 12568

Die richtige Antwort finden Sie am Ende des Artikels.


Was ist jetzt passiert? Grundlegendes zum Verhalten von Zeichenfolgen


In der ersten Zeile sehen wir:


 result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1"; 

In diesem Fall ist das Ergebnis false , da beim Entfernen von Leerzeichen mit der Methode trim() mit dem new Operator ein neuer String erstellt wird.


Als nächstes sehen wir:


 result += "flexibleCode" == "flexibleCode" ? "2" : "3"; 

Hier gibt es kein Geheimnis, die Zeilen im Zeilenpool sind gleich. Dieser Vergleich gibt true .


Dann haben wir:


 result += new String("doYourBest") == new String("doYourBest") ? "4" : "5"; 

Die Verwendung new Zeilen führt zur Erstellung von zwei neuen Zeilen, und es spielt keine Rolle, ob ihre Werte gleich sind oder nicht. In diesem Fall ist der Vergleich auch dann false , wenn die Werte gleich sind.


Weiter:


 result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7"; 

Da wir die Methode equals() verwendet haben, wird der Wert der Zeichenfolge verglichen, nicht die Instanz des Objekts.


In diesem Fall spielt es keine Rolle, ob verschiedene Objekte vorhanden sind oder nicht, da der Wert verglichen wird. Das Ergebnis ist true .


Endlich haben wir:


 result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9"; 

Wie Sie bereits gesehen haben, intern() die intern() -Methode einen String in einen String-Pool ein. Beide Linien zeigen auf dasselbe Objekt, also in diesem Fall true .


Häufige Zeichenfolgenfehler


Es kann schwierig sein zu bestimmen, ob zwei Linien auf dasselbe Objekt zeigen oder nicht, insbesondere wenn die Linien denselben Wert enthalten. Es ist nützlich, sich daran zu erinnern, dass die Verwendung von new immer zur Erstellung eines neuen Objekts im Speicher führt, selbst wenn die Zeichenfolgenwerte gleich sind.


Die Verwendung von String Methoden zum Vergleichen von Objektreferenzen kann ebenfalls schwierig sein. Die Besonderheit ist, dass, wenn die Methode etwas in der Zeile ändert, unterschiedliche Verweise auf Objekte vorhanden sind.


Einige Beispiele zur Verdeutlichung:


 System.out.println("duke".trim() == "duke".trim()); 

Dieser Vergleich ist wahr, da die trim() -Methode keine neue Zeile erstellt.


 System.out.println(" duke".trim() == "duke".trim()); 

In diesem Fall generiert die erste trim() -Methode eine neue Zeile, da die Methode ihre Aufgabe erfüllt und daher die Verknüpfungen unterschiedlich sind.


Wenn trim() seine Arbeit erledigt, erstellt es schließlich eine neue Zeile:


 //   trim   String new String(Arrays.copyOfRange(val, index, index + len), LATIN1); 

Woran Sie sich bei Saiten erinnern sollten


  • Zeilen sind nicht veränderbar, daher kann der Zeilenstatus nicht geändert werden.


  • Um Speicherplatz zu sparen, speichert die JVM Zeichenfolgen in einem Zeichenfolgenpool. Beim Erstellen einer neuen Zeile überprüft die JVM ihren Wert und zeigt auf ein vorhandenes Objekt. Wenn der Pool keine Zeile mit diesem Wert enthält, erstellt die JVM eine neue Zeile.


  • Der Operator " == " vergleicht Objektreferenzen. Die Methode equals() vergleicht Zeichenfolgenwerte. Die gleiche Regel gilt für alle Objekte.


  • Wenn Sie den new Operator verwenden, wird eine neue Zeile im Heap erstellt (Anmerkung des Übersetzers - es wird im Original geschrieben, dass sie sich im Pool befindet, aber dies ist nicht der Fall , danke zagayevskiy ), selbst wenn es eine Zeile mit demselben Wert gibt.



Die Antwort


Die Antwort auf dieses Problem lautet D. Die Schlussfolgerung lautet 12568.


Fortsetzung folgt...

Source: https://habr.com/ru/post/de428449/


All Articles