web-dev-qa-db-fra.com

Passer une chaîne par référence en Java?

J'ai l'habitude de faire ce qui suit dans C:

void main() {
    String zText = "";
    fillString(zText);
    printf(zText);
}

void fillString(String zText) {
    zText += "foo";
}

Et le résultat est:

foo

Cependant, en Java, cela ne semble pas fonctionner. Je suppose que parce que l'objet String est copié au lieu de transmis par référencé. Je pensais que les chaînes étaient des objets, qui sont toujours passés par référence.

Qu'est-ce qui se passe ici?

146
Soren Johnson

Vous avez trois options:

  1. Utilisez un StringBuilder:

    StringBuilder zText = new StringBuilder ();
    void fillString(StringBuilder zText) { zText.append ("foo"); }
    
  2. Créez une classe de conteneur et transmettez une instance du conteneur à votre méthode:

    public class Container { public String data; }
    void fillString(Container c) { c.data += "foo"; }
    
  3. Créer un tableau:

    new String[] zText = new String[1];
    zText[0] = "";
    
    void fillString(String[] zText) { zText[0] += "foo"; }
    

Du point de vue des performances, StringBuilder est généralement la meilleure option.

185
Aaron Digulla

Dans Java , rien n'est passé par référence . Tout est passé par valeur. Références d'objet De plus, les chaînes sont immutable. Ainsi, lorsque vous ajoutez à la chaîne passée, vous obtenez simplement une nouvelle chaîne. Vous pouvez utiliser une valeur de retour ou transmettre un StringBuffer à la place.

81
zedoo

Ce qui se passe est que la référence est passée par valeur, c’est-à-dire qu’un copie de la référence est passé. Rien dans Java est passé par référence, et comme une chaîne est immuable, cette affectation crée un nouvel objet chaîne vers lequel la copie de la référence = pointe maintenant. L'original référence pointe toujours sur la chaîne vide.

Ce serait la même chose pour n’importe quel objet, c’est-à-dire en lui attribuant une nouvelle valeur dans une méthode. L'exemple ci-dessous montre simplement ce qui se passe, mais concaténer une chaîne est en réalité la même chose.

void foo( object o )
{
    o = new Object( );  // original reference still points to old value on the heap
}
49
Ed S.

Java.lang.String est immuable.

Je déteste coller des URL mais https://docs.Oracle.com/javase/10/docs/api/Java/lang/String.html est essentiel pour que vous lisiez et compreniez si vous y êtes Java-land.

18
Ryan Fernandes

les objets sont passés par référence, les primitives par valeur.

String n'est pas une primitive, c'est un objet et c'est un cas particulier d'objet.

Ceci dans un but d'économie de mémoire. Dans la machine virtuelle Java, il existe un pool de chaînes. Pour chaque chaîne créée, JVM essaiera de voir si la même chaîne existe dans le pool de chaînes et la pointera s'il en existe déjà une.

public class TestString
{
    private static String a = "hello world";
    private static String b = "hello world";
    private static String c = "hello " + "world";
    private static String d = new String("hello world");

    private static Object o1 = new Object();
    private static Object o2 = new Object();

    public static void main(String[] args)
    {
        System.out.println("a==b:"+(a == b));
        System.out.println("a==c:"+(a == c));
        System.out.println("a==d:"+(a == d));
        System.out.println("a.equals(d):"+(a.equals(d)));
        System.out.println("o1==o2:"+(o1 == o2));

        passString(a);
        passString(d);
    }

    public static void passString(String s)
    {
        System.out.println("passString:"+(a == s));
    }
}

/ * SORTIE * /

a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false

le == vérifie l'adresse de la mémoire (référence) et le .equals vérifie le contenu (valeur)

7
janetsmith

Tous les arguments dans Java sont passés par valeur. Lorsque vous passez un String à une fonction, la valeur passée est une référence à un objet String , mais vous ne pouvez pas modifier cette référence et l’objet String sous-jacent est immuable.

La tâche

zText += foo;

est équivalent à:

zText = new String(zText + "foo");

En d'autres termes, il réaffecte (localement) le paramètre zText en tant que nouvelle référence, qui pointe vers un nouvel emplacement de mémoire, dans lequel se trouve un nouveau String contenant le contenu d'origine de zText avec "foo" ajouté.

L'objet d'origine n'est pas modifié et la variable locale zText de la méthode main() pointe toujours sur la chaîne d'origine (vide).

class StringFiller {
  static void fillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    fillString(zText);
    System.out.println("Final value: " + zText);
  }
}

impressions:

Original value:
Local value: foo
Final value:

Si vous souhaitez modifier la chaîne, vous pouvez, comme indiqué, utiliser StringBuilder ou un conteneur (un tableau ou un AtomicReference ou une classe de conteneur personnalisée) qui vous donne un niveau supplémentaire d'indirection de pointeur. Sinon, il vous suffit de renvoyer la nouvelle valeur et de l’affecter:

class StringFiller2 {
  static String fillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
    return zText;
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    zText = fillString(zText);
    System.out.println("Final value: " + zText);
  }
}

impressions:

Original value:
Local value: foo
Final value: foo

C’est probablement la solution la plus semblable à Java dans le cas général - voir l’article Java effectif "Favoriser l’immuabilité".

Comme indiqué précédemment, StringBuilder vous donnera souvent de meilleures performances - si vous avez beaucoup à ajouter, en particulier dans une boucle, utilisez StringBuilder.

Mais essayez de faire passer le Strings immuable plutôt que le StringBuilders mutable si vous le pouvez - votre code sera plus facile à lire et plus facile à gérer. Pensez à définir vos paramètres final et à configurer votre IDE) pour vous avertir lorsque vous réaffectez un paramètre de méthode à une nouvelle valeur.

6
David Moles

String est un objet immuable en Java. Vous pouvez utiliser la classe StringBuilder pour effectuer le travail que vous essayez d'accomplir, comme suit:

public static void main(String[] args)
{
    StringBuilder sb = new StringBuilder("hello, world!");
    System.out.println(sb);
    foo(sb);
    System.out.println(sb);

}

public static void foo(StringBuilder str)
{
    str.delete(0, str.length());
    str.append("String has been modified");
}

Une autre option consiste à créer une classe avec une chaîne comme variable d’étendue (fortement déconseillée) comme suit:

class MyString
{
    public String value;
}

public static void main(String[] args)
{
    MyString ms = new MyString();
    ms.value = "Hello, World!";

}

public static void foo(MyString str)
{
    str.value = "String has been modified";
}
5
Fadi Hanna AL-Kass

String est une classe spéciale en Java. C'est Thread Safe qui signifie "une fois qu'une instance String est créée, le contenu de l'instance String ne sera jamais modifié".

Voici ce qui se passe pour

 zText += "foo";

Tout d’abord, Java obtiendra la valeur de l’instance zText String, puis créez une nouvelle instance String dont la valeur est zText en ajoutant "foo". Vous savez donc pourquoi l’instance pointée par zText n’a pas été modifiée. En fait, même String "foo" est une nouvelle instance de String. Ainsi, pour cette instruction, Java créera deux instances de String, l’un est "foo", une autre est la valeur de zText append "foo". La règle est simple: la valeur de l'occurrence de chaîne ne sera jamais modifiée.

Pour la méthode fillString, vous pouvez utiliser un StringBuffer en tant que paramètre ou le modifier comme suit:

String fillString(String zText) {
    return zText += "foo";
}
2
DeepNightTwo

La réponse est simple Dans Java sont immuables. C'est donc comme si vous utilisiez le modificateur 'final' (ou 'const' en C/C++). Ainsi, une fois assigné, vous ne pouvez plus le changer comme vous l'avez fait auparavant.

Vous pouvez changer la valeur qui à laquelle une chaîne est pointée, mais vous ne pouvez PAS changer la valeur réelle sur laquelle cette chaîne pointe actuellement.

C'est à dire. String s1 = "hey". Tu peux faire s1 = "woah", et c’est tout à fait correct, mais vous ne pouvez pas réellement changer la valeur sous-jacente de la chaîne (dans ce cas: "hé") pour qu’elle soit autre chose une fois assignée avec plusEquals, etc. (c.-à-d. s1 += " whatup != "hey whatup").

Pour ce faire, utilisez les classes StringBuilder ou StringBuffer ou d'autres conteneurs mutables, puis appelez simplement .toString () pour reconvertir l'objet en chaîne.

remarque: les chaînes sont souvent utilisées comme clés de hachage, ce qui explique en partie pourquoi elles sont immuables.

2
ak_2050

Cela fonctionne utiliser StringBuffer

public class test {
 public static void main(String[] args) {
 StringBuffer zText = new StringBuffer("");
    fillString(zText);
    System.out.println(zText.toString());
 }
  static void fillString(StringBuffer zText) {
    zText .append("foo");
}
}

Encore mieux utiliser StringBuilder

public class test {
 public static void main(String[] args) {
 StringBuilder zText = new StringBuilder("");
    fillString(zText);
    System.out.println(zText.toString());
 }
  static void fillString(StringBuilder zText) {
    zText .append("foo");
}
}
1
Mohammed Rafeeq

String est immuable en Java. vous ne pouvez pas modifier/changer un littéral/objet de chaîne existant.

String s = "Bonjour"; s = s + "salut";

Ici, la référence précédente s est remplacée par la nouvelle référence s indiquant la valeur "HelloHi".

Cependant, pour apporter la mutabilité, nous avons StringBuilder et StringBuffer.

StringBuilder s = new StringBuilder (); s.append ("Hi");

ceci ajoute la nouvelle valeur "Hi" à la même référence. //

1
gauravJ

Les chaînes sont immuable en Java.

1
Grzegorz Oledzki

Pour passer un objet (y compris String) par référence en Java, vous pouvez le transmettre en tant que membre d'un adaptateur environnant. Une solution avec un générique est ici:

import Java.io.Serializable;

public class ByRef<T extends Object> implements Serializable
{
    private static final long serialVersionUID = 6310102145974374589L;

    T v;

    public ByRef(T v)
    {
        this.v = v;
    }

    public ByRef()
    {
        v = null;
    }

    public void set(T nv)
    {
        v = nv;
    }

    public T get()
    {
        return v;
    }

// ------------------------------------------------------------------

    static void fillString(ByRef<String> zText)
    {
        zText.set(zText.get() + "foo");
    }

    public static void main(String args[])
    {
        final ByRef<String> zText = new ByRef<String>(new String(""));
        fillString(zText);
        System.out.println(zText.get());
    }
}
0
Sam Ginrich

Pour quelqu'un qui est plus curieux

class Testt {
    static void Display(String s , String varname){
        System.out.println(varname + " variable data = "+ s + " :: address hashmap =  " + s.hashCode());
    }

    static void changeto(String s , String t){
        System.out.println("entered function");
        Display(s , "s");
        s = t ;
        Display(s,"s");
        System.out.println("exiting function");
    }

    public static void main(String args[]){
        String s =  "hi" ;
        Display(s,"s");
        changeto(s,"bye");
        Display(s,"s");
    }
}

Maintenant, en exécutant ce code ci-dessus, vous pouvez voir comment l'adresse hashcodes change avec la variable String s. un nouvel objet est alloué à la variable s dans la fonction changeto lorsque s est modifié

0
Rajesh kumar

Aaron Digulla a la meilleure réponse jusqu'à présent. Une variante de sa deuxième option consiste à utiliser le wrapper ou la classe de conteneur MutableObject de la bibliothèque commune de Lang, version 3+:

void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }

vous enregistrez la déclaration de la classe de conteneur. L'inconvénient est une dépendance au commons lang lib. Mais la bibliothèque a beaucoup de fonctions utiles et presque tous les grands projets sur lesquels j'ai travaillé l’utilisaient.

0
user2606740