Cela pourrait être la question la plus stupide jamais posée, mais je pense que c'est une confusion totale pour un débutant.
String
est-elle immuable?StringBuilder
devrait-il être préféré à String et vice-verse?Un bel exemple (en Java) sera vraiment apprécié.
Immutable signifie qu'une fois que le constructeur d'un objet a terminé son exécution, cette instance ne peut plus être modifiée.
Cela est utile car cela signifie que vous pouvez passer des références à l'objet sans vous soucier du fait que quelqu'un d'autre va en modifier le contenu. Surtout lorsqu'il s'agit de concurrence, il n'y a pas de problème de verrouillage avec des objets qui ne changent jamais
par exemple.
class Foo
{
private final String myvar;
public Foo(final String initialValue)
{
this.myvar = initialValue;
}
public String getValue()
{
return this.myvar;
}
}
Foo
n'a pas à craindre que l'appelant à getValue()
puisse modifier le texte de la chaîne.
Si vous imaginez une classe similaire à Foo
, mais avec un StringBuilder
plutôt qu'un String
en tant que membre, vous pouvez voir qu'un appelant à getValue()
pourrait modifier l'attribut StringBuilder
d'une instance Foo
.
Méfiez-vous également des différents types d'immutabilité que vous pourriez rencontrer: Eric Lippert a écrit un article de blog à ce sujet. Fondamentalement, vous pouvez avoir des objets dont l'interface est immuable, mais dans les coulisses de l'état privé mutable réel (et ne peuvent donc pas être partagés en toute sécurité entre les threads).
Un objet immuable est un objet pour lequel les champs internes (ou au moins tous les champs internes qui affectent son comportement externe) ne peuvent pas être modifiés.
Les chaînes immuables présentent de nombreux avantages:
Performances: Prenons l'opération suivante:
String substring = fullstring.substring(x,y);
Le C sous-jacent de la méthode substring () ressemble probablement à ceci:
// Assume string is stored like this:
struct String { char* characters; unsigned int length; };
// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
struct String* out = malloc(sizeof(struct String));
out->characters = in->characters + begin;
out->length = end - begin;
return out;
}
Notez que aucun des caractères ne doit être copié! Si l'objet String était modifiable (les caractères pourraient changer ultérieurement), vous devrez alors copier tous les caractères, sinon les modifications apportées aux caractères de la sous-chaîne seraient répercutées ultérieurement dans l'autre chaîne.
Accès simultané: Si la structure interne d'un objet immuable est valide, elle le sera toujours. Il n'y a aucune chance que différents threads puissent créer un état non valide dans cet objet. Par conséquent, les objets immuables sont Thread Safe.
Ramasse-miettes: Il est beaucoup plus facile pour le ramasse-miettes de prendre des décisions logiques concernant les objets immuables.
Cependant, il y a aussi des inconvénients à l'immutabilité:
Performance: Attendez, je pensais que vous aviez dit que la performance était un avantage de l'immuabilité! Eh bien, c'est parfois, mais pas toujours. Prenez le code suivant:
foo = foo.substring(0,4) + "a" + foo.substring(5); // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder
Les deux lignes remplacent le quatrième caractère par la lettre "a". Non seulement le deuxième morceau de code est plus lisible, mais plus rapide. Regardez comment vous devez faire le code sous-jacent pour foo. Les sous-chaînes sont faciles, mais maintenant, comme il y a déjà un caractère dans l'espace cinq et que quelque chose d'autre pourrait faire référence à foo, vous ne pouvez pas simplement le changer; vous devez copier la chaîne entière (bien sûr, certaines de ces fonctionnalités sont résumées dans des fonctions du C réel sous-jacent, mais le but ici est de montrer le code exécuté en un seul endroit).
struct String* concatenate(struct String* first, struct String* second)
{
struct String* new = malloc(sizeof(struct String));
new->length = first->length + second->length;
new->characters = malloc(new->length);
int i;
for(i = 0; i < first->length; i++)
new->characters[i] = first->characters[i];
for(; i - first->length < second->length; i++)
new->characters[i] = second->characters[i - first->length];
return new;
}
// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));
Notez que concatenate est appelée deux fois , ce qui signifie que toute la chaîne doit être bouclée! Comparez ceci au code C pour l'opération bar
:
bar->characters[4] = 'a';
L'opération de chaîne mutable est évidemment beaucoup plus rapide.
En conclusion: Dans la plupart des cas, vous voulez une chaîne immuable. Mais si vous devez ajouter et insérer beaucoup dans une chaîne, vous avez besoin de la mutabilité pour plus de rapidité. Si vous souhaitez profiter des avantages de la sécurité d'accès concurrentiel et de la récupération de place, la clé consiste à conserver vos objets mutables au sein d'une méthode:
// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
StringBuilder mutable;
boolean first = true;
for(int i = 0; i < strings.length; i++)
{
if(!first) first = false;
else mutable.append(separator);
mutable.append(strings[i]);
}
return mutable.toString();
}
L'objet mutable
étant une référence locale, vous n'avez pas à vous soucier de la sécurité d'accès simultané (un seul thread le touche jamais). Et comme il n'est référencé nulle part ailleurs, il est uniquement alloué sur la pile. Il est donc désalloué dès que l'appel de fonction est terminé (vous n'avez pas à vous soucier de la récupération de place). Et vous bénéficiez de tous les avantages en termes de performances de la mutabilité et de l'immutabilité.
En réalité, String n'est pas immuable si vous utilisez la définition de wikipedia proposée ci-dessus.
L'état de String change après la construction. Jetez un coup d'oeil à la méthode hashcode (). String met en cache la valeur hashcode dans un champ local mais ne la calcule pas avant le premier appel de hashcode (). Cette évaluation paresseuse de hashcode place String dans une position intéressante en tant qu'objet immuable dont l'état change, mais il est impossible de constater qu'il a changé sans utiliser la réflexion.
Alors peut-être que la définition de immuable devrait être un objet dont on ne peut pas dire qu'il a changé.
Si l'état d'un objet immuable change après sa création mais que personne ne peut le voir (sans réflexion), l'objet est-il toujours immuable?
Les objets immuables sont des objets qui ne peuvent pas être modifiés par programme. Ils conviennent particulièrement aux environnements multithreads ou à d'autres environnements dans lesquels plusieurs processus peuvent modifier (muter) les valeurs d'un objet.
Juste pour clarifier, cependant, StringBuilder est en réalité un objet modifiable, et non immuable. Une chaîne Java normale est immuable (ce qui signifie qu'une fois créée, vous ne pouvez pas modifier la chaîne sous-jacente sans modifier l'objet).
Par exemple, supposons que j'ai une classe appelée ColoredString qui a une valeur String et une couleur String:
public class ColoredString {
private String color;
private String string;
public ColoredString(String color, String string) {
this.color = color;
this.string = string;
}
public String getColor() { return this.color; }
public String getString() { return this.string; }
public void setColor(String newColor) {
this.color = newColor;
}
}
Dans cet exemple, ColoredString est dit mutable parce que vous pouvez modifier (muter) l'une de ses propriétés de clé sans créer de nouvelle classe ColoredString. La raison pour laquelle cela peut être mauvais est, par exemple, supposons que vous ayez une application graphique qui comporte plusieurs threads et que vous utilisiez ColoredStrings pour imprimer des données dans la fenêtre. Si vous avez une instance de ColoredString créée en tant que
new ColoredString("Blue", "This is a blue string!");
Ensuite, vous vous attendez à ce que la chaîne soit toujours "bleue". Si un autre fil, cependant, a mis la main sur cette instance et a appelé
blueString.setColor("Red");
Vous auriez soudainement, et probablement de manière inattendue, maintenant une chaîne "rouge" lorsque vous en voudriez une "bleue". Pour cette raison, les objets immuables sont presque toujours préférés lors du passage d'instances d'objets. Lorsque vous avez un cas où des objets mutables sont vraiment nécessaires, vous devez généralement protéger l'objet en ne transmettant que des copies de votre champ de contrôle spécifique.
Pour récapituler, en Java, Java.lang.String est un objet immuable (il ne peut pas ne peut pas être modifié une fois créé) et Java.lang.StringBuilder est un objet mutable car il peut être modifié sans créer de nouvelle instance.
"immuable" signifie que vous ne pouvez pas changer de valeur. Si vous avez une instance de la classe String, toute méthode que vous appelez et qui semble modifier la valeur créera en réalité une autre chaîne.
String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"
Pour préserver les changements, vous devriez faire quelque chose comme ceci foo = foo.sustring (3);
Immutable vs mutable peut être amusant lorsque vous travaillez avec des collections. Pensez à ce qui se passera si vous utilisez un objet mutable comme clé pour la carte, puis modifiez la valeur (astuce: pensez à equals
et hashCode
).
Il est peut-être un peu tardif, mais pour comprendre ce qu'est un objet immuable, considérons l'exemple suivant tiré de la nouvelle API Java 8 Date and Time ( Java.time ). Comme vous le savez probablement, tous les objets de date de Java 8 sont immutable, ainsi dans l'exemple suivant
LocalDate date = LocalDate.of(2014, 3, 18);
date.plusYears(2);
System.out.println(date);
Sortie:
2014-03-18
Ceci imprime la même année que la date initiale car la fonction plusYears(2)
renvoie un nouvel objet, de sorte que l'ancienne date reste inchangée car il s'agit d'un objet immuable. Une fois créé, vous ne pouvez plus le modifier et la variable de date pointe toujours dessus.
Ainsi, cet exemple de code devrait capturer et utiliser le nouvel objet instancié et renvoyé par cet appel à plusYears
.
LocalDate date = LocalDate.of(2014, 3, 18);
LocalDate dateAfterTwoYears = date.plusYears(2);
date.toString ()… 2014-03-18
dateAfterTwoYears.toString ()… 2016-03-18
Les objets immuables ne peuvent pas avoir leur état modifié après leur création.
Il y a trois raisons principales d'utiliser des objets immuables chaque fois que vous le pouvez, ce qui contribuera à réduire le nombre de bogues introduits dans votre code:
Vous pouvez également effectuer d'autres optimisations dans le code lorsque vous savez que l'état d'un objet est immuable - la mise en cache du hachage calculé, par exemple -, mais il s'agit d'optimisations et par conséquent pas si intéressantes.
J'aime beaucoup les explications de Guide d'étude du programmeur certifié pour Java 5 par SCJP Sun .
Pour rendre Java plus efficace en termes de mémoire, la machine virtuelle Java met de côté une zone de mémoire spéciale appelée "pool constant de chaînes". Lorsque le compilateur rencontre un littéral String, il vérifie le pool pour voir si une chaîne identique existe déjà. Si une correspondance est trouvée, la référence au nouveau littéral est dirigée vers la chaîne existante et aucun nouvel objet littéral de chaîne n'est créé.
Un sens a à voir avec la façon dont la valeur est stockée dans l'ordinateur. Pour une chaîne .Net par exemple, cela signifie que la chaîne en mémoire ne peut pas être modifiée chaîne en mémoire et en pointant la variable existante (qui est simplement un pointeur sur la collection de caractères ailleurs) sur la nouvelle chaîne.
String s1="Hi";
String s2=s1;
s1="Bye";
System.out.println(s2); //Hi (if String was mutable output would be: Bye)
System.out.println(s1); //Bye
s1="Hi"
: un objet s1
a été créé avec la valeur "Hi".
s2=s1
: un objet s2
est créé en référence à l'objet s1.
s1="Bye"
: la valeur de l'objet s1
précédent ne change pas car s1
a le type String et que le type String est un type immuable, mais le compilateur crée un nouvel objet String avec la valeur "Bye" et le s1
qui y sont référencés. ici, lorsque nous imprimons la valeur s2
, le résultat sera "Hi" et non pas "Bye" car s2
faisait référence à l’objet précédent s1
qui avait la valeur "Hi".
Immuable signifie simplement immuable ou non modifiable. Une fois que l'objet chaîne est créé, ses données ou son état ne peuvent plus être modifiés.
Prenons l'exemple ci-dessous,
class Testimmutablestring{
public static void main(String args[]){
String s="Future";
s.concat(" World");//concat() method appends the string at the end
System.out.println(s);//will print Future because strings are immutable objects
}
}
Ayons une idée en considérant le diagramme ci-dessous,
Dans ce diagramme, vous pouvez voir le nouvel objet créé en tant que "Monde futur". Mais ne change pas "Future" .Because String is immutable
. s
, référez-vous toujours à "Future". Si vous avez besoin d'appeler "Future World",
String s="Future";
s=s.concat(" World");
System.out.println(s);//print Future World
Pourquoi les objets string sont-ils immuables en Java?
Parce que Java utilise le concept de littéral de chaîne. Supposons qu'il y a 5 variables de référence, tout se réfère à un objet "Future" .Si une variable de référence change la valeur de l'objet, il sera affecté à toutes les variables de référence. C'est pourquoi les objets chaîne sont immuables en Java.
Immuable signifie qu'une fois l'objet créé, aucun de ses membres ne changera. String
est immuable car vous ne pouvez pas changer son contenu ..__ Par exemple:
String s1 = " abc ";
String s2 = s1.trim();
Dans le code ci-dessus, la chaîne s1 n'a pas changé, un autre objet (s2
) a été créé à l'aide de s1
.
Une fois instancié, ne peut pas être modifié. Considérons une classe qu'une instance de pourrait être utilisée comme clé pour une table de hachage ou similaire. Découvrez les meilleures pratiques Java.
Un objet est considéré comme immuable si son état ne peut pas changer après est construit. La confiance maximale dans les objets immuables est largement acceptée comme une bonne stratégie pour créer un code simple et fiable.
Les objets immuables sont particulièrement utiles dans les applications concurrentes . Puisqu'ils ne peuvent pas changer d'état, ils ne peuvent pas être corrompus par le thread interférence ou observé dans un état incohérent.
J'aime cette phrase du post
Les objets immuables facilitent la programmation simultanée
Objets immuables
Un objet est considéré comme immuable si son état ne peut pas changer après sa construction. Le recours maximal à des objets immuables est largement reconnu comme une stratégie judicieuse de création de code simple et fiable.
Les objets immuables sont particulièrement utiles dans les applications concurrentes. Comme ils ne peuvent pas changer d'état, ils ne peuvent pas être corrompus par une interférence de threads ni être observés dans un état incohérent.
Les programmeurs sont souvent réticents à employer des objets immuables, car ils s'inquiètent du coût de la création d'un nouvel objet par opposition à la mise à jour d'un objet sur place. L'impact de la création d'objets est souvent surestimé et peut être compensé par certaines des efficacités associées aux objets immuables. Celles-ci incluent une réduction des frais généraux due au ramassage des ordures et l'élimination du code nécessaire pour protéger les objets mutables de la corruption.
Les sous-sections suivantes prennent une classe dont les instances sont modifiables et en dérivent une classe avec des instances immuables. Ce faisant, ils donnent des règles générales pour ce type de conversion et démontrent certains des avantages des objets immuables.