2017-04-24 3 views
7

Ich habe den folgenden Code innerhalb einer Schleife Anweisung.
In der Schleife werden Strings an sb (StringBuilder) angehängt und geprüft, ob die Größe von sb 5MB erreicht hat.Java - Schnellste Möglichkeit, um die Größe von String zu überprüfen

if (sb.toString().getBytes("UTF-8").length >= 5242880) { 
    // Do something 
} 

Dies funktioniert gut, aber es ist sehr langsam (in Bezug auf die Größe Prüfung)
Was ist der schnellste Weg, dies zu tun wäre?

Antwort

8

Sie berechnen die UTF-8 Länge schnell

public static int utf8Length(CharSequence cs) { 
    return cs.codePoints() 
     .map(cp -> cp<=0x7ff? cp<=0x7f? 1: 2: cp<=0xffff? 3: 4) 
     .sum(); 
} 

mit der Wenn ASCII-Zeichen die Inhalte dominieren, könnte es etwas schneller zu bedienen

public static int utf8Length(CharSequence cs) { 
    return cs.length() 
     + cs.codePoints().filter(cp -> cp>0x7f).map(cp -> cp<=0x7ff? 1: 2).sum(); 
} 

statt.

Sie können aber auch das Optimierungspotenzial der nicht die gesamte Größe neu zu berechnen, sondern nur um die Größe des neuen Fragments betrachten Sie die StringBuilder sind Anfügen etwas gleich

StringBuilder sb = new StringBuilder(); 
    int length = 0; 
    for(…; …; …) { 
     String s = … //calculateNextString(); 
     sb.append(s); 
     length += utf8Length(s); 
     if(length >= 5242880) { 
      // Do something 

      // in case you're flushing the data: 
      sb.setLength(0); 
      length = 0; 
     } 
    } 

Dies setzt voraus, dass, wenn Sie‘ wenn Fragmente mit Ersatzpaaren angehängt werden, sind sie immer vollständig und nicht in ihre Hälften geteilt. Für normale Anwendungen sollte dies immer der Fall sein.

Eine weitere Möglichkeit, durch Didier-L vorgeschlagen, ist die Berechnung zu verschieben, bis Ihre StringBuilder eine Länge der durch drei geteilt Schwelle erreicht, nach wie vor, dass es unmöglich ist, eine UTF-8 Länge größer als die Schwelle zu haben. Dies wird jedoch nur dann von Vorteil sein, wenn Sie in einigen Ausführungen threshold/3 nicht erreichen.

+4

Als weitere Optimierung, da ein Zeichen höchstens 3 Bytes benötigt, könnten Sie auch vermeiden, die Länge zu berechnen, bis die 'StringBuilder'-Länge 5MB/3 erreicht. –

+0

@Holger In jdk-9 gibt es 'String :: codePoints', die den Unterschied zwischen ASCII- und Nicht-ASCII-Strings ausmachen ... Auch diese Technik funktioniert nur für UTF-8, es ist immer noch nett. – Eugene

+1

@Eugene: Die Berechnung der 'UTF-8'-Länge ist der einzige Zweck dieser Übung. Abgesehen davon wird die Implementierung von 'codePoints()' in Java 9 für diese Antwort keinen Unterschied machen. Der Unterschied zwischen den beiden Lösungen dieser Antwort besteht darin, dass die zweite nur eine Bedingung für ASCII-Zeichen ausführt und die Additionsoperation überspringt. Nach der Behebung eines Fehlers unterscheiden sich die beiden Varianten im Worst-Case nicht mehr, so gewinnt immer der 2.. Eine billige "isAllASCII" -Methode wäre hilfreich, aber soweit ich weiß, wird Java 9 intern nur zwischen iso-latin-1 und anderen Strings unterscheiden. – Holger

8

Wenn Sie 1000 Mal eine Schleife ausführen, werden Sie 1000String generieren und dann in "UTF-8 Byte" -Array konvertieren, um die Länge zu erhalten.

Ich würde die Konvertierung durch Speichern der ersten Länge reduzieren. Erhalten Sie dann in jeder Schleife nur die Länge des hinzugefügten Werts, dann ist dies nur eine Addition.

int length = sb.toString().getBytes("UTF-8").length; 
for(String s : list){ 
    sb.append(s); 
    length += s.getBytes("UTF-8").length; 
    if(...){ 
    ... 
    } 
} 

Dies würde verwendet, um den Speicher reduzieren und die Herstellungskosten

2

Verwenden Sie einen ByteArrayOutputStream und einen OutputStreamWriter anstelle von StringBuilder. Verwenden Sie ByteArrayOutputStream.size(), um die Größe zu testen.

Verwandte Themen