En esta sección trabajaremos, sobre todo, con matrices (su contenido se pasa por referencia), tanto
de números como de String o boolean. Veremos que una matriz (unidimensional o
multidimensional) requiere que se indique su tamaño cuando es declarada, lo que supone un impedimento
si se necesita añadir más elementos. Este problema puede solucionarse fácilmente mediante el uso del tipo ArrayList
que nos proporciona una de las librerias de Java.
También trabajaremos con archivos de texto (leer y escribir), lo que puede generar errores en el programa (por ejemplo, cuando
no se encuentra un archivo). Estos errores deben ser tratados. Para ello usaremos los bloques
try, catch y finally).
En el bloque try encerramos las instrucciones que pueden producir errores (abrir un documento, escribir en él, etc.).
-
En los bloques catch (puede haber varios), se indican las posibles excepciones (errores) que
pueden ocurrir en try y las instrucciones a seguir en cada caso.
Si se produce un error en try, el programa buscará entre los bloques catch para poder tratar el error.
El bloque finally se ejecuta siempre: si se produce un error, se sale del bloque try, se pasa
al catch correspondiente y finalmente, al finally. Si no hay errores, se saltan todos los
bloques catch al finalizar try y se pasa al bloque finally.
PROBLEMA 1
Escribir un programa que utilice un método llamado sufijo que devuelve true si el objeto String str1 es un sufijo del objeto
String str2. No utilizar las rutinas generales de búsqueda en cadenas de caracteres, salvo charAt.
El programa pide al usuario las dos cadenas, utiliza el método y muestra su resultado.
Ver código
1 import java.util.Scanner;
2 public class sufijoX
3 {
4 public static void main (String [] args)
5 {
6 System.out.println("Primera cadena?");
7 Scanner in= new Scanner (System.in);
8 String str1 = in.nextLine();
9
10 System.out.println("Segunda cadena?");
11 Scanner in2= new Scanner (System.in);
12 String str2 = in2.nextLine();
13
14
15 boolean su=sufijo(str1,str2);
16 System.out.println(su);
17 }
18
19 public static boolean sufijo(String str1, String str2)
20 {
21 boolean test=false;
22 int len1=str1.length();
23 int len2=str2.length();
24
25 if (len1 >= len2)
26 {
27 String t="";
28 for (int i=len1-len2; i<len1; i++)
29 t=t+str1.charAt(i);
30 test = t.equals(str2);
31 }
32 return test;
33 }
34 }
Observaciones:
Entradas: dos cadenas de caracteres: str1 y str2
-
Salida: se imprime un booleano true si str2 es un sufijo (o la misma
cadena) de str1.
Las funciones de la clase main son obvias: entrada/salida y llamada al método
sufijo.
El método sufijo retorna un valor booleano ( linea 32 ).
-
La comparación se realiza si la longitud de la segunda cadena es menor (o igual) que
la de la primera ( linea 25 ).
Para realizar la comprobación se define un String llamado t que está
formado por los últimos caracteres de la primera cadena (de longitud igual a la segunda cadena).
-
La comparación de la subcadena t y la cadena str2 se realiza mediante la rutina
.equals() en la línea 30, que devuelve un booleano y es la que se debe utilizar para
comparar cadenas de caracteres (la operación a == b devuelve true si a y b
referencian al mismo objeto, no si tienen el mismo contenido). También se puede usar .compareTo.
-
Si se quiere usar charAt() en vez de .equals(), se pueden comparar ambas cadenas posición
a posición mediante un buble for.
PROBLEMA 2
La suma de comprobación es el entero de 32 bits resultante al sumar todos los
caracteres Unicode de un archivo. Cuando dos documentos son idénticos, su suma es la misma.
Escribir un programa que calcule la suma de una línea escrita por el usuario.
En el PROBLEMA 4 volveremos a usar este concepto.
Ver código
1 import java.util.Scanner;
2 public class comp1
3 {
4 public static void main (String [] args)
5 {
6 System.out.println("Texto: ");
7 Scanner in = new Scanner (System.in);
8
9 String line=in.nextLine();
10 int longi=line.length();
11 int sum=0;
12
13 for (int i=0; i<longi; i++)
14 sum+=(int) line.charAt(i);
15
16 System.out.println(sum);
17 }
18 }
Observaciones:
-
Entradas: una cadena de caracteres
-
Salida: un entero (suma de comprobación de la línea/cadena).
-
En el bucle for de la línea 13 se suma el número entero de cada
caracter en la variable sum. El número de cada caracter se obtiene en la línea 14 haciendo
una conversión de char a int.
PROBLEMA 3
Lectura de un archio: Escribir un programa al que se le proporciona el nombre (o la ubicación) de un archivo de texto .txt como
string. El programa muestra el contenido (texto) del documento.
Usar las directivas import java.io.FileReader e import java.io.IOException.
Ver código
1 import java.util.Scanner;
2 import java.io.FileReader;
3 import java.io.IOException;
4 public class leer
5 {
6 public static void main (String [] args)
7 {
8 System.out.println("Nombre archivo: ");
9 Scanner in=new Scanner (System.in);
10 String nombre=in.nextLine();
11 System.out.println("Documento: "+nombre);
12 System.out.println("Texto: \n");
13 texto(nombre);
14
15 }
16 public static void texto (String nombre)
17 {
18 Scanner fileIn=null;
19 try
20 {
21 fileIn=new Scanner( new FileReader( nombre ));
22 while (fileIn.hasNextLine())
23 {
24 String linea=fileIn.nextLine();
25 System.out.println(linea);
26 }
27 }
28 catch(IOException error)
29 {System.out.println(error);}
30 finally
31 {
32 if(fileIn != null)
33 fileIn.close();
34 }
35 }
36 }
Observaciones:
-
Entradas: nombre del archivo (string).
Salida: texto del archivo (líneas del documento, que son string).
-
La parte del main es sencilla: se pide el nombre del archivo y se guarda en
el string nombre. Después se llama al método texto que tiene por finalidad
mostrar el texto del documento.
En la línea 8 se declara la variable fileIn. Debe ser este el lugar donde se haga
para que pueda ser accesible en el bloque finally.
-
En el bloque try de la línea 19 tenemos las instrucciones que pueden generar una excepción (como no encontrar
el archivo especificado). Si se genera una excepción, el bloque try se interrumpe y se busca
el bloque catch correspondiente (en este caso sólo hay uno) que muestra un mensaje de error.
El bloque finally de la línea 30 siempre se ejecuta (se interrumpa o no el bloque
try) y lo único que hace es cerrar el documento (si no es null).
En el bloque try (en la línea 19) se abre el documento y se escanea (en la
línea 21). El bucle while de la línea 22 imprime cada línea del documento.
PROBLEMA 4
Modificar el programa del problema anterior para que calcule la suma de comprobación del archivo de
texto .txt cuyo nombre es proporcionado por el usuario.
Ver código
1 import java.util.Scanner;
2 import java.io.FileReader;
3 import java.io.IOException;
4 public class sumCom
5 {
6 public static void main (String [] args)
7 {
8 System.out.println("Nombre archivo: ");
9 Scanner in=new Scanner (System.in);
10 String nombre=in.nextLine();
11 int suma=sumCom(nombre);
12 System.out.println("Suma de comprobación: "+ suma);
13 }
14 public static int sumCom (String nombre)
15 {
16 Scanner fileIn=null;
17 int sumaTotal=0;
18 try
19 {
20 fileIn=new Scanner( new FileReader( nombre ));
21 while (fileIn.hasNextLine())
22 {
23 String linea=fileIn.nextLine();
24 int longi=linea.length();
25 int suma=0;
26 for (int i=0; i<longi; i++)
27 suma+=(int) linea.charAt(i);
28 sumaTotal+=suma;
29 }
30 }
31 catch(IOException error)
32 {System.out.println(error);}
33 finally
34 {
35 if(fileIn != null)
36 fileIn.close();
37 }
38 return sumaTotal;
39 }
40 }
Observaciones:
-
Entradas: una cadena de caracteres
-
Salida: un entero (suma de comprobación del texto del documento).
-
Hemos utilizado fragmentos del código de los ejercicios 2 y 3. Pero hemos añadido una
vaiable sumaTotal en la que guardamos la suma de comprobación de cada línea del documento.
Notemos que la variable sumaTotal, que es el retorno del método, tiene que definirse
fuera del bloque try.
PROBLEMA 5
Matrices: implementar un programa que tenga dos métodos: uno para sumar dos matrices de
la misma dimensión y otro para restarlas. El funcionamiento del segundo método tiene que ser el siguiente:
se cambia el signo de los elementos de la segunda matriz y se le suma a la primera matriz haciendo uso del
primer método.
Para simplificar el programa, cada matriz es de dimensión 3x3 y de números enteros y se proporciona
al programa introduciendo sus elementos (utilizar .nextInt() ).
Utilizar dos constantes para la dimensión de las matrices, de modo que pueda ser modificado (sobre el código)
en caso de necesitar otras dimensiones.
Tanto las entradas (números enteros de las matrices) como las salidas (las matrices suma y resta) pueden
ser guardadas/impresas mediante otros métodos. Nosotros lo haremos directamente en el main, por lo que
el código de éste será un poco largo.
Ver código
1 import java.util.Scanner;
2 public class matrices
3 {
4 static final int I=2;
5 static final int J=2;
6
7 public static void main (String [] args)
8 {
9 int [][] A = new int [I][J];
10 int [][] B = new int [I][J];
11 for (int k=1; k<3; k++)
12 {
13 System.out.println("Matriz "+k+" "+I+"x"+J+" : ");
14 Scanner in=new Scanner (System.in);
15 for (int i=0; i<I; i++)
16 {
17 for (int j=0; j<J; j++)
18 if (k==1)
19 A[i][j]=in.nextInt();
20 else
21 B[i][j]=in.nextInt();
22 }
23 }
24
25 int [][] S = new int [I][J];
26 int [][] R = new int [I][J];
27
28 S = suma(A,B);
29 R = resta(A,B);
30
31 for (int k=1; k<3; k++)
32 {
33 if (k==1)
34 System.out.println("Suma: \n");
35 else
36 System.out.println("\nResta: \n");
37 for (int i=0; i<I; i++)
38 {
39 for (int j=0; j<J; j++)
40 if(k==1)
41 System.out.printf(S[i][j]+" ");
42 else
43 System.out.printf(R[i][j]+" ");
44 System.out.printf("\n");
45 }
46 }
47 }
48 public static int [][] suma (int [][] A, int [][] B)
49 {
50 int [][] suma= new int [I][J];
51 for (int i=0; i<I; i++)
52 for (int j=0; j<J; j++)
53 suma[i][j]=A[i][j]+B[i][j];
54 return suma;
55 }
56 public static int [][] resta (int [][] A, int [][] B)
57 {
58 int [][] resta= new int [I][J];
59 int [][] MB= new int [I][J];
60 for (int i=0; i<I; i++)
61 for (int j=0; j<J; j++)
62 MB[i][j]= -1*B[i][j];
63
64 resta = suma(A,MB);
65 return resta;
66 }
67 }
Observaciones:
-
Entradas: números enteros de las matrices.
-
Salida: las matrices suma (A, B) y resta (A, B).
-
Los bucles for de las líneas 11 y 31 son para guardar/imprimir las matrices.
Como habría que escribir dos bucles for para cada matriz para guardar las matrices (y también para
escribir las matrices suma y resta), usamos un bucle for (int i=1; i<3; i++) y cuando
i=1 trabajamos con una matriz y cuando es i=2 con la otra.
-
En el método resta (en la línea 56) creamos una neuva referencia para
la matriz MB (en la línea 59) y luego copiamos los elementos de B
multiplicados por -1.
Las constantes I y J se definen dentro de la clase matrices y se utilizan
en todos los métodos (main, suma y resta) sin poder ser alteradas en ninguno de ellos.
-
Finalmente, comentamos que usamos la rutina .nextInt() en las líneas 19 y 21, lo que
podría generar una excepción NoSuchElementException si el valor int no se encuentra disponible
(por ejemplo si se introduce por error una letra o un signo de puntuación). Esta excepción podríamos tratarla
con un bloque try y un bloque catch.
PROBLEMA 6
ArrayList es un tipo que proporciona la libreria java.util.ArrayList de Java para expandir matrices, esto es,
para poder aumentar su tamaño. Para ello tenemos el método .add. Para obtener el
sus elementos usamos .get y para modificarlos .set.
Escribir un programa que pide números enteros que se guardan en un array hasta que se introduce
un elemento no entero (como un signo de puntuación). Después, muestra el contenido del array.
Debe usarse el tipo ArrayList definido para enteros.
Ver código
1 import java.util.Scanner;
2 import java.util.ArrayList;
3 public class array
4 {
5 public static void main (String [] args)
6 {
7 boolean b=true;
8 Scanner in = new Scanner (System.in);
9 ArrayList<Integer> array = new ArrayList<Integer>();
10
11 do
12 {
13 System.out.println("Entero: ");
14 if (in.hasNextInt())
15 array.add(in.nextInt());
16 else
17 b=false;
18 }
19 while(b);
20
21 System.out.println("Array es: ");
22 for (int i=0; i<array.size(); i++)
23 System.out.println(array.get(i));
24 }
25 }
Observaciones:
-
Entradas: número indefinido de enteros más un no entero (para terminar el bucle).
-
Salida: se imprimen los elementos del array.
-
En el vector array lo hemos definido en la línea 9. Notemos que como contiene
enteros se indica que su tipo es Integer.
-
En el bucle do / while de la línea 11 se añaden los elementos del array hasta
que se introduce un no entero (entonces b = false y se termina el bucle). En cada iteración,
el array se expande mediante el método .add.
Para acceder a un elemento de un ArrayList<.> utilizamos .get
(como en la línea 23) y para modificarlo, .set().
- Notemos que el tamaño del array se obtiene mediante .size() y no mediante
.length().
PROBLEMA 7
Implementar los diversos métodos hasDuplicates, todos los cuales devuelven true si
hay entradas duplicadas en el grupo de elementos especificados:
public static boolean hasDuplicates ( int [] arr )
public static boolean hasDuplicates ( int [][] arr )
public static boolean hasDuplicates ( String [] arr )
-
public static boolean hasDuplicates ( ArrayList<String> arr )
Los métodos pueden llamarse entre ellos si así se desea.
Para simplificar los métodos, se considera que ninguno de los parámetros
está vacío y que ninguno de sus elementos es null.
Ver código
Escribimos por separado cada uno de los métodos y explicamos su
funcionamiento.
1 public static boolean hasDuplicates ( int [] arr )
2 {
3 boolean has = false;
4 int n = arr.length;
5 if ( arr.length>1 )
6 for (int i=0; i<n-1; i++)
7 {
8 for (int j=i+1; j<n; j++)
9 {
10 has = arr[i]== arr[j] ? true:false;
11 if (has)
12 break;
13 }
14 if (has)
15 break;
16 }
17 return has;
Observaciones:
-
Entradas: vector arr de números enteros
-
Salida: booleano has, siendo true si hay algún
elemento que se repite
-
En la línea 5 nos aseguramos que array tiene más de un elemento ya que
si no es así, no puede haber ningúna repetición y, por tanto, se retorna false que
es el valor en el que se ha inicializado la variable has.
Si hay más de un elemento, entramos en los bucles for.
El funcionamiento de los bucles for que empiezan en la línea 6 es el
siguiente: se toma un elemento del vector (empezando por el primero) y se compara uno a uno
con los elementos siguientes. Si no hay ninguna coincidencia, se toma el siguiente entero
y se compara con los elementos de posición superior (con los anteriores ya ha sido comparado).
Notemos que en la línea 10 se asigna true a la variable has en caso de haber una
coincidencia. Si no, se le asigna false.
En las líneas 12 y 15 tenemos una instrucción break que hace que el
programa salga del bucle for en el que se encuentra. Esto ocurre cuando se
encuentra una coincidencia.
Hemos utilizado dos break para finalizar el algoritmo de comparación, pero podríamos
haber escrito un break etiquetado para realizar la misma función de una
forma más limpia (como haremos en el siguiente método).
1 public static boolean hasDuplicates ( int [][] arr )
2 {
3 boolean has=false;
4 int fil = arr.length;
5 int col = arr[0].length;
6 externo:
7 for (int i=0; i<fil; i++)
8 for (int j=0; j<col; j++)
9 for (int a=0; a<fil; a++)
10 for (int b=0; b<col; b++)
11 if( arr[i][j] == arr[a][b] )
12 if ( i!=a || j!=b )
13 {
14 has=true;
15 break externo;
16 }
17 return has;
18 }
Observaciones:
Entradas: una matriz arr de enteros
Salidas: un booleano has que indica si hay
elementos repetidos en la matriz
El algoritmo es el siguiente: se selecciona un elemento de la matriz y se compara con todos los demás.
Este proceso se realiza hasta que se comparan todos los elementos o hasta que haya una coincidencia.
Notemos que al comparar un elemento con sí mismo tendremos una coincidencia. Evitamos esto
en la línea 12.
En el caso de haber alguna coincidencia, el break etiquetado finaliza el algoritmo de
comparación. Como el bucle etiquetado es el primero, el de la línea 7, se sale de todos
los bucles.
El número de filas de la matriz arr es arr.length. Como el número de columnas
es el número de elementos de cada fila, podemos obtenerlo con arr[0].length. También podemos usar
cualquier otra fila, pero no olvidando que no sabemos cuántas filas tiene arr.
1 public static boolean hasDuplicates ( String [] arr )
2 {
3 boolean has=false;
4 int n = arr.length;
5
6 if ( n > 1 )
7 for (int i=0; i<n-1; i++)
8 {
9 for (int j=i+1; j<n; j++)
10 {
11 has = arr[i].equals(arr[j]) ? true:false;
12 if (has)
13 break;
14 }
15 if (has)
16 break;
17 }
18 return has;
19 }
Observaciones:
-
Entradas: un vector arr de String
Salidas: un booleano has que es true cuando hay alguna
cadena (elemento) del vector repetido.
-
El algoritmo es el mismo que el del método para el vector de enteros. La diferencia es que
para poder comparar cadenas tenemos que usar .equals (en la línea 11 ).
1 public static boolean hasDuplicates ( ArrayList arr )
2 {
3 boolean has = false;
4 int n = arr.size();
5 if ( n>1 )
6 for (int i=0; i<n-1; i++)
7 {
8 for (int j=i+1; j<n; j++)
9 {
10 has = arr.get(i).equals(arr.get(j)) ? true:false;
11 if (has)
12 break;
13 }
14 if (has)
15 break;
16 }
17 return has;
18 }
Observaciones:
-
Entradas: un ArrayList<String>.
Salidas: un booleano has que es true cuando hay alguna
cadena (elemento) del ArrayList<String> repetido.
-
El algoritmo es el mismo que el del método para el vector de enteros o de String. La
diferencia es que tenemos que usar las rutinas adecuadas: .get() para acceder al elemento
del ArrayList<String> y .equals para poder comparar las cadenas.
PROBLEMA 8
Escritura de archivos: escribir un programa que copia el
contenido de un documento de texto .txt. Para ello necesitamos las directivas
import java.io.PrintWriter;
import java.io.FileWriter;
El programa pregunta el nombre del archivo a copiar (también podemos escribir la ubicación del archivo; en ambos casos, tenemos que indicar el tipo .txt) y el nombre del
nuevo archivo (sin .txt ; que también puede ser una ubicación más \nombre).
Realizar una comprobación para garantizar que los archivos de destino y origen no sean el mismo.
No olvidar utilizar la directiva
import java.io.IOException;
para tratar las posibles excepciones al trabajar con archivos (como no encontrar el archivo especificado).
Ver código
1 import java.util.Scanner;
2 import java.io.FileReader;
3 import java.io.FileWriter;
4 import java.io.PrintWriter;
5 import java.io.IOException;
6 public class copiar
7 {
8 public static void main (String [] args)
9 {
10 System.out.println("Nombre (con .txt): ");
11 Scanner in = new Scanner (System.in);
12 String nombre = in.nextLine();
13 System.out.println("Guardar (sin .txt): ");
14 String guardar = in.nextLine();
15
16 if (nombre.equals(guardar))
17 System.out.println("Error: mismo archivo");
18 else
19 {
20 Scanner fileIn = null;
21 PrintWriter fileOut = null;
22
23 try
24 {
25 fileIn = new Scanner ( new FileReader( nombre ));
26 fileOut = new PrintWriter ( new FileWriter ( guardar+".txt"));
27
28 while ( fileIn.hasNextLine() )
29 {
30 fileOut.println( fileIn.nextLine()+"\n");
31 }
32 }
33 catch ( IOException error )
34 {
35 System.out.println(error);
36 }
37 finally
38 {
39 if( fileIn != null)
40 fileIn.close();
41 if ( fileOut != null )
42 fileOut.close();
43 }
44 }
45 }
46 }
Observaciones:
-
Entradas: un string (nombre del archivo a copiar).
-
Salida: un archivo de texto (PrintWriter).
-
En la línea 16 comprobamos que los archivos de entrada y salida no son el mismo.
-
El conjunto try - catch - finally es necesario cuando trabajamos con archivos para
tratar los posibles errores de IO (como archivo no encontrado o fin de archivo antes de completar la
escritura). Para ello usamos la directiva de línea 5.
En el bloque try encerramos las instrucciones que pueden generar errores. Pero notemos
que fileIn y fileOut deben declarse fuera de este bloque (línas 20 y 21)
ya que luego se utilizan en finally.
En el bloque catch indicamos la instrucción a realizar (escribir en pantalla el error)
en caso de darse el error especificado.
El bloque finally siempre se ejecuta (haya o no una excepción IO) y en él cerramos los
archivos de entrada y salida.