Frage von stevler, 59

Wieso wird der String nicht verändert (Java)?

Ich wollte Zeichen eines Strings ersetzen und versuchte als erstes nat. die normale s#replace benutzen, aber das funzte nicht und veränderte noch nicht einmal den String. Danach versuchte ich:

        char[] array = s.toCharArray();
        for (char c : array) {
            if (c == '&')
                c = '§';
        }

Beide Methoden gehen nicht und dann versuchte ich halt beim ausgeben s#replace() und komischerweise funktionierte es dann, aber ich möchte eine ganze Liste an Zeichen ersetzen durch andere. So sieht meine Klasse aus, welche den String erbt:

String msg = e.getMessage(); 
UTF_Translator translator = new UTF_Translator();
String msgTranslated = translator.translate(msg);
System.out.println(msgTranslated);//Ich habe das hier mal geschrieben, da es in meiner ausgabe zu groß wäre...

Dieser Code dagegen geht einwandfrei, aber da kann ich halt nur 1 Zeichen ersetzen:

System.out.println(msg.replace('&','§'));

Ps: Es sind 2 Klassen die erste erbt und die Zweite enthält eine Methode für Strings allgemein.

Antwort
von NoHumanBeing, 49

Beim ersten gibt es gleich zwei Gründe, weshalb es nicht funktioniert.

  1. String.toCharArray() kopiert die Zeichen in ein char[]. Dieses ist nicht identisch mit dem String.
  2. "for (char c : array)" holt ein Zeichen aus dem Array in die Variable c. Diese ist vom Typ char. Das ist ein Wertetyp (kein Referenztyp). (Der Typ char ist ein 16-bit unsigned Integer.) Dorthin wird dann also das Zeichen kopiert. Wenn Du nun die Variable c veränderst, hat das natürlich keine Auswirkungen auf das Array. Du hast ja nur eine Zahl aus einem Array in eine Variable kopiert und anschließend den Inhalt dieser Variablen (die ja an sich nichts mit dem Array zu tun hat) verändert. Wenn Du ein Array verändern willst, musst Du es wie folgt machen.
for (int i = 0; i < array.Length; i++)
if (array[i] == '&')
array[i] == '§';

char[] ist ein Referenztyp, char ist ein Wertetyp. Wenn Du also array[i] einer Variablen c vom Typ char zuweist und dann c veränderst, veränderst Du nur den Wert in c, nicht den Wert im Array. Wenn Du array[i] etwas zuweist, veränderst Du den Wert im Array.

Kommentar von stevler ,

Ahh ok ja jetzt habe ich den Fehler auch gesehen xD ok dankeschön

Kommentar von NoHumanBeing ,

In der Zeile innerhalb der Schleife sollte natürlich ein Zuweisungsoperator ("=") stehen, kein Vergleichsoperator ("=="). Mein Fehler. ;-)

Kommentar von stevler ,

Und hättest du auch eine Möglichkeit das mit Wörtern zu machen? Ich würde jetzt mit Substrings anfangen, aber wie kriege ich die Reihenfolge dann wieder hin ?

Kommentar von stevler ,

Habs hinbekommen und hoffe es geht:

String[] arrayS = s.split(" ");for (int i = 0; i < arrayS.length; i++){if (arrayS[i].equalsIgnoreCase("example"))      arrayS[i] = "Word";}
Kommentar von NoHumanBeing ,

Ja, solange Du nur "an Wortgrenzen" trennen möchtest, ist das ok.

Allerdings macht er per "ersetze 'aus' --> 'ein'" nicht "mein schönes Haus" zu "mein schönes Hein", falls Du verstehst, worauf ich hinaus möchte.

Außerdem ist bei "Ich habe ein schönes Haus." das letzte Wort eben "Haus." und nicht etwa "Haus".

Kommentar von NoHumanBeing ,

Der Computer kennt keine "Wörter". Wenn Du "Buchstabenfolgen, die durch Leerzeichen getrennt sind" meinst, würde ich erst am Leerzeichen splitten, dann bekommst Du ein String[]. Dann in einer Schleife alle Elemente vergleichen, wenn das richtige "Wort" gefunden wurde, im Array ersetzen, und anschließend die Strings wieder hintereinander hängen, entweder naiv mit "+"-Operator oder (performanter) über einen StringBuilder per StringBuilder.append(...).

Wenn Du "beliebige Zeichenfolgen" ersetzen möchtest, irgendwo im String, dann musst Du wohl sowas ähnliches, wie den Knuth-Morris-Pratt-Algorithmus implementieren. Das mache ich jetzt hier aber nicht "mal eben", weil das ein bisschen dauert. ;-)

Kommentar von stevler ,

Ja habe alles berücksichtigt:

public String translate(String arg) {String s = arg;s.replaceAll(">>", "»");s.replace("&", "§");s.replace('&', '§');s.replace("heart", "EinHerz ;)");char[] array = s.toCharArray();/*for (char c : array) {if (c == '&')c = '§';}*///Charsfor (int i = 0; i < array.length; i++){   if (array[i] == '&')      array[i] = '§';   }//Words/Stringss = new String(array);String[] arrayS = s.split(" ");for (int i = 0; i < arrayS.length; i++){if (arrayS[i].equalsIgnoreCase("example"))      arrayS[i] = "Word";}s = new String();for(int i = 0; i < arrayS.length; i++){arrayS[i] += " ";s += arrayS[i];}return s;}
Kommentar von NoHumanBeing ,

Ich poste Deinen Code hier nochmal "schön".

public String translate(String arg) {
String s = arg;
s.replaceAll(">>", "»");
s.replace("&", "§");
s.replace('&', '§');
s.replace("heart", "\u2764");
char[] array = s.toCharArray();

/*for (char c : array) {

if (c == '&')
c = '§';

}*/

// Chars
for (int i = 0; i < array.length; i++) {

if (array[i] == '&')
array[i] = '§';

}

// Words/Strings
s = new String(array);
String[] arrayS = s.split(" ");

for (int i = 0; i < arrayS.length; i++) {

if (arrayS[i].equalsIgnoreCase("example"))
arrayS[i] = "Word";

}

s = new String();

for (int i = 0; i < arrayS.length; i++) {
arrayS[i] += " ";
s += arrayS[i];
}

return s;
}

s.replace(...) verändert s aber auch nicht, sondern erzeugt eine Kopie.

Wenn Du möchtest, dass sich "s verändert", musst Du s diese Kopie zuweisen.

s = s.replace(...)

Außerdem ersetzt Du "&" zweimal, einmal als String ("&") und einmal als char ('&'). Einmal reicht. ;-)

Zudem sind die geschweiften Klammern um die If-Abfrage bei "Chars" überflüssig. "For" und "if" kann man direkt ineinander schachteln. Du hast ja keine weiteren Statements (außer der Verzweigung) mehr innerhalb der Schleife.

Zudem verstehe ich nicht so ganz, weshalb Du in der letzten Schleife an die Arrayelemente ein Leerzeichen anhängst und nicht einfach beim "Zusammenbasteln" des Strings.

s = new String();

for (int i = 0; i < arrayS.length; i++)
s += arrayS[i] + " ";

return s;

"new String()" ist auch ne "tolle" Konstruktion. Mach's doch einfach so.

s = "";

;-)

Oder, wenn Du's richtig "schön" machen möchtest, dann mach's mit nem StringBuilder.

StringBuilder builder = new StreingBuilder();

for (int i = 0; i < arrayS.length; i++)
builder.append(arrayS[i] + " ");

return builder.toString();
Kommentar von stevler ,

Ich sag es mal so: Ich schreibe immer jeden "Dreck" hin, aber beim fertigstellen entferne ich ihn, aber lieber mehr dazustehen, wenn ich etwas verändere, als wenn ich dann mich wieder wundere ;) aber danke nochmal an euch beide für die Hilfe Ps: Ja das replace gehört zu den "Dreck"

Antwort
von bergerle, 5

Würde hier zwar schon alles irgendwo in einem Kommentar geschrieben, aber nochmal als direkte antwort: String.replace verändert den string nicht (ein String in Java ist sowieso unveränderlich), sondern gibt eine Kopie des strings mit den Änderungen aus. Diesen neuen String musst du dann deiner variable zuweisen. 

Das mit den chars bringt auch nichts. Die liest die chars in eine variable aus, änderst dann aber den Wert dieser variable, anstatt den Wert im Array anzupassen. 

Genau wegen solchem Situationen bin ich immer noch der Meinung, dass Programmiersprachen wie Java nicht geeignet sind, um mit dem programmieren anzufangen, weil dabei einfach kein Verständnis von pointern und so vermittelt wird. 

Antwort
von Gastnr007, 59

bei replace kannst du auch Strings übergeben (Vorsicht bei Sonderzeichen, da es bestimmte Codes für Gruppen und so gibt) z.B. "Haus H".replace("Hau", "Mau") ergibt "Maus H"

und toCharArray funktioniert nicht, da der String erst in ein Char Array umgewandelt wird, das aber nicht an der gleichen Stelle wie der String liegt

Kommentar von stevler ,

Ich weiß, dass man Strings übergeben kann und beim Array gehe ich halt ja eigtl alle Dimensionen durch also müsste es vom logischen gehen ?

Kommentar von Gastnr007 ,

vom logischem vllt schon, aber der/die/das Array wird neu im RAM angelegt, sodass du, wenn du versuchst in dieses zu schreiben nur im Chararray, aber nicht im String schreibst...

wenn du dein Array speicherst und es dann durch iterierst und am Ende msgtranslated = String.valueOf(chararray) nutzt, um es zurück zuverwandeln, sollte es funktionieren :) - ok nur, wenn du, wie nohumanbeing geschrieben hat, du dabei mit Indexen drauf zugreifst

Kommentar von NoHumanBeing ,

Das stimmt. Aber zusätzlich schreibt seine Schleife ja nicht einmal ins Array. Er liest einen Wert aus dem Array aus und speichert ihn in einer lokalen Variablen. Dann prüft er eine Bedingung und wenn die Befindung zutrifft, ändert er den Wert der lokalen Variablen. Er schreibt den Inhalt der lokalen Variablen aber nirgendwo zurück ins Array.

Kommentar von Gastnr007 ,

ja - bei nicht-nativen(eigener Klassen) Objekten funktionieren Methoden von den Objekten aber interessanter Weise in Java - da wird offenbar nur der Zeiger übergeben

Kommentar von NoHumanBeing ,

Wie meinst Du?

Es gibt in Java "Referenztypen" und "Wertetypen".

Alle Klassen (großer Anfangsbuchstabe) sind "Referenztypen" und verhalten sich im Prinzip wie "Pointer" in C. Das trifft übrigens auch auf "String" zu. Allerdings ist die String-Klasse so aufgebaut, dass die Objekte niemals verändert werden, sondern immer Kopien erzeugt werden. Man sagt auch, Strings sein "immutable". Das hat nichts mit dem Datentyp an sich zu tun, sondern nur damit, wie die Klasse implementiert ist.

Alle primitiven Datentypen (kleiner Anfangsbuchstabe) sind "Wertetypen".

Kommentar von Gastnr007 ,

wenn ich ein Array oder eine Liste von z.B. einer Klasse Spieler durchgehe, kann ich mit setName("newname") trotzdem den Namen des Objektes im Array ändern

Kommentar von NoHumanBeing ,

Ja, das wäre prinzipiell bei "String" auch möglich, nur implementiert die Klasse "String" eben keine Methoden, die das Objekt verändern. ;-)

Alle Methoden in "String" erzeugen ein neues Objekt mit dem Resultat der Methode und geben dieses zurück. Das macht bestimmte Dinge in der JVM einfacher. Beispielsweise kann die JVM Strings mit gleichem Inhalt "zusammenlegen" und durch eine Referenz auf das selbe "String-Objekt" ersetzen. (Muss sie aber nicht, "==" ist daher keine zuverlässige Möglichkeit, inhaltliche Gleichheit von Strings zu prüfen. "Kann" funktionieren, "muss" aber nicht. Immer ".equals(...)" benutzen.)

Kommentar von stevler ,

Ja ist mir auch nach dein Kommentar aufgefallen.

Kommentar von NoHumanBeing ,

Wenn Du das Array veränderst, verändert sich der String nicht. Das Array enthält eine Kopie der Zeichen im String, nicht nur einen Verweis auf die Zeichen im String. Außerdem, sobald Du ein Element aus dem Array ausliest, erhältst Du eine Kopie dieses Elements, nicht nur ein Verweis auf das Element.

array[i] in Java ist äquivalent zu array[i] in C. Das [] ist ein Dereferenzierungsoperator (wie auch der *). array[i] in Java ist nicht äquivalent zu &array[i] in C. Es gibt nichts, was daraus wieder eine Referenz machen würde, nachdem Du es mit dem []-Operator bereits dereferenziert hast.

Keine passende Antwort gefunden?

Fragen Sie die Community