Java OCAJP7: differenze tra i metodi replace, replaceAll e replaceFirst della classe String

Una cosa che potrebbe trarre in inganno in fase di esame OCAJP, nel caso in cui non si abbia assoluta confidenza con le signature dei metodi della classe String, è la differenza tra i metodi replace e replaceAll. I nomi sono un po’ fuorvianti e ci si può confondere, anche solo per fretta o disattenzione, e pensare che replaceAll sostituisca tutte le occorrenze di una stringa all’interno di un’altra mentre replace ne sostituisca solo una. Questo non è vero ed entrambi i metodi sostituiscono tutte le occorrenze che trovano.
Nell’esempio seguente definiamo una stringa di test e poi invochiamo su di essa prima il metodo replaceAll e poi il metodo replace.

public class StringReplacingTest {

    public static void main(String[] args) {
        String s = "ABC AA D F AA BA AB CC FA AA A D C BA AA";
        System.out.println(s);

        s = s.replaceAll("AA", "ZZ");
        System.out.println(s);

        s = s.replace("ZZ", "XX");
        System.out.println(s);
    }
}

Il risultato che otteniamo è il seguente, in cui vediamo che il metodo replaceAll sostituisce tutte le occorrenze di “AA” con “ZZ” e poi il metodo replace sostituisce a sua volta tutte le occorrenze di “ZZ” con “XX”.

ABC AA D F AA BA AB CC FA AA A D C BA AA
ABC ZZ D F ZZ BA AB CC FA ZZ A D C BA ZZ
ABC XX D F XX BA AB CC FA XX A D C BA XX

Ma allora, se entrambi i metodi sostituiscono tutte le occorrenze di un pattern in una stringa, quali sono le differenze tra i due metodi?
Per trovarle dobbiamo partire dalla signature dei due metodi:

  • String replace(CharSequence target, CharSequence replacement)
  • String replaceAll(String regex, String replacement)

La prima differenza che possiamo notare è che replaceAll prende come parametri due oggetti String, mentre replace accetta due implementazioni dell’interfaccia CharSequence (quindi, oltre a String, anche StringBuilder, StringBuffer, ecc..)
Quindi possiamo ad esempio invocare replace passando come sequenza da cercare una StringBuilder:

public class StringReplacingTest {

    public static void main(String[] args) {
        String s = "ABC AA D F AA BA AB CC FA AA A D C BA AA";
        System.out.println(s);

        StringBuilder sb = new StringBuilder("AA");
        s = s.replace(sb, "XX");
        System.out.println(s);
    }
}

Ottenendo come risultato:

ABC AA D F AA BA AB CC FA AA A D C BA AA
ABC XX D F XX BA AB CC FA XX A D C BA XX

Ma se proviamo ad effettuare la stessa operazione invocando replaceAll, otteniamo un errore di compilazione:

public class StringReplacingTest {

    public static void main(String[] args) {
        String s = "ABC AA D F AA BA AB CC FA AA A D C BA AA";
        System.out.println(s);

        StringBuilder sb = new StringBuilder("AA");
        s = s.replaceAll(sb, "ZZ");
        System.out.println(s);
    }
}
The method replaceAll(String, String) in the type String is not applicable for the arguments (StringBuilder, String)

Inoltre, replaceAll prende come primo parametro una stringa che rappresenta un’espressione regolare, mentre replace accetta solo delle normali stringhe e non interpreta le regex.
Per cui, se eseguiamo il codice seguente in cui indichiamo una regex come sequenza da sostituire

public class StringReplacingTest {

    public static void main(String[] args) {
        String s = "ABC AA D F AA BA AB CC FA AA A D C BA AA";
        System.out.println(s);

        s = s.replaceAll("\\WAA\\W", "ZZ");
        System.out.println(s);
    }
}

otteniamo il seguente risultato.

ABC AA D F AA BA AB CC FA AA A D C BA AA
ABCZZD FZZBA AB CC FAZZA D C BA AA

Se invece eseguiamo la stessa operazione con le stesse sequenze, ma utilizzando il metodo replace

public class StringReplacingTest {

    public static void main(String[] args) {
        String s = "ABC AA D F AA BA AB CC FA AA A D C BA AA";
        System.out.println(s);

        s = s.replace("\\WAA\\W", "XX");
        System.out.println(s);
    }
}

otteniamo il risultato seguente, in cui possiamo notare come non sia stata sostituita nessuna sequenza, in quanto l’espressione regolare non viene valutata ed il metodo cerca la stringa “\WAA\W” che ovviamente non è presente.

Infine, avendo constatato che entrambi i metodi replace e replaceAll sostituiscono tutte le occorrenze di una stringa dentro ad un’altra, vediamo come fare nel caso in cui si voglia sostituire solo la prima occorrenza. Per fare questo dobbiamo utilizzare il metodo replaceFirst che si comporta come il metodo replace, accettando quindi due String in input, ma sostituisce solo la prima occorrenza della sequenza all’interno della stringa.
L’esempio seguente mostra un riepilogo dell’utilizzo dei metodi visti nell’articolo.

public class StringReplacingTest {

    public static void main(String[] args) {
        String s = "ABC AA D F AA BA AB CC FA AA A D C BA AA";
        System.out.println(s);

        s = s.replaceAll("\\WAA\\W", "-ZZ-");
        System.out.println(s);

        StringBuilder sb = new StringBuilder("-ZZ-");

        s = s.replace(sb, "_XX_");
        System.out.println(s);

        s = s.replaceFirst("_XX_", "YY");
        System.out.println(s);
    }
}
ABC AA D F AA BA AB CC FA AA A D C BA AA
ABC-ZZ-D F-ZZ-BA AB CC FA-ZZ-A D C BA AA
ABC_XX_D F_XX_BA AB CC FA_XX_A D C BA AA
ABCYYD F_XX_BA AB CC FA_XX_A D C BA AA

Come possiamo vedere, l’ultima sostituizione di “_XX_” con “YY” viene effettuata solo sulla prima occorrenza.

Leave a Reply

Your email address will not be published. Required fields are marked *