Método substring en Java con Ejemplos

Métodos

En esta lección te voy a hablar de dos cosas. En concreto, sobre las subcadenas (substring) y sobre la estructura de los objetos de la clase String. Y en esta pregunta tendremos que bucear un poco en la historia de Java… Pero será breve, interesante y útil, lo prometo.

¿Qué es substring en Java?

La subcadena o substring en general es una secuencia contigua de caracteres dentro de la cadena. Puede ser una parte de la cadena o toda la cadena también.

Concepto de substring con Java
Concepto de substring con Java

Entonces, este lenguaje tiene un método substring(), o más bien dos métodos, gracias a la sobrecarga de métodos de Java. Lo explicaré a continuación.

¿Cómo puedo extraer una subcadena (o parte de una cadena)?

A partir de tu pregunta, probablemente ya te habrás dado cuenta de que una subcadena es sólo una parte de una cadena. ¿Sabes cuáles son las acciones más populares en las cadenas? En primer lugar, la concatenación de varias cadenas, que ya la has visto en varias ocasiones. Y la segunda es tomar una subcadena de una cadena (extraer un string de un substring).

Java tiene un método substring para esto. Devuelve parte de una cadena. Hay dos variantes de este método.

  1. El primero devuelve la subcadena dada por los números de caracteres iniciales y finales. Observa que el primer carácter se incluye en la subcadena, pero el último no. Es decir, si pasamos los números (1,3) – del primero al tercero, sólo el primer y segundo carácter estarán en la subcadena.
  2. En la segunda variante de métodos, especificamos sólo el índice inicial de la subcadena, y devuelve la subcadena desde este índice hasta el final de la cadena.

Método 1:

String substring(int beginIndex, int endIndex)

Ejemplo 1:

String s = "¡Buenas noticias para todos!";
s = s.substring(1,6);

El resultado: (nótese que falta “¡” al comienzo, porque es el lugar “0”)

s == "Buena";

Método 2:

String substring(int beginIndex)

Ejemplo 2:

String s = "¡Buenas noticias para todos!";
s = s.substring(1);

El resultado: (nótese que falta “¡” al comienzo, porque es el lugar “0”)

s == "Buenas noticias para todos!";

¿Es muy sencillo, verdad? veamos más ejemplos.

Ejemplo: Cómo obtener un substring en Java (específico)

Este primer ejemplo de substring en Java es bastante fácil. Tienes un String y necesitas encontrar una substring “javadesdecero.es” en él. Ya sabes cómo obtener una substring en Java. Así que aquí está la solución de este problema en particular:

import java.io.IOException;
public class Main {
   public static void main(String[] args) throws IOException {
       String s1 = "El mejor curso de Java gratis es javadesdecero.es.  Fin de la historia";
       String myTarget = "javadesdecero.es";
       int index1 = s1.indexOf(myTarget);
       int index2 = index1 + myTarget.length();
       System.out.println(s1.substring(index1, index2));
   }
}

La salida es:

javadesdecero.es
Process finished with exit code 0
Ejemplo de substring en Java
Ejemplo de substring en Java

Ejemplo: Cómo encontrar todos los substrings de una cadena dada

Aquí tenemos la forma más sencilla de encontrar todos los substrings de una cadena dada.

import java.io.IOException;
public class Main {
   public static void main(String[] args) throws IOException {
       String myTarget = "Java";
       for (int i = 0; i < myTarget.length(); i++) {
           for (int j = i + 1; j <= myTarget.length(); j++) {
               System.out.println(myTarget.substring(i, j));
           }
       }
   }
}

La salida es:

J
Ja
Jav
Java
a
av
ava
v
va
a

Ejemplo 3: Cómo encontrar la substring común más larga (comparación)

El problema de la substring común más larga es una de las tareas más populares en programación. Puedes encontrártelo en tu entrevista de desarrollador junior con una probabilidad bastante alta. De todos modos, intenta resolverlo, es un ejercicio muy útil para un programador principiante.

El problema de la subcadena común más larga significa encontrar la cadena más larga (o algunas de ellas) que es una subsecuencia (o son subcadenas) de dos o más cadenas. Por ejemplo, tienes dos cadenas:

String primero = "ProgramacionJava"
String segundo = "Javadesdecero"

La salida debe ser: Java

Por lo tanto, tienes las strings “primero” y “segundo“. Imprime la substring común más larga. Si dos o más subcadenas tienen el mismo valor para la substring común más larga, imprime cualquiera de ellas.

Te recomiendo encarecidamente que intentes resolver este problema por ti mismo y sólo después de eso mira el código de abajo.

public class SubStringE3 {
   //  en este método buscamos la substring común más larga de la 
   // primera String con longitud = m y la segunda String con longitud = n
   public static String longestCS(String primero, String segundo, int m, int n) {
       // la longitud máxima
       int maxLength = 0;
       // el último índice de la substring común más larga
       int endIndex = m;
       // array almacena la longitud de la subcadena
       int[][] keeper = new int[m + 1][n + 1];
       for (int i = 1; i <= m; i++) {
           for (int j = 1; j <= n; j++) {
               // comprueba si los caracteres actuales de la primera y segunda strings coinciden
               if (primero.charAt(i - 1) == segundo.charAt(j - 1)) {
                   keeper[i][j] = keeper[i - 1][j - 1] + 1;
                   if (keeper[i][j] > maxLength) {
                       maxLength = keeper[i][j];
                       endIndex = i;
                   }
               }
           }
       }
       return primero.substring(endIndex - maxLength, endIndex);
   }
   public static void main(String[] args) {
       String primero = "ProgramacionJava";
       String segundo = "Javadesdecero";
       int m = primero.length(), n = segundo.length();
       System.out.println("La substring común más larga = " + longestCS(primero, segundo, m, n));
   }
}
Ejemplo para encontrar substring en comparación
Ejemplo para encontrar substring en comparación

El funcionamiento interno de los objetos String: una excursión al JDK 6

¿Recuerdas que te prometí una pequeña excursión por la historia de Java? En el contexto de nuestro tema, por supuesto, a saber, las peculiaridades de la clase String.

Érase una vez, en aquellos días, es posible que aún no hayas empezado a programar, la versión más actual del lenguaje fue JDK 6. Desde entonces, una gran cantidad de agua se ha filtrado y el número que denota el número de Java más reciente ha sido durante mucho tiempo de dos dígitos.

Bien, debes saber que String es una clase inmutable. Y esta inmutabilidad dio una forma interesante de obtener la subcadena en JDK 6.

Internamente, un objeto de tipo String es una matriz de caracteres, o más exactamente, contiene una matriz de caracteres. Esto es intuitivamente obvio. Y en la época del JDK 6 se almacenaban allí dos variables más: el número del primer carácter de la matriz y su número. Así, en JDK 6 String tenía tres campos: char value[] (matriz de caracteres), int offset (índice del primer carácter de la matriz) e int count (número de caracteres de la matriz).

Este es el mecanismo que se utilizó para crear la subcadena utilizando el método substring(). Cuando se llama, crea una nueva cadena, es decir, un nuevo objeto String.

Excepto que en lugar de almacenar una referencia al array con el nuevo conjunto de caracteres, en JDK 6 este objeto almacenaba una referencia al antiguo array de caracteres y dos variables más offset y count. Con ellos, determina qué parte de la matriz de caracteres original pertenece a la nueva submatriz (subarray).

-No entiendo nada.

Cuando se crea una subcadena en JDK 6, la matriz de caracteres no se copia en el nuevo objeto String. En cambio, ambos objetos almacenan una referencia a la misma matriz de caracteres. El segundo objeto almacena dos variables más, la primera de las cuales almacena el índice del comienzo de la submatriz y la segunda cuántos caracteres hay en la submatriz.

Esto es lo que parece:

Recuperación de una subcadena

String s = "mama";

Lo que se almacena dentro de la subcadena:

(Lo que se almacena en el s)

char[] value = {'m','a','m','a'};
offset = 0;
count = 4;

Ejemplo 1:

String s2 = s.substring(1);

Lo que se almacena en s2:

char[] value = {'m','a','m','a'};
offset = 1;
count = 3;

Ejemplo 2:

String s3 = s.substring(1, 3);

Lo que se almacena en s3:

char[] value = {'m','a','m','a'};
offset = 1;
count = 2;

Las tres cadenas almacenan una referencia a la misma matriz char, sólo que además almacenan el número del primer y último carácter de la matriz, que se refiere directamente a su objeto. Más concretamente, el número del primer carácter y su cantidad.

-Ahora lo entiendo.

Así, si tomas una cadena de 10.000 caracteres de longitud en JDK 6 y haces 10.000 subcadenas de cualquier longitud a partir de ella, estas “subcadenas” ocuparán muy poca memoria porque la matriz de caracteres no se duplica. Las cadenas, que deberían ocupar mucho espacio, sólo ocuparán un par de bytes.

-Y si se pudieran cambiar las cadenas, ¿funcionaría?

No, alguien podría haber cambiado esa primera cadena y entonces todas sus subcadenas habrían cambiado.

-Es una forma genial de hacerlo. Ahorra memoria, ¿verdad?

Sí, así es. Excepto que este ahorro de memoria en JDK 6 causó un problema. Intentaré contarlo. Supongamos que tenemos una cadena x, y que creamos una subcadena utilizando substring.

String x = "miCadenaLarga";
String y = x.substring(2,6);
String z = x.substring(0,3);

Ahora tenemos un objeto x (por cierto, se almacena en una zona de memoria especial llamada “montículo” o heap) y dos objetos y y z, referidos al mismo objeto x. Sólo y se refiere a los elementos dos a seis, mientras que z se refiere a los elementos cero a tres. Así, puede darse una situación en la que el objeto x original se olvide, ya nadie se refiera a él, y todo el mundo se ocupe sólo de los objetos y y z. ¿Sabes qué pasará en este caso?

-Recuerdo algo sobre la recolección de basura, pero no recuerdo mucho.

No recuerdas mucho, pero tu pensamiento es correcto. En tal situación, un recolector de basura podría llegar y destruir el objeto x, mientras que el array permanecerá en memoria y será utilizado por y y z.

-¿Y luego qué?

En este caso, puede producirse una situación desagradable llamada fuga de memoria.

-Entonces, ¿qué debe hacer al respecto?

Todo el mundo ya ha hecho todo. A partir de JDK 7, el método substring funciona de forma diferente. Y ahora te diré cómo. Así termina nuestra excursión por la historia…

substring() en JDK 7 y posteriores

En JDK 7, substring() ya no cuenta el número de caracteres en la matriz de caracteres que crea, sino que simplemente crea una nueva matriz en la memoria (heap) y hace referencia a ella. Tomemos el mismo ejemplo:

String x = “miCadenaLarga”;
String y = x.substring(2,6);
String z = x.substring(0,3);

Así, en JDK 7 y posteriores, los objetos y y z creados por el método substring() aplicado al objeto x se referirán a dos matrices recién creadas (en el heap) – {C,a,d,e} para y y {m,i,C} para z.

En la versión actualizada del método, estas dos nuevas cadenas (es decir, dos nuevas matrices de caracteres) se almacenarán en la memoria junto con la original miCadenaLarga ({m,i,C,a,d,e,n,a,L,a,r,g,a}, en forma de matriz). Esa es la diferencia.

-Parece que sólo ha empeorado…

Más bien, más costoso en términos de memoria. Este enfoque, sin embargo, evita las fugas de memoria. Además, el método en sí funciona más rápido, ya que no tiene que calcular el número de caracteres.

-Pero, ¿y si cambian algo en las próximas versiones? ¿Qué hacer?

Es totalmente posible. Por eso hice esta pequeña excursión a la historia. Con el tiempo te acostumbrarás a buscar en la documentación oficial de Oracle, allí siempre encontrarás información actualizada. Este ejemplo es muy ilustrativo porque muestra por qué no siempre es fácil cambiar de una versión del lenguaje a otra en proyectos reales. Es posible tropezar con estos escollos.

Sobre el Autor:

Hey hola! Yo soy Alex Walton y tengo el placer de compartir conocimientos hacía ti sobre el tema de Programación en Java, desde cero, Online y Gratis.

Deja una Respuesta

*

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.