CharSequence VS String in Java?


421

Bei der Programmierung in Android werden die meisten Textwerte in erwartet CharSequence.

Warum ist das so? Was ist der Vorteil und was sind die Hauptauswirkungen der Verwendung von CharSequenceOver String?

Was sind die Hauptunterschiede und welche Probleme werden erwartet, wenn Sie sie verwenden und von einem zum anderen konvertieren?


Antworten:


343

Strings sind CharSequences , Sie können also einfach Strings verwenden und müssen sich keine Sorgen machen. Android versucht lediglich, hilfreich zu sein, indem Sie auch andere CharSequence-Objekte wie StringBuffers angeben können.


94
Außer wenn Android mir in einem Rückruf eine CharSequence übergibt und ich einen String benötige, rufen Sie charSeq.toString () auf.
Martin Konicek

100
Beachten Sie jedoch diese Einschränkung aus dem CharSequenceJavadoc: Diese Schnittstelle verfeinert nicht die allgemeinen Verträge der Methoden equalsund hashCode. Das Ergebnis des Vergleichs zweier implementierter Objekte CharSequenceist daher im Allgemeinen undefiniert . Jedes Objekt kann von einer anderen Klasse implementiert werden, und es gibt keine Garantie dafür, dass jede Klasse ihre Instanzen auf Gleichheit mit denen der anderen testen kann. Es ist daher unangemessen, beliebige CharSequenceInstanzen als Elemente in einer Menge oder als Schlüssel in einer Karte zu verwenden.
Trevor Robinson

3
@ Pacerier: Ich denke, es ist eher eine praktische Einschränkung. CharSequencewar eine Nachrüstung in JDK 1.4, um eine gemeinsame Schnittstelle mit eingeschränktem Verwendungszweck für Objekte einzuführen, die Zeichenfolgen enthalten. Einige dieser Objekte enthalten einen anderen Status, daher ist es möglicherweise nicht sinnvoll, Object.equals"enthält dieselbe Zeichenfolge" zu definieren . NIO CharBufferlegt beispielsweise nur die Zeichen zwischen positionund limitals offen , CharSequenceobwohl möglicherweise viele andere Zeichen enthalten sind.
Trevor Robinson

6
@ TrevorRobinson, also der Designfehler hat equals/ hashCodean Objecterster Stelle ....
Pacerier

4
@Pacerier: IMHO Es gibt keine Designfehler entweder Objectoder CharSequence, keine Schnittstelle erforderlich ist Gleichheit sanity zwischen Implementierungen zu bieten. Es Collectionsind keine zwei erforderlich, um die Gleichheit zwischen der CollectionSchnittstelle zu gewährleisten , aber sie können, wenn sie dies wünschen. IMHO CharSequencesollte auf Eingaben beschränkt und weniger für Rückgabetypen verwendet werden.
Brett Ryan

58

CharSequence= Schnittstelle
String= konkrete Umsetzung

Du sagtest:

Konvertieren von einem zum anderen

Es erfolgt keine Konvertierung von String.

  • Jedes StringObjekt ist ein CharSequence.
  • Jeder CharSequencekann eine produzieren String. Rufen Sie an CharSequence::toString. Wenn das CharSequencezufällig a ist String, gibt die Methode einen Verweis auf ihr eigenes Objekt zurück.

Mit anderen Worten, jeder Stringist ein CharSequence, aber nicht jeder CharSequenceist ein String.

Programmierung auf eine Schnittstelle

Bei der Programmierung in Android werden die meisten Textwerte in CharSequence erwartet.

Warum ist das so? Was ist der Vorteil und was sind die Hauptauswirkungen der Verwendung von CharSequence über String?

Im Allgemeinen ist das Programmieren auf eine Schnittstelle besser als das Programmieren auf konkrete Klassen. Dies bietet Flexibilität, sodass wir zwischen konkreten Implementierungen einer bestimmten Schnittstelle wechseln können, ohne anderen Code zu beschädigen.

Wenn Sie eine API entwickeln , die von verschiedenen Programmierern in verschiedenen Situationen verwendet werden soll, schreiben Sie Ihren Code, um die allgemeinsten Schnittstellen zu erhalten und zu übernehmen. Dies gibt dem aufrufenden Programmierer die Freiheit, verschiedene Implementierungen dieser Schnittstelle zu verwenden, je nachdem, welche Implementierung für ihren jeweiligen Kontext am besten geeignet ist.

Schauen Sie sich beispielsweise das Java Collections Framework an . Wenn Ihr API gibt oder nimmt eine geordnete Sammlung von Objekten, erklären Ihre Methoden wie die Verwendung Listnicht ArrayList, LinkedListoder jede andere 3rd-Party - Implementierung List.

Wenn Sie eine schnelle und schmutzige kleine Methode schreiben, die nur von Ihrem Code an einer bestimmten Stelle verwendet werden soll, anstatt eine API zu schreiben, die an mehreren Stellen verwendet werden soll, müssen Sie sich nicht mit der Verwendung der allgemeineren Benutzeroberfläche befassen, sondern mit einer bestimmten konkreten Klasse. Aber selbst dann tut es weh, die allgemeinste Oberfläche zu verwenden, die Sie können.

Was sind die Hauptunterschiede und welche Probleme werden bei der Verwendung erwartet?

  • Mit a Stringwissen Sie, dass Sie einen einzigen Text haben, der vollständig im Gedächtnis bleibt und unveränderlich ist.
  • Mit a CharSequencewissen Sie nicht, welche Besonderheiten die konkrete Implementierung haben könnte.

Das CharSequenceObjekt kann einen enormen Textblock darstellen und hat daher Auswirkungen auf das Gedächtnis. Oder es können viele Textblöcke separat verfolgt werden, die beim Aufrufen zusammengefügt werden toStringmüssen und daher Leistungsprobleme aufweisen. Die Implementierung ruft möglicherweise sogar Text von einem Remotedienst ab und hat daher Auswirkungen auf die Latenz.

und von einem zum anderen konvertieren?

Sie werden im Allgemeinen nicht hin und her konvertieren. A String ist a CharSequence. Wenn Ihre Methode deklariert, dass es a benötigt CharSequence, kann der aufrufende Programmierer ein StringObjekt oder etwas anderes wie ein StringBufferoder übergeben StringBuilder. Der Code Ihrer Methode verwendet einfach alles, was übergeben wird, und ruft eine der CharSequenceMethoden auf.

Die Konvertierung kommt Ihnen am nächsten, wenn Ihr Code eine erhält CharSequenceund Sie wissen, dass Sie eine benötigen String. Möglicherweise sind Sie mit altem Code verbunden, der in die StringKlasse geschrieben wurde, anstatt in die CharSequenceSchnittstelle geschrieben zu werden. Oder Ihr Code arbeitet intensiv mit dem Text, z. B. wiederholte Schleifen oder andere Analysen. In diesem Fall möchten Sie einen möglichen Leistungstreffer nur einmal ausführen, also rufen Sie im Voraus toStringan. Fahren Sie dann mit Ihrer Arbeit fort, indem Sie einen einzigen Text verwenden, von dem Sie wissen, dass er vollständig im Gedächtnis bleibt.

Verdrehte Geschichte

Beachten Sie die Kommentare zur akzeptierten Antwort . Die CharSequenceSchnittstelle wurde an vorhandenen Klassenstrukturen nachgerüstet, sodass einige wichtige Feinheiten ( equals()& hashCode()) vorhanden sind. Beachten Sie die verschiedenen Versionen von Java (1, 2, 4 & 5), die auf den Klassen / Schnittstellen markiert sind - im Laufe der Jahre einiges an Abwanderung. Idealerweise CharSequencewäre es von Anfang an vorhanden gewesen, aber so ist das Leben.

Mein Klassendiagramm unten kann Ihnen helfen, das Gesamtbild der Zeichenfolgentypen in Java 7/8 zu sehen. Ich bin mir nicht sicher, ob all dies in Android vorhanden ist, aber der Gesamtkontext kann sich für Sie dennoch als nützlich erweisen.

Diagramm verschiedener stringbezogener Klassen und Schnittstellen


2
Haben Sie dieses Diagramm selbst erstellt? Ich frage mich, ob es einen Katalog dieser Diagramme für verschiedene Datenstrukturen gibt.
user171943

4
@ user171943 Von mir verfasst, handgefertigt mit der OmniGraffle- App der OmniGroup.
Basil Bourque

37

Ich glaube, es ist am besten, CharSequence zu verwenden. Der Grund dafür ist, dass String CharSequence implementiert. Daher können Sie einen String an eine CharSequence übergeben. Sie können jedoch keine CharSequence an einen String übergeben, da CharSequence keinen String implementiert. AUCH in Android gibt die EditText.getText()Methode eine Bearbeitbare zurück, die auch CharSequence implementiert und einfach in eine, aber nicht einfach in eine Zeichenfolge übergeben werden kann. CharSequence kümmert sich um alles!


7
Sie können tuncharSequence.toString()
Jorge Fuentes González

1
@jorge: Außer dass dies relativ ineffizient ist, wenn die Sequenz veränderlich ist (oder aus irgendeinem Grund eine Kopie der Zeichen erfordert, um eine unveränderliche Zeichenfolge zu erstellen).
Lawrence Dol

sehr schöne Erklärung ..!
Majurageer als

23

Im Allgemeinen können Sie mithilfe einer Schnittstelle die Implementierung mit minimalem Kollateralschaden variieren. Obwohl java.lang.String sehr beliebt ist, kann es sein, dass in bestimmten Kontexten eine andere Implementierung verwendet werden soll. Durch das Erstellen der API um CharSequences anstelle von Strings bietet der Code die Möglichkeit, dies zu tun.


8

Dies ist mit ziemlicher Sicherheit ein Leistungsgrund. Stellen Sie sich beispielsweise einen Parser vor, der einen 500-KByte-ByteBuffer mit Zeichenfolgen durchläuft.

Es gibt drei Ansätze zum Zurückgeben des Zeichenfolgeninhalts:

  1. Erstellen Sie einen String [] zur Analysezeit, jeweils ein Zeichen. Dies wird spürbar lange dauern. Wir können == anstelle von .equals verwenden, um zwischengespeicherte Referenzen zu vergleichen.

  2. Erstellen Sie ein int [] mit Offsets zur Analysezeit und erstellen Sie dann dynamisch einen String, wenn ein get () auftritt. Jeder String ist ein neues Objekt, daher werden keine zurückgegebenen Werte zwischengespeichert und == verwendet

  3. Erstellen Sie zur Analysezeit eine CharSequence []. Da keine neuen Daten gespeichert werden (außer Offsets im Bytepuffer), ist das Parsen viel niedriger als # 1. Zum Zeitpunkt des Abrufs müssen wir keinen String erstellen, daher ist die Abrufleistung gleich # 1 (viel besser als # 2), da wir nur einen Verweis auf ein vorhandenes Objekt zurückgeben.

Zusätzlich zu den Verarbeitungsgewinnen, die Sie mit CharSequence erzielen, reduzieren Sie auch den Speicherbedarf, indem Sie keine Daten duplizieren. Wenn Sie beispielsweise einen Puffer mit 3 Textabschnitten haben und entweder alle 3 oder einen einzelnen Absatz zurückgeben möchten, benötigen Sie 4 Zeichenfolgen, um dies darzustellen. Mit CharSequence benötigen Sie nur 1 Puffer mit den Daten und 4 Instanzen einer CharSequence-Implementierung, die den Start und die Länge verfolgt.


6
Referenzen plz. klingt wie zufällig erraten, was los ist. Ich finde dein Argument auch nicht gültig. Man könnte einfach den 500k-Bytebuffer als String speichern und einfach Teilzeichenfolgen zurückgeben, was verrückt schnell und viel häufiger ist.
kritzikratzi

6
@kritzikratzi - Ab JDK7 teilt der Teilstring auf String nicht mehr das zugrunde liegende Array und ist nicht "verrückt schnell". Es dauert O (N) Zeit in der Länge des Teilstrings und generiert jedes Mal, wenn Sie es aufrufen, eine Kopie der zugrunde liegenden Zeichen (also viel Müll).
BeeOnRope

@kritzikratzi Ich glaube, der Grund für die Änderung ist, dass, wenn die Kopie nicht erstellt würde, der ursprüngliche String für die Lebensdauer aller Teilzeichenfolgen erhalten bleibt. Da Teilzeichenfolgen normalerweise nur kleine Teile des Originals sind und je nach Verwendung unbegrenzt lange halten können, würde dies häufig zu noch mehr Müll führen, wenn die Teilzeichenfolgen viel länger als die ursprüngliche Zeichenfolge verwendet würden. Eine interessante Alternative könnte sein, anhand des Verhältnisses von Teilzeichenfolge zu übergeordneter Zeichenfolgengröße zu bestimmen, ob kopiert werden soll oder nicht, aber Sie müssten dafür Ihre eigene CharSequenceImplementierung rollen .
JAB

1
Vielleicht habe ich den Punkt verpasst, aber diese Antwort ist Unsinn. A CharSequenceist eine Schnittstelle - per Definition enthält es keine der von Ihnen diskutierten Implementierungsdetails, da es keine eigene Implementierung hat . A Stringist eine von mehreren konkreten Klassen, die die CharSequenceSchnittstelle implementieren . Also String ist a ein CharSequence. Sie können die Leistungsdetails von Stringvs StringBuffervs vergleichen StringBuilder, aber nicht CharSequence. Das Schreiben von „Verarbeitungsgewinnen, die Sie mit CharSequence erzielen“ ist bedeutungslos.
Basil Bourque

7

Ein Problem, das im praktischen Android-Code auftritt, ist, dass der Vergleich mit CharSequence.equals gültig ist, aber nicht unbedingt wie beabsichtigt funktioniert.

EditText t = (EditText )getView(R.id.myEditText); // Contains "OK"
Boolean isFalse = t.getText().equals("OK"); // will always return false.

Der Vergleich sollte von durchgeführt werden

("OK").contentEquals(t.GetText()); 

5

Zeichenfolge

A CharSequenceist eine Schnittstelle, keine tatsächliche Klasse. Eine Schnittstelle ist nur eine Reihe von Regeln (Methoden), die eine Klasse enthalten muss, wenn sie die Schnittstelle implementiert. In Android CharSequenceist a ein Regenschirm für verschiedene Arten von Textzeichenfolgen. Hier sind einige der häufigsten:

(Mehr über die Unterschiede zwischen diesen können Sie hier lesen .)

Wenn Sie ein CharSequenceObjekt haben, ist es tatsächlich ein Objekt einer der implementierten Klassen CharSequence. Zum Beispiel:

CharSequence myString = "hello";
CharSequence mySpannableStringBuilder = new SpannableStringBuilder();

Der Vorteil eines allgemeinen Schirmtyps CharSequencebesteht darin, dass Sie mehrere Typen mit einer einzigen Methode verarbeiten können. Wenn ich zum Beispiel eine Methode habe, die a CharSequenceals Parameter verwendet, könnte ich a Stringoder a übergeben SpannableStringBuilderund sie würde beide behandeln.

public int getLength(CharSequence text) {
    return text.length();
}

String

Man könnte sagen, dass a Stringnur eine Art von ist CharSequence. Im Gegensatz dazu CharSequencehandelt es sich jedoch um eine tatsächliche Klasse, sodass Sie daraus Objekte erstellen können. Sie könnten also Folgendes tun:

String myString = new String();

aber das kannst du nicht machen:

CharSequence myCharSequence = new CharSequence(); // error: 'CharSequence is abstract; cannot be instantiated

Da CharSequencees sich nur um eine Liste von Regeln handelt, die den StringAnforderungen entsprechen, können Sie Folgendes tun:

CharSequence myString = new String();

Das bedeutet, dass CharSequencees immer dann in Ordnung ist , wenn eine Methode nach a fragt String.

String myString = "hello";
getLength(myString); // OK

// ...

public int getLength(CharSequence text) {
    return text.length();
}

Das Gegenteil ist jedoch nicht der Fall. Wenn die Methode einen StringParameter verwendet, können Sie ihm nichts übergeben, von dem nur allgemein bekannt ist CharSequence, dass es sich um einen Parameter handelt , da es sich möglicherweise tatsächlich um einen SpannableStringoder einen anderen Parameter handelt CharSequence.

CharSequence myString = "hello";
getLength(myString); // error

// ...

public int getLength(String text) {
    return text.length();
}

0

CharSequenceist eine Schnittstelle und Stringimplementiert sie. Sie können a instanziieren, Stringaber das können Sie nicht, CharSequenceda es sich um eine Schnittstelle handelt. Weitere Implementierungen finden Sie CharSequenceauf der offiziellen Java-Website.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.