CharSequence VS String en Java?

421

Programmation sous Android, la plupart des valeurs de texte sont attendues dans CharSequence.

Pourquoi donc? Quel est l'avantage et quels sont les principaux impacts de l'utilisation CharSequenceexcessive String?

Quelles sont les principales différences et quels problèmes sont attendus lors de leur utilisation et de leur conversion de l'un à l'autre?

e-satis
la source
1
De meilleures réponses peuvent être trouvées à la différence exacte entre CharSequence et String en java
Suragch

Réponses:

343

Les chaînes sont des CharSequences , vous pouvez donc simplement utiliser des chaînes et ne vous inquiétez pas. Android essaie simplement d'être utile en vous permettant de spécifier également d'autres objets CharSequence, comme StringBuffers.

Zarkonnen
la source
94
Sauf quand Android me passe une CharSequence dans un rappel et que j'ai besoin d'une String - appelez charSeq.toString ().
Martin Konicek
100
Mais gardez à l'esprit cette mise en garde du CharSequencejavadoc: cette interface ne raffine pas les contrats généraux des méthodes equalset hashCode. Le résultat de la comparaison de deux objets qui implémentent CharSequenceest donc, en général, indéfini . Chaque objet peut être implémenté par une classe différente, et il n'y a aucune garantie que chaque classe sera capable de tester ses instances pour l'égalité avec celles de l'autre. Il est donc inapproprié d'utiliser des CharSequenceinstances arbitraires comme éléments dans un ensemble ou comme clés dans une carte.
Trevor Robinson
3
@Pacerier: Je pense que c'est plus une limitation pratique. CharSequenceétait une mise à niveau dans JDK 1.4 pour introduire une interface commune à usage limité aux objets contenant des séquences de caractères. Certains de ces objets contiennent un autre état, il n'est donc pas logique de définir Object.equalscomme "contient la même séquence de caractères". NIO CharBuffer, par exemple, expose uniquement les caractères entre son positionetlimit comme le CharSequence, bien qu'il puisse contenir de nombreux autres personnages.
Trevor Robinson
6
@TrevorRobinson, donc le bug de conception est d' avoir equals/ hashCodesur Objecten premier lieu ....
Pacerier
4
@Pacerier: IMHO Il n'y a pas de bogue de conception dans l'un Objectou l' autre CharSequence, aucune interface n'est requise pour fournir un équilibre entre les implémentations. Aucun deux Collectionn'est requis pour assurer l'égalité entre l' Collectioninterface, mais ils le peuvent s'ils le souhaitent. L'IMHO CharSequencedevrait être limité aux entrées et moins utilisé pour les types de retour.
Brett Ryan
58

CharSequence= interface
String= mise en œuvre concrète

Tu as dit:

conversion de l'un à l'autre

Il n'y a pas de conversion depuis String.

  • Chaque String objet est un CharSequence.
  • Chacun CharSequencepeut produire un String. Appelle CharSequence::toString. Si laCharSequence se trouve être un String, la méthode renvoie une référence à son propre objet.

En d'autres termes, tout Stringest unCharSequence , mais pas tout CharSequenceest un String.

Programmation vers une interface

Programmation sous Android, la plupart des valeurs de texte sont attendues dans CharSequence.

Pourquoi donc? Quel est l'avantage et quels sont les principaux impacts de l'utilisation de CharSequence sur String?

Généralement, la programmation sur une interface est meilleure que la programmation sur des classes concrètes. Cela donne de la flexibilité, nous pouvons donc basculer entre les implémentations concrètes d'une interface particulière sans casser d'autres codes.

Lors du développement d'un API à utiliser par divers programmeurs dans diverses situations, écrivez votre code pour donner et prendre les interfaces les plus générales possibles. Cela donne au programmeur appelant la liberté d'utiliser diverses implémentations de cette interface, selon celle qui convient le mieux à son contexte particulier.

Par exemple, regardez le Java Collections Framework . Si votre API donne ou prend une collection ordonnée d'objets, déclarer vos méthodes que l' utilisation Listplutôt que ArrayList, LinkedListou toute autre mise en œuvre 3ème partie deList .

Lorsque vous écrivez une petite méthode rapide et sale à utiliser uniquement par votre code à un endroit spécifique, par opposition à l'écriture d'une API à utiliser à plusieurs endroits, vous n'avez pas à vous soucier d'utiliser l'interface plus générale plutôt qu'un béton spécifique classe. Mais même dans ce cas, il est difficile d'utiliser l'interface la plus générale possible.

Quelles sont les principales différences et quels problèmes sont attendus lors de leur utilisation,

  • Avec un String vous savez que vous avez un seul morceau de texte, entièrement en mémoire, et est immuable.
  • Avec a CharSequence, vous ne savez pas quelles pourraient être les particularités de l'implémentation concrète.

L' CharSequenceobjet peut représenter un énorme morceau de texte, et a donc des implications de mémoire. Ou il peut y avoir de nombreux morceaux de texte suivis séparément qui devront être assemblés lorsque vous appelez toString, et ont donc des problèmes de performances. L'implémentation peut même récupérer du texte à partir d'un service distant et a donc des implications de latence.

et la conversion de l'un à l'autre?

En général, vous ne vous convertirez pas d'avant en arrière. A String est un CharSequence. Si votre méthode déclare qu'il faut un CharSequence, le programmeur appelant peut passer un Stringobjet, ou peut passer quelque chose d'autre comme un StringBufferou StringBuilder. Le code de votre méthode utilisera simplement tout ce qui est passé, appelant l'une des CharSequenceméthodes.

La conversion la plus proche serait si votre code reçoit un CharSequenceet que vous savez que vous en avez besoin String. Peut-être que vous interfacez avec de l'ancien code écrit en Stringclasse plutôt qu'en écrit sur l' CharSequenceinterface. Ou peut-être que votre code fonctionnera intensément avec le texte, comme une boucle répétée ou une autre analyse. Dans ce cas, vous voulez prendre une seule fois les performances possibles, vous appeleztoString donc à l'avance. Continuez ensuite votre travail en utilisant ce que vous savez être un seul morceau de texte entièrement en mémoire.

Histoire tordue

Notez les commentaires faits sur la réponse acceptée . L' CharSequenceinterface a été adaptée aux structures de classe existantes, il y a donc quelques subtilités importantes ( equals()& hashCode()). Remarquez les différentes versions de Java (1, 2, 4 et 5) marquées sur les classes / interfaces, ce qui a beaucoup bougé au fil des ans. Idéalement, CharSequenceil aurait été en place depuis le début, mais telle est la vie.

Mon diagramme de classes ci-dessous peut vous aider à voir la grande image des types de chaînes en Java 7/8. Je ne sais pas si tous ces éléments sont présents dans Android, mais le contexte global peut toujours vous être utile.

diagramme des différentes classes et interfaces liées aux chaînes

Basil Bourque
la source
2
Avez-vous fait ce diagramme vous-même? se demandant s'il existe un catalogue de ces diagrammes pour différentes structures de données.
user171943
4
@ user171943 Créé par mes soins, fabriqué à la main à l'aide de l' application OmniGraffle d'OmniGroup.
Basil Bourque
37

Je pense qu'il est préférable d'utiliser CharSequence. La raison en est que String implémente CharSequence, donc vous pouvez passer une String dans une CharSequence, CEPENDANT vous ne pouvez pas passer une CharSequence dans une String, car CharSequence n'implémente pas String. ÉGALEMENT, dans Android, la EditText.getText()méthode retourne un modifiable, qui implémente également CharSequence et peut être passé facilement en un, mais pas facilement en une chaîne. CharSequence s'occupe de tout!

Faux
la source
7
Vous pouvez le fairecharSequence.toString()
Jorge Fuentes González
1
@jorge: Sauf que cela sera relativement inefficace si la séquence est modifiable (ou pour une raison quelconque, nécessite une copie des caractères afin de les rendre en chaîne immuable).
Lawrence Dol
très belle explication ..!
majurageerthan
23

En général, l'utilisation d'une interface vous permet de varier la mise en œuvre avec un minimum de dommages collatéraux. Bien que java.lang.String soit très populaire, il peut être possible que dans certains contextes, vous souhaitiez utiliser une autre implémentation. En construisant l'API autour de CharSequences plutôt que de Strings, le code donne l'occasion de le faire.

Itay Maman
la source
8

Il s'agit presque certainement de raisons de performances. Par exemple, imaginez un analyseur qui passe par un ByteBuffer de 500k contenant des chaînes.

Il existe 3 approches pour renvoyer le contenu de la chaîne:

  1. Créez une chaîne [] au moment de l'analyse, un caractère à la fois. Cela prendra un temps considérable. Nous pouvons utiliser == au lieu de .equals pour comparer les références mises en cache.

  2. Construisez un int [] avec des décalages au moment de l'analyse, puis construisez dynamiquement String quand un get () se produit. Chaque chaîne sera un nouvel objet, donc pas de mise en cache des valeurs retournées et en utilisant ==

  3. Créez une CharSequence [] au moment de l'analyse. Puisqu'aucune nouvelle donnée n'est stockée (autre que les décalages dans le tampon d'octets), l'analyse est beaucoup plus faible que # 1. Au moment d'obtenir, nous n'avons pas besoin de construire une chaîne, donc les performances d'obtention sont égales à # 1 (bien mieux que # 2), car nous ne renvoyons qu'une référence à un objet existant.

En plus des gains de traitement que vous obtenez en utilisant CharSequence, vous réduisez également l'empreinte mémoire en ne dupliquant pas les données. Par exemple, si vous avez un tampon contenant 3 paragraphes de texte et que vous souhaitez renvoyer les 3 ou un seul paragraphe, vous avez besoin de 4 chaînes pour représenter cela. En utilisant CharSequence, vous n'avez besoin que d'un tampon avec les données et de 4 instances d'une implémentation CharSequence qui suit le début et la longueur.

Phil Lello
la source
6
références plz. ressemble à deviner au hasard ce qui se passe. Je ne trouve pas non plus votre argument valable. on pourrait simplement stocker le buffer de 500k sous forme de chaîne en premier lieu et simplement retourner des sous-chaînes, ce qui est très rapide et beaucoup plus courant.
kritzikratzi
6
@kritzikratzi - à partir de JDK7, la sous-chaîne sur String ne partage plus le tableau sous-jacent et n'est pas "très rapide". Cela prend du temps O (N) dans la longueur de la sous-chaîne et génère une copie des caractères sous-jacents chaque fois que vous l'appelez (donc beaucoup de déchets).
BeeOnRope
@kritzikratzi Je pense que la raison du changement est que, si la copie n'était pas effectuée, la chaîne d'origine serait conservée pour la durée de vie de toutes les sous-chaînes. Étant donné que les sous-chaînes ne sont généralement que de petites parties de l'original et peuvent durer indéfiniment selon la façon dont elles sont utilisées, cela entraînerait souvent encore plus de déchets si les sous-chaînes étaient utilisées pendant beaucoup plus longtemps que la chaîne d'origine. Une alternative intéressante pourrait être de déterminer s'il faut copier ou non en fonction du rapport de la sous-chaîne à la taille de la chaîne parent, mais vous devrez rouler votre propre CharSequenceimplémentation pour cela.
JAB
1
J'ai peut-être manqué le point, mais cette réponse est absurde. A CharSequenceest une interface - par définition, elle n'a aucun des détails d'implémentation dont vous discutez car elle n'a pas d'implémentation propre. A Stringest l'une des nombreuses classes concrètes qui implémentent l' CharSequenceinterface. Donc, String c'est un CharSequence. Vous pouvez comparer les détails des performances de Stringvs StringBuffervs StringBuilder, mais pas CharSequence. Écrire «les gains de traitement que vous obtenez en utilisant CharSequence» n'a aucun sens.
Basil Bourque
7

Un problème qui survient dans le code Android pratique est que leur comparaison avec CharSequence.equals est valide mais ne fonctionne pas nécessairement comme prévu.

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

La comparaison doit être faite par

("OK").contentEquals(t.GetText()); 
Crypth
la source
5

CharSequence

A CharSequenceest une interface, pas une classe réelle. Une interface n'est qu'un ensemble de règles (méthodes) qu'une classe doit contenir si elle implémente l'interface. Dans Android a CharSequenceest un parapluie pour différents types de chaînes de texte. Voici quelques-uns des plus courants:

(Vous pouvez en savoir plus sur les différences entre ces éléments ici .)

Si vous avez un CharSequenceobjet, il s'agit en fait d'un objet de l'une des classes qui implémentent CharSequence. Par exemple:

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

L'avantage d'avoir un type générique général CharSequenceest que vous pouvez gérer plusieurs types avec une seule méthode. Par exemple, si j'ai une méthode qui prend un CharSequencecomme paramètre, je pourrais passer un Stringou un SpannableStringBuilderet il gérerait l'un ou l'autre.

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

Chaîne

On pourrait dire que a Stringn'est qu'un type CharSequence. Cependant, contrairement à CharSequence, il s'agit d'une classe réelle, vous pouvez donc en créer des objets. Vous pouvez donc faire ceci:

String myString = new String();

mais vous ne pouvez pas faire ça:

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

Puisqu'il ne CharSequences'agit que d'une liste de règles Stringconformes, vous pouvez le faire:

CharSequence myString = new String();

Cela signifie que chaque fois qu'une méthode demande un CharSequence, il est bon de lui donner un String.

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

// ...

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

Cependant, l'inverse n'est pas vrai. Si la méthode prend un Stringparamètre, vous ne pouvez pas lui transmettre quelque chose qui n'est généralement connu que comme étant un CharSequence, car il peut en fait s'agir d'un SpannableStringou d'un autre type de CharSequence.

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

// ...

public int getLength(String text) {
    return text.length();
}
Suragch
la source
0

CharSequenceest une interface et l' Stringimplémente. Vous pouvez instancier un Stringmais vous ne pouvez pas le faire CharSequencecar il s'agit d'une interface. Vous pouvez trouver d'autres implémentations CharSequencedans le site officiel de Java.

Xiaogang
la source
-4

CharSequence est une séquence lisible de valeurs char qui implémente String. il a 4 méthodes

  1. charAt (int index)
  2. longueur()
  3. subSequence (int start, int end)
  4. toString ()

Veuillez consulter la documentation Documentation CharSequence

Srinivas Thammanaboina
la source
6
CharSequencene met pas en œuvre String. L'inverse est vrai, cependant.
seh
1
Veuillez supprimer cette réponse absurde
J. Doe