compareTo vs equals

Publicado en Java el 20 de March, 2009 por Lek.

Al actualizar el código de un proyecto antiguo uno se puede encontrar cualquier cosa. En una de esas paranoias mías (o eso creía yo) me dio por dedicarme a cambiar todos los compareTo por equals, que me parecía como una instrucción más directa, óptima y mejor. No iba yo muy desencaminado.

La primera nos devuelve un entero que indica si el objeto es mayor que el parámetro de entrada, menor, o igual. La segunda nos devuelve directamente un booleano que nos dice si son iguales o no. Pero yo personalmente no recuerdo haber utilizado nunca el compareTo para algo que no fuera comprobar si un objeto (y más en concreto un String) es igual a otro. Así pues, me he creado un código chorras para saber cuánta diferencia hay (de paso metí el equalsIgnoreCase):

public static void main (String [] args) {
  String s = "necesito una prueba a ver si cuela ésta misma";
  long ini = System.nanoTime ();
  for (int i = 0 ; i < 10000000; i ++ ) {
    boolean b = s.compareTo ("necesito una prueba") == 0;
  }
  System.out.println (
      "CompareTo: " + (System.nanoTime () - ini)/1000000.0);

  ini = System.nanoTime ();
  for (int i = 0 ; i < 10000000; i ++ ) {
    boolean b = s.equals ("necesito una prueba");
  }
  System.out.println (
      "equals: " + (System.nanoTime () - ini)/1000000.0);

  ini = System.nanoTime ();
  for (int i = 0 ; i < 10000000; i ++ ) {
    boolean b = s.equalsIgnoreCase ("necesito una prueba");
  }
  System.out.println (
      "equalsIgnoreCase: " + (System.nanoTime () - ini) /1000000.0);
}

Los resultados son demoledores. compareTo es unas 150 veces más lento que equals:

(el tiempo va en milisegundos)
CompareTo: 1886.853143
equals: 71.728111
equalsIgnoreCase: 60.81111

Sorprende que equalsIgnoreCase sea más rápido que equals, sobre todo porque lo primero que hace es ejecutar el equals. Puede ser cosa del orden, pero probando a cambiarlas los datos no variaban demasiado. Sí puedo decir que de tanto en tanto equals es más rápido. En cualquier caso, puede parecer una chorrada perder 1'8 segundos en 10 millones de consultas que no se van a dar así en un programa real, pero al final todo suma y si los Strings ya son poco óptimos de por sí...

14 comentarios

  1. ¿Dónde probaste esto? Porque lo he probado ahora mismo en el trabajo, y me salen tiempos totalmente diferentes:

    CompareTo: 21.168117
    equals: 52.222325
    equalsIgnoreCase: 92.20248

    Mi máquina es un AMD Athlon(tm) 64 X2 Dual Core Processor 3800+ con 4 GB de RAM, utilizo Debian y la versión 1.6.0.12 de la JVM de Sun.

    #  Fran 30 de March, 2009

  2. CompareTo: 1094.54239
    equals: 92.673307
    equalsIgnoreCase: 71.966938

    Ahora mismo. En un WinXP de un Dell a 3.19GHz con 2 gigas de RAM. Desde Eclipse, con el JDK 1.6.0_06…

    #  Lek 2 de April, 2009

  3. Pues tío, es una diferencia muy grande, intenta probarlo sin eclipse y con el JDK que te dicho que he usado, sólo por curiosidad, que ya me tiene intrigado.

    #  Fran 2 de April, 2009

  4. Compilado desde línea de comandos, ejecutado desde línea de comandos… he cambiado el orden y metido en un bucle de 10 iteraciones:

    equalsIgnoreCase: 61.969535
    equals: 54.436252
    CompareTo: 1778.058944
    
    equalsIgnoreCase: 58.933227
    equals: 54.510268
    CompareTo: 1781.598479
    
    equalsIgnoreCase: 53.646282
    equals: 55.438165
    CompareTo: 2106.608551
    
    equalsIgnoreCase: 51.59063
    equals: 49.069956
    CompareTo: 1796.075206
    
    equalsIgnoreCase: 52.061743
    equals: 48.619233
    CompareTo: 1779.318443
    
    equalsIgnoreCase: 54.289203
    equals: 50.518945
    CompareTo: 1822.693875
    
    equalsIgnoreCase: 52.598742
    equals: 47.859914
    CompareTo: 1785.852798
    
    equalsIgnoreCase: 51.543059
    equals: 51.088611
    CompareTo: 1835.57281
    
    equalsIgnoreCase: 51.870409
    equals: 49.680866
    CompareTo: 1765.428026
    
    equalsIgnoreCase: 53.133372
    equals: 49.521157
    CompareTo: 1776.850374

    Eso sí, el mismo JDK 6…06. Curioso, curioso…

    #  Lek 2 de April, 2009

  5. Pues sí, es muy raro, yo no sé si es Linux, los 64 bits o el JDK. En principio daría igual la arquitectura y el sistema operativo, ya que para eso está la JVM, por eso me resulta curioso.

    #  Fran 2 de April, 2009

  6. Con Java 5 me salen mejores resultados para compareTo… pero con el último JDK me salen prácticamente iguales. Será el Windows… es la única alternativa…

    #  Lek 2 de April, 2009

  7. ¡Maldito Windows! xD

    #  Fran 2 de April, 2009

  8. lek creo que te pelaste en el calculo, segun tu prueba compareTo no es 150 veces mas lento sino 27 aprox.

    la clase String sobreescribe el metodo equals y realiza la comparacion caracter por caracter, como tambien lo hace compareTo, lo que hace aun mas extraño el resultado!

    String.equals(String) entonces deberia hacer una comparacion mas que compareTo para retornar el booleano

    #  samuel 5 de May, 2009

  9. bueno tambien probé el codigo pero calculando la demora en segundos, acá los resultados:

    compareTo 1.914529639
    equals 0.157562256
    ini-actual: 0.134674135

    #  samuel 5 de May, 2009

  10. sorry por el post anterior, lo publiqué incompleto sin querer, los resultados fueron en segundos:

    compareTo
    1.914529639
    equals
    0.157562256
    equalsIgnore
    0.134674135

    en java 1.6, en ubuntu 9.04.
    tambien resulto mas rapido equals, como que no era windows(no esta vez jaja)

    #  samuel 5 de May, 2009

  11. Samuel, gracias por la aclaración sobre las diferencias… (¿nadie más lo vio?). En cualquier caso, veo que también te da más rápido el equals (incluso el equalsIgnoreCase), lo cual hace las cosas aún más extrañas :-/

    #  Lek 5 de May, 2009

  12. Ps a mi me salió esto con el código que tienes al inicio:

    CompareTo: 472.068861
    equals: 69.260289
    equalsIgnoreCase: 69.272754

    Utilicé la línea de comandos y con JDK 1.6 en un Pentium Dual Core a 2.5 GHz con 2 GB de RAM y Windows XP SP3

    #  Luis 23 de September, 2009

  13. Haber , yo creo que es mejor usar equals por motivos de performance , yo siempre lo he usado en cualquier sistema windows , linux , solaris , bueno solo es mi opinion

    String cadena=”hola mundo”;
    if(cadena.compareTo(“hola”) == 0){
    //yo creo que aca se hacen 2 comprobaciones
    }
    if(cadena.equals(“hola”)){
    //yo creo que aca se hace 1 comprobacion
    }

    #  vegeta 10 de February, 2010

  14. Creo que hoy voy a dar el premio al comentario tonto del blog, vegeta en el código que has puesto en las dos condiciones se hacen dos comprobaciones:

    1. La del compareTo y la del igual a cero, esta está clara.
    2. La del equals y la que tiene que hacer el if para saber si el resultado del equals es verdadero o falso, sino ¿cómo sabe si tiene que entrar?

    PD: Es a ver, no haber.

    #  Fran 15 de February, 2010

Escribe un comentario