Cómo recortar un java stringbuilder?

Tengo un objeto de StringBuilder que necesita ser recortado (es decir, todos los caracteres de espacio en blanco / u0020 y abajo eliminado de cualquier extremo).

Parece que no puedo encontrar un método en el constructor de cadenas que haga esto.

Esto es lo que estoy haciendo ahora:

String trimmedStr = strBuilder.toString().trim(); 

Esto da exactamente la salida deseada, pero requiere dos cadenas para ser asignado en lugar de uno. ¿Hay un más eficiente para recortar la cadena mientras está todavía en el StringBuilder?

No debe utilizar el método deleteCharAt.

Como Boris señaló, el método deleteCharAt copia la matriz sobre cada vez. El código en el Java 5 que hace esto se parece a esto:

 public AbstractStringBuilder deleteCharAt(int index) { if ((index < 0) || (index >= count)) throw new StringIndexOutOfBoundsException(index); System.arraycopy(value, index+1, value, index, count-index-1); count--; return this; } 

Por supuesto, la especulación por sí sola no es suficiente para elegir un método de optimización sobre otro, así que decidí a tiempo los 3 enfoques en este hilo: el original, el enfoque de eliminación y el enfoque de subcadena.

Aquí está el código que he probado para el orignal:

 public static String trimOriginal(StringBuilder sb) { return sb.toString().trim(); } 

El enfoque de eliminación:

 public static String trimDelete(StringBuilder sb) { while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { sb.deleteCharAt(0); } while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } 

Y el enfoque de subcadena:

 public static String trimSubstring(StringBuilder sb) { int first, last; for (first=0; first<sb.length(); first++) if (!Character.isWhitespace(sb.charAt(first))) break; for (last=sb.length(); last>first; last--) if (!Character.isWhitespace(sb.charAt(last-1))) break; return sb.substring(first, last); } 

He realizado 100 pruebas, cada vez que genera un StringBuffer de un millón de caracteres con diez mil espacios de arrastre y líderes. La prueba en sí es muy básica, pero da una buena idea de cuánto tiempo toman los métodos.

Aquí está el código de tiempo de los 3 enfoques:

 public static void main(String[] args) { long originalTime = 0; long deleteTime = 0; long substringTime = 0; for (int i=0; i<100; i++) { StringBuilder sb1 = new StringBuilder(); StringBuilder sb2 = new StringBuilder(); StringBuilder sb3 = new StringBuilder(); for (int j=0; j<10000; j++) { sb1.append(" "); sb2.append(" "); sb3.append(" "); } for (int j=0; j<980000; j++) { sb1.append("a"); sb2.append("a"); sb3.append("a"); } for (int j=0; j<10000; j++) { sb1.append(" "); sb2.append(" "); sb3.append(" "); } long timer1 = System.currentTimeMillis(); trimOriginal(sb1); originalTime += System.currentTimeMillis() - timer1; long timer2 = System.currentTimeMillis(); trimDelete(sb2); deleteTime += System.currentTimeMillis() - timer2; long timer3 = System.currentTimeMillis(); trimSubstring(sb3); substringTime += System.currentTimeMillis() - timer3; } System.out.println("original: " + originalTime + " ms"); System.out.println("delete: " + deleteTime + " ms"); System.out.println("substring: " + substringTime + " ms"); } 

Conseguí la salida siguiente:

 original: 176 ms delete: 179242 ms substring: 154 ms 

Como vemos, el enfoque de subcadena proporciona una optimización muy ligera sobre el enfoque original de "dos cadenas". Sin embargo, el enfoque de eliminación es extremadamente lento y debe evitarse.

Así que para responder a su pregunta: usted está recortando bien su StringBuilder de la manera que sugirió en la pregunta. La muy ligera optimización que ofrece el método de subcadena probablemente no justifica el código en exceso.

No te preocupes por tener dos cuerdas. Es una microoptimización.

Si realmente ha detectado un cuello de botella, puede tener un recorte casi constante de tiempo: sólo iterar los primeros N caracteres, hasta que sean Character.isWhitespace(c)

He utilizado el método de análisis de Zaven y el método delete (start, end) de StringBuilder que funciona mucho mejor que el método deleteCharAt (index) , pero ligeramente peor que el método substring () . Este método también utiliza la copia de matriz, pero copia de matriz se llama mucho menos veces (sólo dos veces en el peor de los casos). Además, esto evita la creación de múltiples instancias de cadenas intermedias en caso de que trim () se llama repetidamente en el mismo objeto StringBuilder.

 public class Main { public static String trimOriginal(StringBuilder sb) { return sb.toString().trim(); } public static String trimDeleteRange(StringBuilder sb) { int first, last; for (first = 0; first < sb.length(); first++) if (!Character.isWhitespace(sb.charAt(first))) break; for (last = sb.length(); last > first; last--) if (!Character.isWhitespace(sb.charAt(last - 1))) break; if (first == last) { sb.delete(0, sb.length()); } else { if (last < sb.length()) { sb.delete(last, sb.length()); } if (first > 0) { sb.delete(0, first); } } return sb.toString(); } public static String trimSubstring(StringBuilder sb) { int first, last; for (first = 0; first < sb.length(); first++) if (!Character.isWhitespace(sb.charAt(first))) break; for (last = sb.length(); last > first; last--) if (!Character.isWhitespace(sb.charAt(last - 1))) break; return sb.substring(first, last); } public static void main(String[] args) { runAnalysis(1000); runAnalysis(10000); runAnalysis(100000); runAnalysis(200000); runAnalysis(500000); runAnalysis(1000000); } private static void runAnalysis(int stringLength) { System.out.println("Main:runAnalysis(string-length=" + stringLength + ")"); long originalTime = 0; long deleteTime = 0; long substringTime = 0; for (int i = 0; i < 200; i++) { StringBuilder temp = new StringBuilder(); char[] options = {' ', ' ', ' ', ' ', 'a', 'b', 'c', 'd'}; for (int j = 0; j < stringLength; j++) { temp.append(options[(int) ((Math.random() * 1000)) % options.length]); } String testStr = temp.toString(); StringBuilder sb1 = new StringBuilder(testStr); StringBuilder sb2 = new StringBuilder(testStr); StringBuilder sb3 = new StringBuilder(testStr); long timer1 = System.currentTimeMillis(); trimOriginal(sb1); originalTime += System.currentTimeMillis() - timer1; long timer2 = System.currentTimeMillis(); trimDeleteRange(sb2); deleteTime += System.currentTimeMillis() - timer2; long timer3 = System.currentTimeMillis(); trimSubstring(sb3); substringTime += System.currentTimeMillis() - timer3; } System.out.println(" original: " + originalTime + " ms"); System.out.println(" delete-range: " + deleteTime + " ms"); System.out.println(" substring: " + substringTime + " ms"); } } 

Salida:

 Main:runAnalysis(string-length=1000) original: 0 ms delete-range: 4 ms substring: 0 ms Main:runAnalysis(string-length=10000) original: 4 ms delete-range: 9 ms substring: 4 ms Main:runAnalysis(string-length=100000) original: 22 ms delete-range: 33 ms substring: 43 ms Main:runAnalysis(string-length=200000) original: 57 ms delete-range: 93 ms substring: 110 ms Main:runAnalysis(string-length=500000) original: 266 ms delete-range: 220 ms substring: 191 ms Main:runAnalysis(string-length=1000000) original: 479 ms delete-range: 467 ms substring: 426 ms 

Sólo uno de ustedes ha tenido en cuenta que al convertir el constructor String a una "cadena" y luego "recortar", se crea un objeto inmutable dos veces que tiene que ser recolectado de basura, por lo que la asignación total es:

  1. Objeto Stringbuilder
  2. Cadena inmutable del objeto SB 1 objeto inmutable de la cadena que se ha recortado.

Así, mientras que puede "aparecer" que el recorte es más rápido, en el mundo real y con un esquema de memoria cargado de hecho será peor.

Obtienes dos cadenas, pero espero que los datos sólo se asignen una vez. Como las cadenas en Java son inmutables, espero que la implementación de recorte le proporcione un objeto que comparta los mismos datos de caracteres, pero con diferentes índices de inicio y fin. Al menos eso es lo que hace el método substr. Por lo tanto, cualquier cosa que tratar de optimizar esto con toda seguridad tendrá el efecto contrario, ya que añadir la sobrecarga que no es necesario.

Sólo tienes que pasar por el método trim () con tu depurador.

Hice un poco de código. Funciona y los casos de prueba están ahí para que lo veas. Déjame saber si esto está bien.

Código principal –

 public static StringBuilder trimStringBuilderSpaces(StringBuilder sb) { int len = sb.length(); if (len > 0) { int start = 0; int end = 1; char space = ' '; int i = 0; // Remove spaces at start for (i = 0; i < len; i++) { if (sb.charAt(i) != space) { break; } } end = i; //System.out.println("s = " + start + ", e = " + end); sb.delete(start, end); // Remove the ending spaces len = sb.length(); if (len > 1) { for (i = len - 1; i > 0; i--) { if (sb.charAt(i) != space) { i = i + 1; break; } } start = i; end = len;// or len + any positive number ! //System.out.println("s = " + start + ", e = " + end); sb.delete(start, end); } } return sb; } 

El código completo con prueba –

 package source; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; public class StringBuilderTrim { public static void main(String[] args) { testCode(); } public static void testCode() { StringBuilder s1 = new StringBuilder(""); StringBuilder s2 = new StringBuilder(" "); StringBuilder s3 = new StringBuilder(" "); StringBuilder s4 = new StringBuilder(" 123"); StringBuilder s5 = new StringBuilder(" 123"); StringBuilder s6 = new StringBuilder("1"); StringBuilder s7 = new StringBuilder("123 "); StringBuilder s8 = new StringBuilder("123 "); StringBuilder s9 = new StringBuilder(" 123 "); StringBuilder s10 = new StringBuilder(" 123 "); /* * Using a rough form of TDD here. Initially, one one test input * "test case" was added and rest were commented. Write no code for the * method being tested. So, the test will fail. Write just enough code * to make it pass. Then, enable the next test. Repeat !!! */ ArrayList<StringBuilder> ins = new ArrayList<StringBuilder>(); ins.add(s1); ins.add(s2); ins.add(s3); ins.add(s4); ins.add(s5); ins.add(s6); ins.add(s7); ins.add(s8); ins.add(s9); ins.add(s10); // Run test for (StringBuilder sb : ins) { System.out .println("\n\n---------------------------------------------"); String expected = sb.toString().trim(); String result = trimStringBuilderSpaces(sb).toString(); System.out.println("In [" + sb + "]" + ", Expected [" + expected + "]" + ", Out [" + result + "]"); if (result.equals(expected)) { System.out.println("Success!"); } else { System.out.println("FAILED!"); } System.out.println("---------------------------------------------"); } } public static StringBuilder trimStringBuilderSpaces(StringBuilder inputSb) { StringBuilder sb = new StringBuilder(inputSb); int len = sb.length(); if (len > 0) { try { int start = 0; int end = 1; char space = ' '; int i = 0; // Remove spaces at start for (i = 0; i < len; i++) { if (sb.charAt(i) != space) { break; } } end = i; //System.out.println("s = " + start + ", e = " + end); sb.delete(start, end); // Remove the ending spaces len = sb.length(); if (len > 1) { for (i = len - 1; i > 0; i--) { if (sb.charAt(i) != space) { i = i + 1; break; } } start = i; end = len;// or len + any positive number ! //System.out.println("s = " + start + ", e = " + end); sb.delete(start, end); } } catch (Exception ex) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); ex.printStackTrace(pw); sw.toString(); // stack trace as a string sb = new StringBuilder("\nNo Out due to error:\n" + "\n" + sw); return sb; } } return sb; } } 
  • Genymotion no detendrá la optimización de la aplicación
  • Android: variable tiene un valor incorrecto en while loop
  • Las propuestas de optimización para ListView con datos de múltiples "tablas"
  • Optimización de un dibujo VBO enorme en dispositivos Android / iOS
  • Seguimiento del uso de energía en Android
  • Desempeño de Android: "Evita los internos y los setters"
  • Optimización png android
  • ¿Por qué esto no funciona a veces?
  • OpenGL en Android versus iOS: optimizaciones, y donde difieren
  • Android ScrollView Vs ListView
  • ¿Es posible minimizar todas las funciones de Android Studio a la vez?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.