2010-09-27 10 views
25

¿Cuál es la comparación más rápida de dos cadenas en Java?¿Cuál es la forma más rápida de comparar cadenas en Java?

¿Hay algo más rápido que los iguales?

EDITAR: No puedo ayudar mucho para aclarar el problema.

Tengo dos cuerdas que están ordenadas alfabéticamente y exactamente el mismo tamaño

Ejemplo: abbcee y abcdee

Las cadenas pueden ser de largo hasta 30 caracteres

+12

¿Por qué 'igual()' sería lento para usted? – BoltClock

+6

¿Ha perfilado su aplicación, y fue la conclusión de que el punto de acceso en su código fue causado por 'String.equals (...)'? Si no ha perfilado su aplicación, ¿por qué cree que 'String.equals (...)' es (o podría ser) un problema? –

+3

Su pregunta no indica que igual es lento. Me pregunto si hay algo más rápido que equals(). – Sagar

Respuesta

28

No espero que Sun Oracle aún no ha optimizado el estándar String#equals() al máximo. Por lo tanto, espero que ya sea la forma más rápida. Eche un vistazo en su fuente si quiere saber cómo lo implementaron. Aquí hay un extracto:

public boolean equals(Object anObject) { 
    if (this == anObject) { 
     return true; 
    } 
    if (anObject instanceof String) { 
     String anotherString = (String)anObject; 
     int n = count; 
     if (n == anotherString.count) { 
      char v1[] = value; 
      char v2[] = anotherString.value; 
      int i = offset; 
      int j = anotherString.offset; 
      while (n-- != 0) { 
       if (v1[i++] != v2[j++]) 
        return false; 
      } 
      return true; 
     } 
    } 
    return false; 
} 
+0

Esto se ve bastante optimizado para mí ... sería teóricamente posible optimizarlo aún más para las limitaciones específicas del OP (por ejemplo, utilizando el conocimiento de que las cadenas ya son de igual longitud y mayor probabilidad de tener diferentes personajes en el medio de la cadena), pero obviamente no se puede hacer eso en la práctica porque la clase es final y los campos son privados .... +1 para extraer la fuente! – mikera

+0

No entiendo por qué no compararon el código hash antes de hacer una comparación de cadena completa. Eso sería más rápido. – Stephan

+9

@Stephan: eso hubiera sido más ineficiente. El 'hashCode()' recorre todos los caracteres de la cadena para realizar el cálculo. Si el 'hashCode()' después de todo no era el mismo, entonces 'equals()' básicamente necesitaría repetir todos los caracteres * por segunda vez *. – BalusC

3

Depende de lo que necesite. Creo que equals() está realmente optimizado, pero quizás necesites algo más rápido que equals(). Eche un vistazo al this post.

0

Como siempre, deberá comparar su aplicación/entorno. Y a menos que ya hayas perfilado e idetificado esto como un cuello de botella de rendimiento, probablemente no importará ("la optimización prematura es la raíz de todo mal").

Una vez dicho esto:

a.equals (B) es muy rápido para cuerdas. Probablemente sea uno de los códigos más estrechamente optimizados en la plataforma Java. Me sorprendería mucho si puede encontrar una forma más rápida de comparar dos cadenas arbitrarias.

Hay casos especiales donde se puede trucos y usar (a == b) con seguridad, por ejemplo, si sabe que both Strings are interned (y, por lo tanto, la identidad del valor implica la identidad del objeto). En ese caso, puede ser un poco más rápido que a.equals (b), pero nuevamente esto depende de la implementación del compilador/JVM. Y es muy fácil de disparar en el pie si usted no sabe lo que está haciendo .....

+0

p.s. Acabo de micro-benchmarking esto, y (a == b) vence a a.equals (b) por un factor de aproximadamente 2-4 veces (30ns vs. 70-110ns) en mi entorno (Eclipse en Sun Java 1.6) . YMMV, y las advertencias habituales sobre micro-benchmarking, por supuesto, se aplican :-) – mikera

+0

Mirando el código de implementación publicado por @BalusC, no puedo ver optimizaciones pesadas, nada en absoluto para justificar su declaración. Por supuesto, la optimización de este código ya trivial no es fácil.Pero de bajo nivel, lo que * podría * haberse hecho para optimizar es pasar de una comparación de tipo inteligente a una interacción interna (obviamente, esto requiere trucos de bajo nivel que no están disponibles en Java, y puede que no sean más rápidos después de todo). –

+0

Hmmm Se ve muy estrechamente optimizado para mí, en la medida en que, por ejemplo, están reutilizando la longitud de la cadena como un contador de bucle negativo (que es una clásica optimización de bajo nivel). No puedo ver personalmente ninguna optimización adicional que se pueda realizar, salvo abandonar Java puro y pasar a una implementación nativa especializada (y es posible que el JIT lo haga de todos modos ...) – mikera

3

Si usted puede demostrar que es un importante cuello de botella, lo que me sorprende, se podría tratar

s1.hashCode() == s2.hashCode() && s1.equals(s2) 

podría ser un poco más rápido. Puede que no.

+0

Esta fue mi primera idea, también. Como las cuerdas son inmutables (¿está realmente deletreado, verdad?) Básicamente estás comparando las constantes constantes aquí, lo cual debería ser rápido. Podría ser un problema solo si los objetos son iguales la mayor parte del tiempo, entonces podría intercambiar la implementación dinámicamente. Demasiado triste, no tengo el jdk en esta máquina, me encantaría hacer un perfil de eso ahora. – atamanroman

+2

es "inmutable" :) – KevinDTimm

+1

Sí, es más rápido. Pero debe hacer una comprobación 'nula' antes. – Stephan

24

comparar cadenas de igual longitud más rápido utilizando el código hash:

public static boolean equals(final String s1, final String s2) { 
return s1 != null && s2 != null && s1.hashCode() == s2.hashCode() 
    && s1.equals(s2); 
} 

puede probarlo, mis resultados son de 4.000.000 comparar las operaciones incluidas cadenas idénticas, iguales y diferentes:

String.equals(String): 177081939 
equals(String, String): 44153608 

Nota : El cálculo del hashCode de un nuevo objeto de cadena requiere cierto tiempo de cálculo y luego el hashCode se almacena en el objeto. Por lo tanto, mi mejora sugerida solo será más rápida que la comparación predeterminada si los objetos de cadena se reutilizan.En mi aplicación, estoy usando constantes de cadenas y cadenas de tiendas en colecciones. Las comparaciones múltiples de cadenas usando mi método son más rápidas para mí, pero puede que no sean en general.

Si el método se utiliza con nuevas cadenas todo el tiempo como compare("a", "b"), no será una mejora.

Así que la manera más rápida de comparar cadenas depende de:

  • Ya sea que sus objetos de cadena se vuelven a utilizar (como de una colección) o son siempre nuevas (como el de un flujo de entrada)
  • Ya sea que su cadenas tienen diferentes longitudes
  • Ya sea que sus cadenas difieren al principio o al final de la cadena
  • Su estilo de programación, la cantidad de constantes se utilizan
  • La utilización del String.intern()

Haciendo caso omiso de estos hechos, la mayoría de los programas va a estar bien con String.equals().

+0

+1 He estado usando esto para una gran cantidad de "palabras crujientes" y el rendimiento es fantástico – xchiltonx

+4

Creo que vale la pena mencionar que podría haber algunas colisiones de código hash, por lo que hay una probabilidad muy, muy pequeña de que la comparación de hashes vuelva falsos positivos. Esto explica el hecho de que todavía tienes que usar iguales. Por esta razón, creo que esto será más lento si la mayoría de tus cadenas son iguales. – Nepoxx

+1

¿Por qué [agrega "algo de longitud"] (http://stackoverflow.com/revisions/9850634/2)? – Flow

4

I tenían tries diferentes combinaciones para la comparación de cadenas (code here):

1. s1.equals(s2) 
2. s1.length() == s2.length() && s1.hashCode() == s2.hashCode() && s1.equals(s2) 
3. s1.hashCode() == s2.hashCode() && s1.equals(s2); 
4. s1.length() == s2.length() && s1.equals(s2); 

que solía cadenas de longitud 40 caracteres, en 10000000000L iteraciones y antes de cualquier iteración I reinicializa las cuerdas.

por la igualdad de las picaduras que tengo:

equal: 2873 milis ??? 
equal: 21386 milis 
equal: 7181 milis 
equal: 2710 milis ??? 

para mismas cadenas de tamaño, pero la última con caracteres diferentes que tengo:

different: 3011 milis 
different: 23415 milis 
different: 6924 milis 
different: 2791 milis 

de diferentes tamaños, casi mismas cadenas, pero con un carácter añadido al final de S2:

different size: 3167 milis 
different size: 5188 milis 
different size: 6902 milis 
different size: 2951 milis 

me parece que lo mejor es utilizar una primera STRI ng.length() comparación antes de igual().

Pero esto no importará casi en absoluto porque este es el caso donde tengo 10^10 comparaciones de cuerdas con 40 caracteres de longitud y lo que es extraño para mí es el caso donde para cadenas iguales tengo una mejor velocidad cuando compare la longitud de la cuerda primero.

+5

Creo hay algo mal con tus datos Cuando comparas cadenas de la misma longitud, ¿cómo podría el algoritmo 4 (comparar longitud y luego usar .equals()) ser más rápido que el algoritmo 1 (comparando solo con .equals()). Para estos casos, el algoritmo 4 está haciendo una comparación de longitud de cadena innecesaria que siempre devolverá verdadero. – Tyler

0

Respuesta simple

String.equals(Object)

Estoy bastante seguro (this answer has some references) y es muy probable que el JIT tendrá un intrínseco para String#equals, lo que significa que sería capaz de reemplazar la llamada con especialmente diseñado código de máquina para la arquitectura en la que se está ejecutando su JVM.

Cuestiones relacionadas