Mehrmals replace() auf langem String, zu speicherintensiv

Status
Nicht offen für weitere Antworten.

gigi99

Mitglied
Ich habe einen seeeeehr langen String.

Darin muss ich viele Ersetzungen vornehmen. Das mache ich in einer for-Schleife (für jede Ersetzung eine Iteration)

Code:
String str =  "seeeeeeeeehr lang usw. ..... ";
for(int i = 0; i<=100000; i++)
{
   str = str.replace(key[i],  value[i]);
}

Das geht zwar einigermaßen schnell, Problem ist aber der Speicherverbrauch.
Wenn in meine Klasse 2x parallell laufen lasse, wird schon gemotzt, dass der virtuelle Speicher aus ist.
Wenn der String noch größer wird / noch mehr Ersetzungen notwendig sind, dann ist wohl OutOfMemoryException zu erwarten.

Ich vermute, dass es mit dem replace zusammenhängt, dass ja viele tausend Mal auf den langen String angewandt wird. Liegt der String vielmals im Speicher?

Wie könnte man das besser machen, ohne auf eine gute Performance zu verzichten?

Danke
 

kleiner_held

Top Contributor
Fuer performante Stringersetzungen wuerde ich immer auf java.util.regex zurueckgreifen (wird intern bei String.replace() sowieso verwendet).
Abgesehen davon wuerde ich schon bei dem Fakt "seeeeehr langer String" ueberlegen, ob man nicht auf eine stream-basierte Vorgehensweise umsteigen kann. Das haengt aber halt von deiner konkreten (Teil-)aufgabe ab und laesst sich nicht pauschal aussagen.
 

gigi99

Mitglied
StringBuffer habe ich auch schon angeschauft. Allerdings muss bei der replace-Methode dort immer ein Anfang und Ende angegeben werden. Geht zwar irgendwie auch, diese herauszufinden, aber vielleicht geht's auch einfacher?!

replace im Zusammenhang mit RegEx schaue ich mir mal an.

Das mit Streams hört sich interessant an. Wäre mir auch am liebsten.
Aber wie kann ich ein replace in einem Stream vornehmen?
 

kleiner_held

Top Contributor
Es kommt drauf an wo deine Daten, die du in dem riesigen String hast, eigentlich her kommen. Wenn du z.B.: aus einer Text-Datei liest, die Ersetzungen vornimmst und wieder in eine Textdatei schreibst, dann ist es wesentlich besser nicht erst die komplette Datei einzulesen, sondern z.B.: Zeilenweise zu arbeiten, also einen BufferedReader verwenden und in einer Schleife jeweils:
1. eine Zeile einlesen
2. Ersetzungen vornehmen
3. die Zeile rausschreiben (in einen Writer)

Die Fragestellung ist also, ob man das ganze auf einen Eingabe->Verarbeitung->Ausgabe Prozess runterbrechen kann, der abschnittsweise (in dem Beispiel zeilenweise) druchfuehrbar ist (anstatt komplett).
 

gigi99

Mitglied
Eigentlich sind mein Ausgangspunkt mehrere ByteArrayOutputStreams, die
ich zu einem String umwandle, konkateniere
und dann die Ersetzungen durchführe (wie beschrieben)
 
M

maki

Gast
kleiner_held hat gesagt.:
Fuer performante Stringersetzungen wuerde ich immer auf java.util.regex zurueckgreifen (wird intern bei String.replace() sowieso verwendet).
Abgesehen davon wuerde ich schon bei dem Fakt "seeeeehr langer String" ueberlegen, ob man nicht auf eine stream-basierte Vorgehensweise umsteigen kann. Das haengt aber halt von deiner konkreten (Teil-)aufgabe ab und laesst sich nicht pauschal aussagen.
Die nicht-regex basierten Methoden sind um einiges schneller und damit performanter als die Regex Version.
StringBuffer#replace nutzt übrigens keine regex, wie denn auch, ersetzt ja nur die angegebn teile des StringBuffers.
 

kleiner_held

Top Contributor
maki hat gesagt.:
Die nicht-regex basierten Methoden sind um einiges schneller und damit performanter als die Regex Version.
StringBuffer#replace nutzt übrigens keine regex, wie denn auch, ersetzt ja nur die angegebn teile des StringBuffers.
Es geht aber eben um den Fall in String A den String B durch C zu ersetzen wobei B und C nicht die gleiche Laenge haben. Und da ist mit Matcher.group(), Matcher.appendReplacement() und Matcher.appendTail() einen StringBuffer neu zu fuellen schneller als StringBuffer.replace(). Denn wenn B.length() nicht gleich C.length() ist, dann ist bei jedem StringBuffer.replace() ein System.arraycopy() faellig und den start und end index muss man sich vorher auch noch besorgen.
 

gigi99

Mitglied
Habe mal mit Pattern/Matcher/StringBuffer experimentiert.
Geht grundsätzlich mal, habe aber noch nicht getestet bzgl.
1. Laufzeit und
2. Speicherverbrauch.

Ist das nur eine Pseudo-Lösung oder könnte damit wirklich weniger Speicher verbraucht werden?



Code:
import java.util.regex.*;

public class ReplaceTest 
{
	public static void main(String[] args) 
	{
		StringBuffer buffer = new StringBuffer("das ist ein test string, der möglicherweise auch ganz lang ist");
		
		ReplaceTest rt = new ReplaceTest();
		buffer = rt.replace(buffer, "test", "TestT");
		buffer = rt.replace(buffer, "ist", "frisst");
		
		System.out.println(buffer.toString());
	}
	
	
	public StringBuffer replace (StringBuffer replaceIn, String findPattern, String replacePatternWith )
	{
		 Pattern p = Pattern.compile(findPattern);
		 Matcher m = p.matcher(replaceIn);
		 StringBuffer sb = new StringBuffer();
		 
		 while (m.find()) 
		 {
		     m.appendReplacement(sb, replacePatternWith);
		 }
		 m.appendTail(sb);
		 
		 
		 return sb;
	}
}
 

Marco13

Top Contributor
Eigentlich ist regex eher langsam ... :?

In bezug auf die Laufzeit (die mit dem "brute force" Verfahren, das du bisher (einmal im ursprünglichen post, und jetzt nochmal mit Regex) verwendet hast, grottenst-schelcht sein dürfte :autsch: ) wäre es hilfreich, zu wissen, WAS dort GENAU gemacht wird. Einige wichtige Fragen wären da solche wie
- Kommen die Keys in der Reihenfolge im String vor, in der sie im keys-Array liegen?
- Gibt es keys, die sich im Input überschneiden?
Z.B.
input="wortest"
key0 = "wort", value0 = "aaaa";
key1 = "test", value1 = "bbbb";
Ergebnis: MUSS "aaaaest" sein - und nicht "aaaabbb" oder "wortbbb" oder "aaabbbb" ....
- Gibt es values, die (auch teilweise) aus keys bestehen?
Z.B.
input="wortest"
key0 = "wort", value0 = "tttt";
key1 = "test", value1 = "bbbb";
Ergebnis: MUSS "tttbbb" sein - und nicht irgendwas anderes....

In bezug auf den Speicher (den du ja als das Hauptproblem siehst...) : Schau mal nach, ob noch "lebende" Referenzen auf deine ByteArrayOutputStreams existieren (müssen). Z.B. könnte sowas Problematisch sein:
Code:
void foo()
{
    ByteArrayOutputStream b0 = createByteArrayOutputStreamWithLotsOfData();
    ByteArrayOutputStream b1 = createByteArrayOutputStreamWithLotsOfData();

    String input = new String(b0.toByteArray())+new String(b1.toByteArray());
    replace(....);
}
Da könnte es schon helfen, dem GC zu erlauben, die ByteArrayOutputStreams wegzuwerfen, indem man sie explizit auf null setzt:
Code:
void foo()
{
    ByteArrayOutputStream b0 = createByteArrayOutputStreamWithLotsOfData();
    ByteArrayOutputStream b1 = createByteArrayOutputStreamWithLotsOfData();

    String input = new String(b0.toByteArray())+new String(b1.toByteArray());

    b0 = null;
    b1 = null;
    // Jetzt kann der Speicher, der vorher von den ByteArrayOutputStreams belegt war, freigegeben werden

    replace(....);
}
 

gigi99

Mitglied
Meine Tests ergaben, dass die zweite Version mit Pattern/Matcher/StringBuffer wesentlich sparsamer mit dem Speicher umgeht.

Stelle jetzt alles um auf StringBuffer (leistet replace) und StringTokenizer (leistet split).

Guggsch du hier:
http://www.particle.kth.se/~lindsey/JavaCourse/Book/Part1/Java/Chapter10/stringBufferToken.html

Auszug:
------------------------------------------
String objects are immutable, meaning that once created they cannot be altered. Concatenating two strings does not modify either string but instead creates a new string object:

String str = "This is ";
str = str + " a new string object";

Here str variable now references a completely new object that holds the "This is a new string object" string.

This is not very efficient if you are doing extensive string manipulation with lots of new strings created through this sort of append operations. The String class maintains a pool of strings in memory. String literals are saved there and new strings are added as they are created. Extensive string manipulation with lots of new strings created with the String append operations can therefore result in lots of memory taken up by unneeded strings.
------------------------------------------
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
Master3000 Java Datei mehrmals einlesen Allgemeine Java-Themen 4
D API Keys mehrmals verwenden Allgemeine Java-Themen 6
J Farbe von Buttons mehrmals ändern Allgemeine Java-Themen 5
M Java (GUI) Code verdoppeln oder anzeige mehrmals anzeigen? Allgemeine Java-Themen 8
X Threads Thread mehrmals verwenden Allgemeine Java-Themen 4
J Threads mehrmals starten Allgemeine Java-Themen 18
I PlayButton mehrmals starten? Allgemeine Java-Themen 6
G Thread mehrmals starten Allgemeine Java-Themen 4
S Action mehrmals klicken Allgemeine Java-Themen 3
S Verhindern das Programm mehrmals geöffnet wird Allgemeine Java-Themen 26
P String.replace() funktioniert nicht? Allgemeine Java-Themen 3
Xge Replace x Zeichen aus String Allgemeine Java-Themen 2
Thallius Warum läst mein replace die Klammern drin? Allgemeine Java-Themen 10
B Input/Output BufferedWriter/Reader replace line Allgemeine Java-Themen 6
KaffeeFan Methoden replace alle Buchstaben Allgemeine Java-Themen 3
K String.replace funktioniert nicht Allgemeine Java-Themen 3
nrg Find and replace Text docx Allgemeine Java-Themen 6
P ganze Zeilen in einem File mit .replace() ändern. Allgemeine Java-Themen 10
F Replace von Leerzeichen Allgemeine Java-Themen 8
M Replace Problem Allgemeine Java-Themen 10
L String Replace mit Regulärem Ausdruck Allgemeine Java-Themen 2
kodela replace und die Umlaute Allgemeine Java-Themen 10
E String replace java 1.4 Allgemeine Java-Themen 4
D Abstruse Probleme mit eigenem replace Algorithmus Allgemeine Java-Themen 11
E String.replace für (sehr) großen Text Allgemeine Java-Themen 9
S ￾ Zeichen umwandeln in ü per .replace(); Allgemeine Java-Themen 6
G spezielles replace Allgemeine Java-Themen 3
C Problem mit String.replace(CharSequence, CharSequence) Allgemeine Java-Themen 3
B Ersatz für "replace" Allgemeine Java-Themen 4
C String replace Allgemeine Java-Themen 8

Ähnliche Java Themen


Oben