Una implementación más rápida: utilización String.regionMatches()
Usar regexp puede ser relativamente lento. (Ser lento) no importa si solo quieres verificar en un caso. Pero si tiene una matriz o una colección de miles o cientos de miles de cadenas, las cosas pueden volverse bastante lentas.
La solución presentada a continuación no usa expresiones regulares ni toLowerCase()
(que también es lenta porque crea otras cadenas y las tira después de la comprobación).
La solución se basa en el método String.regionMatches () que parece ser desconocido. Comprueba si 2 String
regiones coinciden, pero lo importante es que también tiene una sobrecarga con un ignoreCase
parámetro útil .
public static boolean containsIgnoreCase(String src, String what) {
final int length = what.length();
if (length == 0)
return true; // Empty string is contained
final char firstLo = Character.toLowerCase(what.charAt(0));
final char firstUp = Character.toUpperCase(what.charAt(0));
for (int i = src.length() - length; i >= 0; i--) {
// Quick check before calling the more expensive regionMatches() method:
final char ch = src.charAt(i);
if (ch != firstLo && ch != firstUp)
continue;
if (src.regionMatches(true, i, what, 0, length))
return true;
}
return false;
}
Análisis de velocidad
Este análisis de velocidad no significa ser ciencia de cohetes, solo una imagen aproximada de lo rápido que son los diferentes métodos.
Comparo 5 métodos.
- Nuestro contieneIgnoreCase () método .
- Al convertir ambas cadenas a minúsculas y llamar
String.contains()
.
- Al convertir la cadena de origen en minúsculas y llamar
String.contains()
con la subcadena pre-almacenada en caché y en minúsculas. Esta solución ya no es tan flexible porque prueba una subcadena predefinida.
- Usando expresión regular (la respuesta aceptada
Pattern.compile().matcher().find()
...)
- Usando expresiones regulares pero con pre-creado y almacenado en caché
Pattern
. Esta solución ya no es tan flexible porque prueba una subcadena predefinida.
Resultados (llamando al método 10 millones de veces):
- Nuestro método: 670 ms
- 2x toLowerCase () y contiene (): 2829 ms
- 1x toLowerCase () y contiene () con subcadena en caché: 2446 ms
- Regexp: 7180 ms
- Regexp con caché
Pattern
: 1845 ms
Resultados en una tabla:
RELATIVE SPEED 1/RELATIVE SPEED
METHOD EXEC TIME TO SLOWEST TO FASTEST (#1)
------------------------------------------------------------------------------
1. Using regionMatches() 670 ms 10.7x 1.0x
2. 2x lowercase+contains 2829 ms 2.5x 4.2x
3. 1x lowercase+contains cache 2446 ms 2.9x 3.7x
4. Regexp 7180 ms 1.0x 10.7x
5. Regexp+cached pattern 1845 ms 3.9x 2.8x
Nuestro método es 4 veces más rápido en comparación con las minúsculas y el uso contains()
, 10 veces más rápido en comparación con el uso de expresiones regulares y también 3 veces más rápido incluso si Pattern
está pre-almacenado en caché (y pierde la flexibilidad de verificar una subcadena arbitraria).
Código de prueba de análisis
Si está interesado en cómo se realizó el análisis, aquí está la aplicación completa ejecutable:
import java.util.regex.Pattern;
public class ContainsAnalysis {
// Case 1 utilizing String.regionMatches()
public static boolean containsIgnoreCase(String src, String what) {
final int length = what.length();
if (length == 0)
return true; // Empty string is contained
final char firstLo = Character.toLowerCase(what.charAt(0));
final char firstUp = Character.toUpperCase(what.charAt(0));
for (int i = src.length() - length; i >= 0; i--) {
// Quick check before calling the more expensive regionMatches()
// method:
final char ch = src.charAt(i);
if (ch != firstLo && ch != firstUp)
continue;
if (src.regionMatches(true, i, what, 0, length))
return true;
}
return false;
}
// Case 2 with 2x toLowerCase() and contains()
public static boolean containsConverting(String src, String what) {
return src.toLowerCase().contains(what.toLowerCase());
}
// The cached substring for case 3
private static final String S = "i am".toLowerCase();
// Case 3 with pre-cached substring and 1x toLowerCase() and contains()
public static boolean containsConverting(String src) {
return src.toLowerCase().contains(S);
}
// Case 4 with regexp
public static boolean containsIgnoreCaseRegexp(String src, String what) {
return Pattern.compile(Pattern.quote(what), Pattern.CASE_INSENSITIVE)
.matcher(src).find();
}
// The cached pattern for case 5
private static final Pattern P = Pattern.compile(
Pattern.quote("i am"), Pattern.CASE_INSENSITIVE);
// Case 5 with pre-cached Pattern
public static boolean containsIgnoreCaseRegexp(String src) {
return P.matcher(src).find();
}
// Main method: perfroms speed analysis on different contains methods
// (case ignored)
public static void main(String[] args) throws Exception {
final String src = "Hi, I am Adam";
final String what = "i am";
long start, end;
final int N = 10_000_000;
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsIgnoreCase(src, what);
end = System.nanoTime();
System.out.println("Case 1 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsConverting(src, what);
end = System.nanoTime();
System.out.println("Case 2 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsConverting(src);
end = System.nanoTime();
System.out.println("Case 3 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsIgnoreCaseRegexp(src, what);
end = System.nanoTime();
System.out.println("Case 4 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsIgnoreCaseRegexp(src);
end = System.nanoTime();
System.out.println("Case 5 took " + ((end - start) / 1000000) + "ms");
}
}