Pourquoi String est-il immuable en Java?


78

Je ne pouvais pas comprendre la raison de cela. J'utilise toujours la classe String comme les autres développeurs, mais lorsque j'en modifie la valeur, une nouvelle instance de String est créée.

Quelle pourrait être la raison de l'immuabilité de la classe String en Java?

Je sais qu'il existe des alternatives telles que StringBuffer ou StringBuilder. C'est juste de la curiosité.


20
Techniquement, ce n'est pas un doublon, mais Eric Lippert donne ici une excellente réponse à cette question: programmers.stackexchange.com/a/190913/33843
Heinzi

Réponses:


105

Simultanéité

Java a été défini dès le départ avec des considérations de simultanéité. Comme souvent mentionné, les mutables partagés sont problématiques. Une chose peut en changer une autre derrière le dos d'un autre thread sans que ce dernier en soit conscient.

Une multitude de bogues C ++ multithreads sont apparus à cause d'une chaîne partagée - un module a pensé qu'il était prudent de le modifier lorsqu'un autre module du code avait enregistré un pointeur sur celui-ci et s'attendait à ce qu'il reste identique.

La «solution» à cela est que chaque classe crée une copie défensive des objets mutables qui lui sont transmis. Pour les chaînes mutables, c’est O (n) pour faire la copie. Pour les chaînes immuables, faire une copie est O (1) car ce n'est pas une copie, c'est le même objet qui ne peut pas changer.

Dans un environnement multithread, les objets immuables peuvent toujours être partagés en toute sécurité. Cela entraîne une réduction globale de l'utilisation de la mémoire et améliore la mise en cache de la mémoire.

Sécurité

Souvent, les chaînes sont transmises comme arguments aux constructeurs - les connexions réseau et les protocoles sont les deux qui viennent le plus facilement à l’esprit. Pouvoir modifier cela à un moment indéterminé plus tard au cours de l'exécution peut poser des problèmes de sécurité (la fonction pensait qu'elle se connectait à une machine, mais qu'elle était déviée vers une autre, mais tout ce qui se trouve dans l'objet a l'air d'être connecté à la première ... c'est même la même chaîne).

Java permet d'utiliser la réflexion - et les paramètres pour cela sont des chaînes. Le danger de passer une chaîne qui peut être modifiée en passant à une autre méthode qui reflète. C'est très mauvais.

Les clés du hachage

La table de hachage est l'une des structures de données les plus utilisées. Les clés de la structure de données sont très souvent des chaînes. Avoir des chaînes immuables signifie que (comme ci-dessus) la table de hachage n'a pas besoin de faire une copie de la clé de hachage à chaque fois. Si les chaînes étaient mutables et que la table de hachage ne le permettait pas, il serait possible que quelque chose change la clé de hachage à distance.

La façon dont fonctionne l’objet en java, c’est que tout a une clé de hachage (accessible via la méthode hashCode ()). Avoir une chaîne immuable signifie que le hashCode peut être mis en cache. Compte tenu de la fréquence à laquelle les chaînes sont utilisées comme clés d'un hachage, cela améliore considérablement les performances (au lieu de devoir recalculer le code de hachage à chaque fois).

Substrings

Si la chaîne est immuable, le tableau de caractères sous-jacent qui sauvegarde la structure de données est également immuable. Cela permet certaines optimisations sur la substringméthode à effectuer (elles ne le sont pas nécessairement - cela introduit également la possibilité de fuites de mémoire).

Si tu fais:

String foo = "smiles";
String bar = foo.substring(1,5);

La valeur de barest 'mile'. Cependant, les deux fooet barpeuvent être sauvegardés par le même tableau de caractères, ce qui réduit l'instanciation de plusieurs tableaux de caractères ou le copie, à l'aide de points de début et de fin différents dans la chaîne.

foo | | (0, 6)
    vv
    sourit
     ^ ^
bar | | (1, 5)

L’inconvénient de cela (la fuite de mémoire) est que si l’on avait une chaîne longue de 1k et que l’on prenait la sous-chaîne du premier et du deuxième caractère, il serait également sauvegardé par le tableau de caractères long de 1k. Ce tableau resterait en mémoire même si la chaîne d'origine ayant une valeur de l'ensemble du tableau de caractères était récupérée.

On peut voir cela dans String de JDK 6b14 (le code suivant provient d’une source GPL v2 et est utilisé à titre d’exemple).

   public String(char value[], int offset, int count) {
       if (offset < 0) {
           throw new StringIndexOutOfBoundsException(offset);
       }
       if (count < 0) {
           throw new StringIndexOutOfBoundsException(count);
       }
       // Note: offset or count might be near -1>>>1.
       if (offset > value.length - count) {
           throw new StringIndexOutOfBoundsException(offset + count);
       }
       this.offset = 0;
       this.count = count;
       this.value = Arrays.copyOfRange(value, offset, offset+count);
   }

   // Package private constructor which shares value array for speed.
   String(int offset, int count, char value[]) {
       this.value = value;
       this.offset = offset;
       this.count = count;
   }

   public String substring(int beginIndex, int endIndex) {
       if (beginIndex < 0) {
           throw new StringIndexOutOfBoundsException(beginIndex);
       }
       if (endIndex > count) {
           throw new StringIndexOutOfBoundsException(endIndex);
       }
       if (beginIndex > endIndex) {
           throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
       }
       return ((beginIndex == 0) && (endIndex == count)) ? this :
           new String(offset + beginIndex, endIndex - beginIndex, value);
   }

Notez que la sous-chaîne utilise le constructeur String au niveau du package qui n'implique aucune copie du tableau et serait beaucoup plus rapide (au détriment de la conservation de certains tableaux de grande taille - sans toutefois dupliquer les tableaux de grande taille).

Notez que le code ci-dessus est pour Java 1.6. La façon dont le constructeur de sous-chaînes est implémenté a été modifiée avec Java 1.7, comme indiqué dans la représentation interne Modifications de chaîne effectuée en Java 1.7.0_06 - le problème de cette fuite de mémoire que j'ai mentionné ci-dessus. Java n'était probablement pas considéré comme un langage avec beaucoup de manipulations de chaînes et l'amélioration des performances d'une sous-chaîne était donc une bonne chose. Maintenant, avec d'énormes documents XML stockés dans des chaînes qui ne sont jamais collectées, cela devient un problème ... et donc la modification consiste à ne Stringpas utiliser le même tableau sous-jacent avec une sous-chaîne, de sorte que le tableau de caractères plus grand puisse être collecté plus rapidement.

Ne pas abuser de la pile

On pourrait transmettre la valeur de la chaîne au lieu de la référence à la chaîne immuable pour éviter les problèmes de mutabilité. Cependant, avec des chaînes de grande taille, transmettre ceci sur la pile serait ... abusif pour le système (placer des documents xml entiers sous forme de chaînes sur la pile puis les enlever ou continuer à les transmettre ...).

La possibilité de déduplication

Certes, ce n'était pas une motivation initiale pour laquelle les chaînes devraient être immuables, mais quand on examine la raison pour laquelle les chaînes immuables sont une bonne chose, c'est certainement une chose à considérer.

Quiconque a un peu travaillé avec Strings sait qu’il peut aspirer de la mémoire. Cela est particulièrement vrai lorsque vous effectuez des opérations telles que l'extraction de données à partir de bases de données pendant un certain temps. Plusieurs fois avec ces piqûres, ils sont la même chaîne encore et encore (une fois pour chaque ligne).

De nombreuses applications Java à grande échelle sont actuellement saturées en mémoire. Les mesures ont montré qu'environ 25% du jeu de données en temps réel Java dans ces types d'applications sont consommés par des objets String. En outre, environ la moitié de ces objets String sont des doublons, doublons signifiant que string1.equals (string2) est true. Le fait de dupliquer des objets String sur le tas est essentiellement un gaspillage de mémoire. ...

JEP 192 (motivation citée ci-dessus) est en cours de mise en œuvre avec Java 8 mise à jour 20 pour résoudre ce problème. Sans entrer dans les détails du fonctionnement de la déduplication des chaînes, il est essentiel que les chaînes elles-mêmes soient immuables. Vous ne pouvez pas dédupliquer StringBuilders car ils peuvent changer et vous ne voulez pas que quelqu'un change quelque chose sous vous. Chaînes immuables (liées à ce pool de chaînes) signifie que vous pouvez passer et si vous trouvez deux chaînes identiques, vous pouvez pointer une référence de chaîne vers une autre et laisser le garbage collector utiliser la nouvelle.

Autres langues

L’objectif C (qui est antérieur à Java) a NSStringet NSMutableString.

C # et .NET ont fait les mêmes choix de conception, la chaîne par défaut étant immuable.

Les cordes Lua sont également immuables.

Python aussi.

Historiquement, Lisp, Scheme, Smalltalk intègrent la chaîne et la rendent immuable. Les langages dynamiques plus modernes utilisent souvent des chaînes d'une manière qui les oblige à être immuables (ce n'est peut-être pas une chaîne , mais c'est immuable).

Conclusion

Ces considérations de conception ont été répétées dans une multitude de langues. Il est généralement admis que les chaînes immuables, malgré leur maladresse, sont meilleures que les alternatives et conduisent à un meilleur code (moins de bugs) et à des exécutables plus rapides.


3
Java fournit des chaînes mutables et immuables. Cette réponse détaille certains avantages en termes de performances pouvant être obtenus sur des chaînes immuables, ainsi que certaines raisons permettant de choisir des données immuables. mais ne discute pas pourquoi la version immuable est la version par défaut.
Billy ONeal

3
@BillyONeal: une solution sécurisée par défaut et une alternative non sécurisée conduit presque toujours à des systèmes plus sécurisés que l'approche inverse.
Joachim Sauer

4
@BillyONeal Si l'immuable n'était pas la valeur par défaut, les problèmes de concurrence, de sécurité et de hachage seraient plus courants. Les concepteurs de langage ont choisi (en partie en réponse à C) de créer un langage dans lequel les paramètres par défaut sont configurés pour éviter un certain nombre de bogues courants afin d'améliorer l'efficacité du programmeur (ne plus avoir à se soucier de ces bogues). Il y a moins de bugs (évidents et cachés) avec des chaînes immuables que avec des chaînes mutables.

@ Joachim: Je ne prétends pas le contraire.
Billy ONeal

1
Techniquement, Common Lisp a des chaînes mutables, pour des opérations "ressemblant à des chaînes" et des symboles avec des noms immuables pour des identificateurs immuables.
Vatine

21

Raisons dont je peux me souvenir:

  1. Un pool de chaînes sans rendre la chaîne immuable n'est pas du tout possible, car dans le cas du pool de chaînes, un objet / littéral de chaîne, par exemple "XYZ", sera référencé par de nombreuses variables de référence. Ainsi, si l'une d'elles change, la valeur des autres sera automatiquement affectée. .

  2. La chaîne a été largement utilisée comme paramètre pour de nombreuses classes java, par exemple pour ouvrir une connexion réseau, pour ouvrir une connexion de base de données, ouvrir des fichiers. Si String n'est pas immuable, cela entraînerait une grave menace pour la sécurité.

  3. Immutability permet à String de mettre en cache son hashcode.

  4. Le rend thread-safe.


7

1) Pool Pool

Le concepteur Java sait que String sera le type de données le plus utilisé dans toutes sortes d’applications Java et c’est pourquoi il a voulu optimiser dès le début. L’une des principales étapes dans cette direction était l’idée de stocker les littéraux de chaîne dans le pool de chaînes. L'objectif était de réduire les objets String temporaires en les partageant et pour pouvoir les partager, ils devaient obligatoirement appartenir à la classe Immutable. Vous ne pouvez pas partager un objet mutable avec deux parties qui se sont inconnues. Prenons un exemple hypothétique, où deux variables de référence pointent sur le même objet String:

String s1 = "Java";
String s2 = "Java";

Maintenant, si s1 modifie l'objet de "Java" en "C ++", la variable de référence a également reçu la valeur s2 = "C ++", dont elle n'est même pas au courant. En rendant String immuable, ce partage de littéral était possible. En bref, l'idée clé du pool de chaînes ne peut pas être mise en œuvre sans rendre String final ou immuable en Java.

2) la sécurité

Java a clairement pour objectif de fournir un environnement sécurisé à tous les niveaux de service et String est essentiel pour tout ce qui concerne la sécurité. String a été largement utilisé comme paramètre pour de nombreuses classes Java. Par exemple, pour ouvrir une connexion réseau, vous pouvez passer l’hôte et le port sous la forme String, pour lire les fichiers en Java, vous pouvez indiquer le chemin des fichiers et le répertoire sous forme de String et pour ouvrir la connexion à la base de données. transmettre l'URL de la base de données sous forme de chaîne. Si String n'était pas immuable, un utilisateur aurait peut-être autorisé l'accès à un fichier particulier dans le système, mais après s'être authentifié, il peut remplacer le chemin PATH par un autre, ce qui pourrait poser de graves problèmes de sécurité. De même, lors de la connexion à une base de données ou à une autre machine du réseau, la mutation de la valeur de String peut constituer une menace pour la sécurité. Les chaînes mutables peuvent également causer des problèmes de sécurité dans Reflection.

3) Utilisation de la ficelle dans le mécanisme de chargement de classe

Une autre raison pour laquelle String final ou Immutable était motivé par le fait qu’elle était fortement utilisée dans le mécanisme de chargement de classe. String n'étant pas immuable, un attaquant peut profiter de ce fait et une demande de chargement de classes Java standard, par exemple java.io.Reader, peut être remplacée par la classe illicite com.unknown.DataStolenReader. En gardant String final et immuable, nous pouvons au moins nous assurer que JVM charge les classes correctes.

4) Avantages du multithreading

Comme la clé de voûte de Java était l’offre simultanée et le multi-threading, il était logique de penser à la sécurité des threads des objets String. Comme il était prévu que String sera utilisé largement, le fait de le rendre immuable signifie qu'aucune synchronisation externe ne signifie, un code beaucoup plus propre impliquant le partage de String entre plusieurs threads. Cette caractéristique unique facilite beaucoup le codage des accès concurrents déjà compliqué, déroutant et sujet aux erreurs. Comme String est immuable et que nous le partageons simplement entre les threads, le code obtenu est plus lisible.

5) Optimisation et performance

Maintenant, quand vous faites une classe Immutable, vous savez à l'avance que cette classe ne va pas changer une fois créée. Cela garantit un chemin ouvert pour de nombreuses optimisations de performances, par exemple la mise en cache. String lui-même sait que, je ne vais pas changer, donc String cache son hashcode. Il calcule même le hashcode lentement et une fois créé, cachez-le simplement. Dans un monde simple, lorsque vous appelez pour la première fois la méthode hashCode () de n'importe quel objet String, elle calcule le code de hachage et tout appel ultérieur à hashCode () renvoie une valeur déjà calculée et mise en cache. Cela se traduit par un bon gain de performances, étant donné que String est fortement utilisé dans les cartes basées sur le hachage, par exemple Hashtable et HashMap. La mise en cache de hashcode n’était pas possible sans la rendre immuable et définitive, car elle dépend du contenu de String lui-même.


5

La machine virtuelle Java effectue plusieurs optimisations concernant les opérations sur les chaînes qui ne pourraient pas être effectuées autrement. Par exemple, si vous avez une chaîne avec la valeur "Mississippi" et que vous avez affecté "Mississippi" .substring (0, 4) à une autre chaîne, une copie a été faite à partir des quatre premiers caractères pour créer "Miss". . Ce que vous ne savez pas, c'est que les deux partagent la même chaîne d'origine "Mississippi", l'un étant le propriétaire et l'autre une référence de cette chaîne de la position 0 à 4. (La référence au propriétaire empêche le propriétaire de le récupérer par le ramasse-miettes lorsque le propriétaire sort de la portée)

Ceci est trivial pour une chaîne aussi petite que "Mississippi", mais avec des chaînes plus grandes et des opérations multiples, ne pas avoir à copier la chaîne est un gain de temps considérable! Si les chaînes étaient mutables, vous ne pourriez pas le faire, car la modification de l'original affecterait également les "copies" de la sous-chaîne.

En outre, comme Donal le mentionne, l’avantage serait grandement alourdi par son inconvénient. Imaginons que vous écriviez un programme qui dépend d’une bibliothèque et que vous utilisiez une fonction qui renvoie une chaîne. Comment pouvez-vous être sûr que cette valeur restera constante? Pour s'assurer que cela ne se produise pas, vous devez toujours en produire une copie.

Que faire si vous avez deux threads partageant la même chaîne? Vous ne voudriez pas lire une chaîne en cours de réécriture par un autre thread, n'est-ce pas? String devrait donc être thread-safe, ce qui serait la classe la plus courante, ce qui ralentirait pratiquement tous les programmes Java. Sinon, vous devrez faire une copie pour chaque thread qui requiert cette chaîne ou vous devrez placer le code utilisant cette chaîne dans un bloc de synchronisation, ce qui ne fera que ralentir votre programme.

Pour toutes ces raisons, cela a été l'une des premières décisions prises pour Java afin de se différencier du C ++.


Théoriquement, vous pouvez effectuer une gestion de la mémoire tampon multi-couche qui permet la copie sur mutation si elle est partagée, mais il est très difficile de travailler efficacement dans un environnement multi-thread.
Donal Fellows

@DonalFellows J'ai juste supposé que, puisque la machine virtuelle Java n'est pas écrite en Java (évidemment), elle est gérée en interne à l'aide de pointeurs partagés ou similaires.
Neil

5

La raison de l'immuabilité de string provient de la cohérence avec d'autres types primitifs du langage. Si vous avez un intcontenant la valeur 42 et que vous y ajoutez la valeur 1, vous ne modifiez pas la 42. Vous obtenez une nouvelle valeur, 43, qui n'a aucun lien avec les valeurs de départ. La mutation de primitives autres que la chaîne n'a aucun sens conceptuel; et en tant que tels programmes qui traitent les chaînes comme immuables sont souvent plus faciles à raisonner et à comprendre.

De plus, Java fournit réellement des chaînes mutables et immuables, comme vous le voyez avec StringBuilder; vraiment, seule la valeur par défaut est la chaîne immuable. Si vous souhaitez faire référence à de StringBuildernombreuses sources, vous êtes parfaitement invité à le faire. Java utilise des types séparés ( Stringet StringBuilder) pour ces concepts car il ne prend pas en charge l'expression de la mutabilité ou de l'absence de mutabilité dans son système de types. Dans les langages qui prennent en charge l'immuabilité dans leurs systèmes de types (par exemple, C ++ const), il existe souvent un seul type de chaîne qui sert les deux objectifs.

Oui, le fait que string soit immuable permet d'implémenter certaines optimisations spécifiques aux chaînes immuables, telles que l'interning, et permet de passer des références de chaîne sans synchronisation entre les threads. Toutefois, cela confond le mécanisme avec l'objectif visé d'un langage avec un système de types simple et cohérent. Je compare cela à la façon dont tout le monde pense à la collecte des ordures dans le mauvais sens; le ramassage des ordures n'est pas une "récupération de la mémoire inutilisée"; c'est "simuler un ordinateur avec une mémoire illimitée" . Les optimisations de performances discutées sont des choses qui sont faites pour que l’objectif de chaînes immuables fonctionne bien sur de vraies machines; pas la raison pour laquelle de telles chaînes sont immuables en premier lieu.


@ Billy-Oneal .. Concernant "Si vous avez un int contenant la valeur 42 et que vous y ajoutez la valeur 1, vous ne changez pas le 42. Vous obtenez une nouvelle valeur, 43, qui n'a aucun rapport avec le début valeurs." Êtes-vous sûr de cela?
Shamit Verma

@Shamit: Oui, j'en suis sûr. Ajouter 1 à 42 résultats dans 43. Cela ne signifie pas que le nombre 42 signifie la même chose que le nombre 43.
Billy ONeal

@Shamit: De même, vous ne pouvez rien faire de pareil 43 = 6et vous vous attendez à ce que le chiffre 43 ait le même sens que le numéro 6.
Billy ONeal le

int i = 42; i = i + 1; ce code stockera 42 en mémoire, puis changera les valeurs au même emplacement en 43. Donc, en fait, la variable "i" acquiert une nouvelle valeur de 43.
Shamit Verma

@Shamit: Dans ce cas, vous avez muté i, pas 42. Envisagez string s = "Hello "; s += "World";. Vous avez muté la valeur de variable s. Mais les chaînes "Hello ", "World"et "Hello World"sont immuables.
Billy ONeal

4

L'immuabilité signifie que les constantes détenues par des classes que vous ne possédez pas ne peuvent pas être modifiées. Les classes que vous ne possédez pas incluent celles qui sont au cœur de l’implémentation de Java, et les chaînes qui ne devraient pas être modifiées incluent des éléments tels que des jetons de sécurité, des adresses de service, etc. Vous ne devriez vraiment pas pouvoir modifier ces tris. des choses (et cela s’applique doublement en mode sandbox).

Si String n'était pas immuable, chaque fois que vous le récupériez dans un contexte ne voulant pas que le contenu de la chaîne soit modifié, vous devez en prendre une copie «au cas où». Cela devient très cher.


4
Ce même argument s'applique à tout type, pas seulement à String. Mais, par exemple, Arrays sont néanmoins mutables. Alors, pourquoi sont-ils Stringimmuables et Arraynon pas. Et si l’immuabilité est si importante, alors pourquoi Java rend-il la création et le travail avec des objets immuables si difficiles?
Jörg W Mittag

1
@ JörgWMittag: Je suppose que c'est essentiellement une question de leur radicalité. Avoir une chaîne immuable était assez radical, à l'époque de Java 1.0. Avoir aussi un cadre de collection (principalement ou même exclusivement) immuable aurait pu être trop radical pour obtenir une utilisation large de la langue.
Joachim Sauer

Faire un framework efficace de collections immuables est assez délicat à rendre performant, parlant comme quelqu'un qui a écrit ça (mais pas en Java). Je souhaite aussi totalement que j'ai immuablement des tableaux; cela m'aurait épargné pas mal de travail.
Donal Fellows

@DonalFellows: pcollections a précisément pour but de faire cela (je ne l'ai jamais utilisé moi-même).
Joachim Sauer

3
@ JörgWMittag: Certaines personnes (généralement d'un point de vue purement fonctionnel) soutiendraient que tous les types devraient être immuables. De même, je pense que si vous additionnez toutes les questions abordées lors de l'utilisation d'un état mutable dans des logiciels parallèles et simultanés, vous conviendrez peut-être que travailler avec des objets immuables est souvent beaucoup plus simple que ceux qui peuvent être mutés.
Steven Evers

2

Imaginez un système dans lequel vous acceptez certaines données, vérifiez leur exactitude, puis transmettez-les (pour les stocker dans une base de données, par exemple).

En supposant que les données soient a Stringet qu’elles doivent contenir au moins 5 caractères. Votre méthode ressemble à ceci:

public void handle(String input) {
  if (input.length() < 5) {
    throw new IllegalArgumentException();
  }
  storeInDatabase(input);
}

Nous pouvons maintenant convenir que, lorsque l’ storeInDatabaseappel est appelé ici, il inputrépondra aux besoins. Mais si elles Stringétaient mutables, l' appelant pourrait modifier l' inputobjet (à partir d'un autre thread) juste après qu'il ait été vérifié et avant qu'il ne soit stocké dans la base de données . Cela nécessiterait un bon timing et ne se déroulerait probablement pas bien à chaque fois, mais il pourrait parfois vous demander de stocker des valeurs non valides dans la base de données.

Les types de données immuables constituent une solution très simple à ce problème (et à de nombreux autres problèmes connexes): chaque fois que vous vérifiez une valeur, vous pouvez compter sur le fait que la condition vérifiée est toujours vraie plus tard.


Merci pour l'explication. Et si j'appelais une méthode de traitement comme celle-ci; handle (nouvelle chaîne (entrée + "naberlan")). Je suppose que je peux stocker des valeurs non valides dans la base de données comme celle-ci.
Yfklon

1
@blank: bien, puisque la inputde la handleméthode est déjà trop longue (peu importe l' origine input est), il serait tout simplement jeter une exception. Vous créez une nouvelle entrée avant d' appeler la méthode. Ce n'est pas un problème.
Joachim Sauer

0

En général, vous rencontrerez des types de valeur et des types de référence . Avec un type de valeur, vous ne vous souciez pas de l'objet qui le représente, vous vous souciez de la valeur. Si je vous attribue une valeur, vous vous attendez à ce que cette valeur reste la même. Vous ne voulez pas que ça change soudainement. Le nombre 5 est une valeur. Vous ne vous attendez pas à ce qu'il passe à 6 soudainement. La chaîne "Bonjour" est une valeur. Vous ne vous attendez pas à ce qu'il devienne soudainement "P *** off".

Avec les types de référence, vous vous souciez de l'objet et vous vous attendez à ce qu'il change. Par exemple, vous vous attendez souvent à ce qu'un tableau change. Si je vous donne un tableau et que vous voulez le conserver tel quel, vous devez soit me faire confiance pour ne pas le changer, soit vous en faire une copie.

Avec la classe de chaînes Java, les concepteurs devaient prendre une décision: est-il préférable que les chaînes se comportent comme un type de valeur ou doivent-elles se comporter comme un type de référence? Dans le cas des chaînes Java, il a été décidé qu'elles devraient être des types valeur, ce qui signifie que, comme il s'agit d'objets, il doit s'agir d'objets immuables.

La décision opposée aurait pu être prise, mais aurait, à mon avis, causé beaucoup de maux de tête. Comme indiqué ailleurs, de nombreuses langues ont pris la même décision et sont parvenues à la même conclusion. Une exception est C ++, qui a une classe de chaîne, et les chaînes peuvent être constantes ou non constantes, mais en C ++, contrairement à Java, les paramètres d'objet peuvent être transmis en tant que valeurs et non en tant que références.


0

Je suis vraiment surpris que personne ne l'a signalé.

Réponse: Cela ne vous apporterait aucun avantage significatif, même s'il était modifiable. Cela ne vous profiterait pas autant que cela cause des problèmes supplémentaires. Examinons les deux cas de mutation les plus courants:

Changer un caractère d'une chaîne

Puisque chaque caractère d'une chaîne Java prend 2 ou 4 octets, demandez-vous, pourriez-vous gagner quelque chose si vous pouviez transformer la copie existante?

Dans le cas où vous remplacez un caractère de 2 octets par un caractère de 4 octets (ou inversement), vous devez décaler le reste de la chaîne de 2 octets à gauche ou à droite. Ce qui n’est pas très différent de copier la chaîne entière du point de vue informatique.

C'est aussi un comportement vraiment irrégulier qui est généralement indésirable. Imaginez que quelqu'un teste une application avec du texte anglais et que l'application soit adoptée par des pays étrangers, tels que la Chine, tout commence à produire des résultats étranges.

Ajouter une autre chaîne (ou caractère) à celle existante

Si vous avez deux chaînes arbitraires, celles-ci se trouvent à deux emplacements de mémoire distincts. Si vous souhaitez modifier le premier en ajoutant le second, vous ne pouvez pas simplement demander de la mémoire supplémentaire à la fin de la première chaîne, car celle-ci est probablement déjà occupée.

Vous devez copier la chaîne concaténée dans un tout nouvel emplacement, exactement comme si les deux chaînes étaient immuables.

Si vous voulez faire des ajouts efficacement, vous pouvez utiliser StringBuilder, ce qui réserve un assez gros espace à la fin d'une chaîne, rien que dans le but d'un éventuel ajout ultérieur.


-2
  1. ils sont chers et leur maintien immuable permet, par exemple, de partager des sous-chaînes partageant le tableau d'octets de la chaîne principale. (Augmentation de la vitesse également car vous n'avez pas besoin de créer un nouveau tableau d'octets et de le copier)

  2. sécurité - ne voudrait pas que votre code de package ou de classe soit renommé

    [Supprimé ancien 3 regardé StringBuilder src - il ne partage pas la mémoire avec une chaîne (jusqu'à ce que modifié) Je pense que c'était en 1.3 ou 1.4]

  3. cache hashcode

  4. pour les chaînes mutalbles, utilisez SB (générateur ou tampon au besoin)


2
1. Bien sûr, il y a la peine de ne pas pouvoir détruire les plus grandes parties de la corde si cela se produit. L'internat n'est pas gratuit; bien que cela améliore les performances de nombreux programmes du monde réel. 2. Il pourrait facilement y avoir "chaîne" et "ImmutableString" qui pourraient satisfaire à cette exigence. 3. Je ne suis pas sûr de comprendre cela ...
Billy ONeal

.3. aurait dû mettre en cache le code de hachage. Cela aussi pourrait être fait avec une chaîne mutable. @ billy-oneal
tgkprog

-4

Les chaînes auraient dû être un type de données primitif en Java. S'ils l'avaient été, les chaînes seraient mutables par défaut et le mot-clé final générerait des chaînes immuables. Les chaînes mutables sont utiles et il existe donc plusieurs hacks pour les chaînes mutables dans les classes stringbuffer, stringbuilder et charsequence.


3
Cela ne répond pas à l'aspect "pourquoi" de ce que c'est maintenant que la question pose. De plus, Java final ne fonctionne pas de cette façon. Les chaînes mutables ne sont pas des hacks, mais plutôt des considérations de conception réelles basées sur les utilisations les plus courantes des chaînes et sur les optimisations pouvant être apportées pour améliorer jvm.

1
La réponse au "pourquoi" est une mauvaise décision en matière de conception linguistique. Trois méthodes légèrement différentes de prise en charge des chaînes mutables sont un hack que le compilateur / JVM devrait gérer.
CWallach

3
String et StringBuffer étaient les originaux. StringBuilder a été ajouté plus tard, reconnaissant une difficulté de conception avec StringBuffer. Des chaînes mutables et immuables constituant des objets différents se retrouvent dans de nombreux langages car la conception a été prise à plusieurs reprises et a décidé qu'il s'agissait d'objets différents à chaque fois. C # "Les chaînes sont immuables" et pourquoi .NET String est immuable? , objectif C NSString est immuable alors que NSMutableString est modifiable. stackoverflow.com/questions/9544182
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.