¿Cuál es la forma más rápida de eliminar todos los caracteres no imprimibles de a String
en Java?
Hasta ahora lo he probado y medido en una cadena de 138 bytes y 131 caracteres:
- String's
replaceAll()
- método más lento- 517009 resultados / seg
- Precompile un patrón, luego use Matcher's
replaceAll()
- 637836 resultados / seg
- Use StringBuffer, obtenga puntos de código
codepointAt()
uno por uno y añádalos a StringBuffer- 711946 resultados / seg
- Use StringBuffer, obtenga caracteres
charAt()
uno por uno y agregue a StringBuffer- 1052964 resultados / seg
- Preasigne un
char[]
búfer, obtenga caracterescharAt()
uno por uno y llene este búfer, luego conviértalo de nuevo a String- 2022653 resultados / seg
- Preasigne 2
char[]
búferes: antiguo y nuevo, obtenga todos los caracteres para String existente a la vez usandogetChars()
, itere sobre el búfer antiguo uno por uno y llene el nuevo búfer, luego convierta el nuevo búfer en String: mi propia versión más rápida- 2502502 resultados / seg
- Misma materia con 2 buffers - solamente utilizando
byte[]
,getBytes()
y que especifican codificación como "UTF-8"- 857485 resultados / seg
- Lo mismo con 2
byte[]
búferes, pero especificando la codificación como una constanteCharset.forName("utf-8")
- 791076 resultados / seg
- Lo mismo con 2
byte[]
búferes, pero especificando la codificación como codificación local de 1 byte (apenas es una cosa sensata)- 370164 resultados / seg
Mi mejor intento fue el siguiente:
char[] oldChars = new char[s.length()];
s.getChars(0, s.length(), oldChars, 0);
char[] newChars = new char[s.length()];
int newLen = 0;
for (int j = 0; j < s.length(); j++) {
char ch = oldChars[j];
if (ch >= ' ') {
newChars[newLen] = ch;
newLen++;
}
}
s = new String(newChars, 0, newLen);
¿Alguna idea sobre cómo hacerlo aún más rápido?
Puntos de bonificación por responder una pregunta muy extraña: ¿por qué usar el nombre del juego de caracteres "utf-8" produce directamente un mejor rendimiento que usar la constante estática preasignada Charset.forName("utf-8")
?
Actualizar
- La sugerencia del fanático del trinquete produce un rendimiento impresionante de 3105590 resultados por segundo, ¡una mejora del + 24%!
- La sugerencia de Ed Staub arroja otra mejora: 3471017 resultados / segundo, un + 12% sobre el mejor anterior.
Actualización 2
Hice todo lo posible para recopilar todas las soluciones propuestas y sus mutaciones cruzadas y las publiqué como un pequeño marco de evaluación comparativa en github . Actualmente tiene 17 algoritmos. Uno de ellos es "especial": el algoritmo Voo1 ( proporcionado por el usuario de SO Voo ) emplea intrincados trucos de reflexión para lograr velocidades estelares, pero estropea el estado de las cadenas JVM, por lo que se compara por separado.
Le invitamos a comprobarlo y ejecutarlo para determinar los resultados en su caja. Aquí hay un resumen de los resultados que obtuve con el mío. Son especificaciones:
- Debian sid
- Linux 2.6.39-2-amd64 (x86_64)
- Java instalado desde un paquete
sun-java6-jdk-6.24-1
, JVM se identifica como- Entorno de ejecución Java (TM) SE (compilación 1.6.0_24-b07)
- Servidor VM Java HotSpot (TM) de 64 bits (compilación 19.1-b02, modo mixto)
Los diferentes algoritmos muestran en última instancia resultados diferentes dado un conjunto diferente de datos de entrada. Ejecuté un punto de referencia en 3 modos:
La misma cuerda única
Este modo funciona en una misma cadena única proporcionada por la StringSource
clase como constante. El enfrentamiento es:
Ops / s │ Algoritmo ──────────┼────────────────────────────── 6535 947 │ Voo1 ──────────┼────────────────────────────── 5350454 │ RatchetFreak2EdStaub1GreyCat1 5249343 │ EdStaub1 5002501 │ EdStaub1GreyCat1 4859086 │ ArrayOfCharFromStringCharAt 4295532 │ RatchetFreak1 4045307 │ ArrayOfCharFromArrayOfChar 2790178 │ RatchetFreak2EdStaub1GreyCat2 2583311 │ RatchetFreak2 1274 859 │ StringBuilderChar 1138174 │ StringBuilderCodePoint 994727 │ ArrayOfByteUTF8String 918611 │ ArrayOfByteUTF8Const 756086 │ MatcherReplace 598945 │ StringReplaceAll 460 045 │ ArrayOfByteWindows1251
En forma de gráficos:
(fuente: greycat.ru )
Varias cadenas, el 100% de las cadenas contienen caracteres de control
El proveedor de cadenas de origen generó muchas cadenas aleatorias utilizando el juego de caracteres (0..127), por lo que casi todas las cadenas contenían al menos un carácter de control. Los algoritmos recibieron cadenas de esta matriz pregenerada en forma de turnos.
Ops / s │ Algoritmo ──────────┼────────────────────────────── 2123142 │ Voo1 ──────────┼────────────────────────────── 1782 214 │ EdStaub1 1776199 │ EdStaub1GreyCat1 1694628 │ ArrayOfCharFromStringCharAt 1484481 │ ArrayOfCharFromArrayOfChar 1460 067 │ RatchetFreak2EdStaub1GreyCat1 1438435 │ RatchetFreak2EdStaub1GreyCat2 1366494 │ RatchetFreak2 1349710 │ RatchetFreak1 893176 │ ArrayOfByteUTF8String 817127 │ ArrayOfByteUTF8Const 778089 │ StringBuilderChar 734754 │ StringBuilderCodePoint 377829 │ ArrayOfByteWindows1251 224140 │ MatcherReplace 21114 │ StringReplaceAll
En forma de gráficos:
(fuente: greycat.ru )
Varias cadenas, el 1% de las cadenas contienen caracteres de control
Igual que antes, pero solo el 1% de las cadenas se generó con caracteres de control; el otro 99% se generó usando el conjunto de caracteres [32..127], por lo que no podían contener caracteres de control en absoluto. Esta carga sintética es la más cercana a la aplicación de este algoritmo en el mundo real en mi casa.
Ops / s │ Algoritmo ──────────┼────────────────────────────── 3711952 │ Voo1 ──────────┼────────────────────────────── 2851440 │ EdStaub1GreyCat1 2455 796 │ EdStaub1 2426007 │ ArrayOfCharFromStringCharAt 2347969 │ RatchetFreak2EdStaub1GreyCat2 2242152 │ RatchetFreak1 2 171 553 │ ArrayOfCharFromArrayOfChar 1922707 │ RatchetFreak2EdStaub1GreyCat1 1857 010 │ RatchetFreak2 1023751 │ ArrayOfByteUTF8String 939055 │ StringBuilderChar 907194 │ ArrayOfByteUTF8Const 841963 │ StringBuilderCodePoint 606465 │ MatcherReplace 501555 │ StringReplaceAll 381185 │ ArrayOfByteWindows1251
En forma de gráficos:
(fuente: greycat.ru )
Es muy difícil para mí decidir quién proporcionó la mejor respuesta, pero dada la aplicación del mundo real que mejor solución fue dada / inspirada por Ed Staub, creo que sería justo marcar su respuesta. Gracias a todos los que participaron en esto, su aporte fue muy útil e invaluable. Siéntase libre de ejecutar el conjunto de pruebas en su caja y proponer soluciones aún mejores (solución JNI funcional, ¿alguien?).
Referencias
- Repositorio de GitHub con una suite de evaluación comparativa