0% encontró este documento útil (0 votos)
12 vistas156 páginas

Final Java-Olivia Gallego Toscano

Cargado por

bclaudia177
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
12 vistas156 páginas

Final Java-Olivia Gallego Toscano

Cargado por

bclaudia177
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 156

EJERCICIOS

FINAL JAVA
Olivia Gallego Toscano
Tipo Test: …p.1-3

Tema 5:
Ej1: función recursiva suma……………………………………….p.4
Ej2: función recursiva muestra números entre limites…………….p.5
Ej3: función recursiva nº dígitos…………………………………..p.6
Ej4: función recursiva potencias…………………………………..p.7
Ej5: función recursiva Max de una lista……………………………p.8
Ej6: función recursiva cadena dígitos hexadecimal ………………p.9
Ej7: Calcular C (n,k)………………………………………………p.10
Ej8: Crea cadenas ADN……………………………………………p.11-12
Ej9: Ordenar fichero texto alfabéticamente……………………….p.13-14
Ej10: Buscar palabra en fichero de texto …………………………p.15
Ej11: búsqueda binaria fichero de texto …………………………. p.16
Ej12: ordenar fechas….……………………………………………p.17

Tema 6:
Ej1: clase pareja de cualquier tipo………………………………….p.19
Ej2: lista de nº aleatorio……………………………………………..p.20
Ej3: lista de parejas de nº aleatorios ………………………………..p.21
Ej4: lista caracteres, eliminar o introducir caracteres………………..p.22-23
Ej5: lista caracteres, eliminar o añadir en cualquier posición ………p.24-25
Ej6: lista caracteres ordenados, eliminar o añadir caracteres ……….p.26
Ej7: mapa nº y palabra ………………………………………………p.27
Ej8: mapa nº y letra………………………………………………….p.28
Ej9: TreeSet nombres y apellidos …………………………………..p.29-30
Ej10: ventas de una tienda………………………………………… p.31-33
Ej11: leer y ordenar líneas de archivo de texto ………………….. p.34
Ej12: lista nombres todos los archivos ……………………………p.35-36
Ej13: TreeSet ordena 1 millón de enteros…………………………p.37
Preguntas Desarrollo :
Diferencia recursion lineal final y no lineal final ……………………..p.38
Explicación método búsqueda binaria …………………………………p.38
Diferencia pila y cola…………………………………………………..p.39
Diferencia tabla Hash y árbol………………………………………….p.40
Diferencia pila y cola, inserción, acceso aleatorio y búsqueda………..p.41
Diferencia tabla Hash y árbol, inserción, acceso aleatorio, búsqueda y
crecimiento……………………..…………………………………….p.42-43
Limitaciones análisis O………….. …………………………………..p.43

Ejercicios Prácticos:
Función recursiva lista inversa……….……………………………p.44
Función recursiva suma dígitos ……………………………………p.45
Función recursiva x^y………………………………………………p.46
Tipo de recursividad de C(n,k)……………………………………..p.47
Complejidad computacional de funciones………………………….p.47
Calcular tamaño A =200n B = 2n^2………………………………p.48
Calcular tamaño A = 1024n. B = 16n^3…………………………..p.48
Algoritmo Quicksort……………………………………………….p.48
Escribir código de obtenerFechas() con diferentes bucles …………p.49
Complementar clase GotIO………………………………………..p.50-51
Analizar código recursive………………………………………….p.51-52
Analizar UML Polígono, coche y ordenador………………………p.52
Analizar UML Persona, cita trabajador…………………………….p.53
Analizar código Dinosaurio………………………………………..p.54-55

Ejercicios Desarrollo :
Aplicación contactos móvil Android……………………………..p.56-62
Red social Twitter…………………………………………………p.63-67
Clase Utils cargar lista usuarios…………………………………..p.68-71
Juego de Tronos…………………………………………………..p.72-84
Explicaciones telemedicina cantidad memoria (4KB)……………p.85
Clase número complejo …………………………….………………p.86-87
FuncionInterface Newton-Raphson…………………………………p.88-90
Programa que lee otro programa ……………………………………p.91-92
Programa que lee otro programa Ejemplo2…………………………p.93-94
Aplicación contactos móvil Android………………………………..p.95-100

Ejercicios Extra :
Clase estudiantes y orden por edad……….………………………p.101-102
Ventas tienda orden por precio y códigos…………………………p.103-104
Figuras Geométricas………………………………………………p.105-108
Biblioteca y orden alfabéticamente……………………………….p.109-113
Cartas y barajarlas…….…………………………………………..p.114-115
Tienda Online…….……………………………………………….p.116-119
Electrodomésticos………….……………………………………..p.120-124
Serie , Videojuego y Entrega……………………………………..p.125-128
Clase de Alumnos y profesores (materias mates, filo y física)……p.129-133
Baraja cartas española……………………………………………p.134-137
Ruleta rusa………………………………………………………..p.138-140
Almacén de bebidas………………..………………………….…p.141-145
Tipo Test
1. Cuál de las siguientes afirmaciones es falsa:
(a) Un objeto es una instancia concreta de una clase abstracta
(b) Un objeto es una instancia concreta de una clase

2. Los elementos que definen a un objeto son:


(a) El tipo de visibilidad que tiene asignado: privado o publico
(b)Los atributos que representan su estado y los métodos que representan su comportamiento

3. En relación con el estado de un objeto, para preservar el principio de


encapsulamiento:
(a) Los atributos de nuestra clase deben permanecer públicos, para permitir un acceso total a
la información que almacenamos en los objetos. Sin embargo, aquellos métodos que realicen
operaciones internas (y no deban ser utilizados), deben establecerse como privados.
(b) Debemos establecer la visibilidad mas restrictiva (por ejemplo, privada) en los atributos de
una clase. Así, cualquier software que utilice nuestro objeto solo accederá al estado de los
objetos mediante los métodos que le hayamos permitido utilizar.

4. En el paradigma de la programación orientada a objetos:


(a) Para conseguir una buena eficiencia se recomienda que los programas estén lo mas
acoplados posibles al código.
(b) Se favorece la semántica y la reutilización de código en detrimento de rendimiento.

5. Si tenemos una variable local, declarada dentro del ámbito de un bloque try podemos
afirmar que:
(a) La variable no es visible dentro de los bloques catch y finally.
(b) La variable es visible dentro del bloque finally pero no dentro del bloque catch.

6. El hecho de que un objeto pueda utilizarse en cualquier método que reciba como
parámetro a cualquier instancia de sus clases padre, se denomina:
(a) Herencia
(b)Polimorfismo

7. Gracias al mecanismo de herencia, una clase hija tiene acceso a públicos, protegidos y
amistosos definidos en su clase padre:
(a) Verdadero.
(b) Falso

8. Al hecho de que, dentro de una misma clase existan dos métodos con el mismo
nombre pero diferente numero/tipo de argumentos, se le denomina:
(a) Sobre escritura de métodos
(b) Sobrecarga de métodos

9. Al utilizar un array en Java podemos acceder a la longitud del mismo gracias a que el
atributo que define dicha longitud tiene una visibilidad:
(a) public
(b) protected

10. Una ................. no proporciona ningún tipo de implementación de código, solo


definde signaturas de métodos.
(a) Clase abstracta
(b) Interfaz

1
11. Cuando una subclase especifica un método que tiene: el mismo nombre, el mismo
numero y el mismo tipo de argumentos que un método declarado en alguna de sus
superclases, esa clase está:
(a) Sobreescribiendo ese método
(b) Sobrecargando ese método

12. Los métodos de una clase pueden devolver objetos.


(a) Verdadero
(b) Falso

13. Los objetos que se declaran dentro de la función main():


(a) Se pueden utilizar dentro de la función main() y en cualquier otra parte del
programa.
(b) Solo pueden utilizarse dentro de función main().

14. ¿Cuál de las siguientes caracteristics de la programación orientada a objetos esta


relacionada con la reutilización de código?
(a) Polimorfismo
(b)Encapsulamiento.

15. Imagine que la clase C hereda de la clase B. Y la clase B hereda de la clase A. Cuando
se cree un objeto de la clase C, ¿Cuál será la secuencia en que se ejecutarán los
constructores?
(a) Primero el constructor C, luego el constructor B y, finalmente, el constructor A.
(b) Primero el constructor A, luego el constructor B y, finalmente, el constructor C.

16. En qué parte de la memoria de almacenamiento de un ordenador se reserva espacio


para un objeto a la hora de realizar la operación de construcción de dicha instancia
determinada de una clase?
(a) En la Pila o Stack
(b) En el Montón o Heap.

17. Una clase de Java puede estar formada por clases, métodos y funciones.
(a) Verdadero
(b) Falso.

18. El polimorfismo paramétrico permite que un objeto pueda utilizarse de forma


diferente en distintos contextos o situaciones.
(a) Verdadero
(b) Falso

19. Las variables y objetos en Java deben liberarse de forma explícita cuando no se vayan
a utilizar más:
(a) Verdadero
(b) Falso.

20. La diferencia entre la lectura de teclado y la lectura en modo texto de un fichero se


encuentra en el Stream al que se conecta el InputStreamReader.
(a) Verdadero
(b) Falso.

2
21. Los elementos que definen a un objeto son:
a) El tipo de visibilidad que tiene asignado: privado o público.
b) Los atributos que representan su estado y los métodos que representan su comportamiento.

22. El hecho de que un método pueda recibir un número variable de atributos se


denomina:
a) Herencia
b) Polimorfismo

23. Imagine que la clase C hereda de la clase B. Y la clase B hereda de la clase A. Cuando
se cree un objeto de la clase C, ¿cuál será la secuencia en que se ejecutarán los
constructores?
a) Primero el constructor de C, luego constructor de B y, finalmente, el constructor de A
b) Primero el constructor de A, luego constructor de B y, finalmente, el constructor de C

24. Una lista es:


a) Una estructura de datos estática muy utilizada en programación.
b) Una estructura de datos compuesta por nodos que contienen al elemento y uno o dos
punteros a otros nodos.

25. Un algoritmo de ordenación que implemente el método de la burbuja, tiene una


complejidad en el peor caso de:
a) O(n2)
b) O(n3)

26. En relación al estado de un objeto, para preservar el principio de encapsulación:


a)Los atributos de nuestra clase deben permanecer públicos, para permitir un acceso total a la
información que almacenamos en los objetos. Sin embargo, aquellos métodos que realicen
operaciones internas (y no deban ser utilizados). deben establecerse como privados.
b)Debemos establecer la visibilidad más restrictiva (por ejemplo, privada) en los atributos de
una clase. Así, cualquier software que utilice nuestro objeto, sólo accederá al estado de los
objetos mediante los métodos que le hayamos permitido utilizar.

27. Los métodos de una clase no pueden devolver obietos:


a)Verdadero
b)Falso

28. ¿Cuál de las siguientes características de la programación orientada a objetos está


relacionada con la reutilización de código?
a)Abstracción
b)Herencia

29. El tiempo de acceso a un dato en una lista es:


a)O(n)
b)O(1)

30. Un algoritmo de ordenación que implemente el método de inserción, tiene una


complejidad en el peor caso de:
a) O(n2)
b) O(n3)
31. Un algoritmo de ordenación que implemente el método de inserción se basa en la
idea de ir seleccionando el número correspondiente en la lista desordenada que se va
a insertar en la posición última de la lista ordenada:
a) Verdadero
b) Falso

3
EJERCICIOS
TEMA 5
1. Construir una función recursiva que calcule la suma de los n primeros números naturales.

package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Ejercicio1 {


public static void main(String[] args) throws IOException {
int numero=0;
try{
BufferedReader consola= new BufferedReader(new
InputStreamReader(System.in));
System.out.println("Dame un numero: ");
numero= Integer.parseInt(consola.readLine());
System.out.println("La suma de los n primeros numeros
naturales: "+suma(numero));
}catch (IOException e){
System.out.println(e.getMessage());
}
}

public static int suma(int num){


if(num==1){
return 1;
}else{
return num+suma(num-1);
}
}

4
2.Construir una función recursiva que imprima la lista de números naturales comprendidos entre dos
valores a y d dados por el usuario.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Ej2T5 {


public static List<Integer> num(int a, int b){
List<Integer> numeros= new ArrayList<>();
numeros(a, b, numeros);
return numeros;
}
public static void numeros(int a, int b, List<Integer>num){
if (a > b) {
return;
}
num.add(a);
numeros(a + 1, b, num);
}

public static void main(String[] args) {


BufferedReader teclado= new BufferedReader(new
InputStreamReader(System.in));
try {
System.out.println("Dame un numero de principio: ");
int a=Integer.parseInt(teclado.readLine());
System.out.println("Dame un numero del final: ");
int b=Integer.parseInt(teclado.readLine());
List<Integer> numeros= num(a,b);
for (int i:numeros){
System.out.println(i);
}
}catch (IOException e){
System.out.println(e.getMessage());
}
}
}

5
3.Escribir una función recursiva que devuelva la cantidad de dígitos de un número entero.

package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Ejercicio3 {


public static void main(String[] args) throws IOException {
double numero=0;
try{
BufferedReader consola= new BufferedReader(new
InputStreamReader(System.in));
System.out.println("Dame un numero: ");
numero= Double.parseDouble(consola.readLine());
System.out.println("Tu numero contiene
"+cantidadDigitos(numero)+" digitos.");
}catch (IOException e){
System.out.println(e.getMessage());
}

public static double cantidadDigitos(double digito){


int contador=0;
if(digito<10){
return 1;
}else{
return 1+cantidadDigitos(digito/10);
}
}
}

6
4. Escribir una función recursiva que calcule xy mediante multiplicaciones sucesivas, siendo x e y dos
números enteros.

package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Ejercico4 {


public static void main(String[] args) throws IOException {
int x=0;
int y=0;
try{
BufferedReader consola= new BufferedReader(new
InputStreamReader(System.in));
System.out.println("Dame un numero x: ");
x= Integer.parseInt(consola.readLine());
System.out.println("Dame otro numero y: ");
y= Integer.parseInt(consola.readLine());
System.out.println("X^y= "+potencia(x,y));
}catch (IOException e){
System.out.println(e.getMessage());
}
}

public static int potencia(int x, int y){


if(y==0){
return 1;
}else{
return x*potencia(x,y-1);
}
}

I
5. Calcula mediante un diseño recursivo el valor máximo de un vector de componentes numéricas.

package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Ejercicio5 {


public static void main(String[] args) throws IOException {
int tamanio=0;
try{
BufferedReader consola= new BufferedReader(new
InputStreamReader(System.in));
System.out.println("Dame el tamanio del vector: ");
tamanio= Integer.parseInt(consola.readLine());
int []lista= new int[tamanio];
for(int i=0; i<tamanio;i++){
System.out.println("Dame el num de la posicion "+i+": ");
lista[i]= Integer.parseInt(consola.readLine());
}
int max=maximo(lista, tamanio);
System.out.println("El max de tu lista es: "+max);
}catch (IOException e){
System.out.println(e.getMessage());
}
}
public static int maximo(int []lista, int tamanio){
//Caso base
if(tamanio==1){
return lista[0];
}else{//caso recursivo
int max=maximo(lista, tamanio-1);
if(lista[tamanio-1]>max){//compara el valor max obtenido
anteriormente con el actual
return lista[tamanio-1];//si el elemento actual es mayor
al anterior se devuelve ese
}
return max;//si el elemento actual es menor que el anterior,
devolvemos el anterior
}
}
}

8
6. Escribir la función recursiva que recibiendo como parámetros una cadena de dígitos
hexadecimales y su longitud devuelva el valor decimal que representa dicha cadena.
public class Ejercicio6 {
public static void main(String[] args)throws IOException {
int tamanio=0;
try{
BufferedReader consola= new BufferedReader(new
InputStreamReader(System.in));
System.out.println("Dame el tamanio del vector: ");
tamanio= Integer.parseInt(consola.readLine());
String []lista= new String[tamanio];
for(int i=0; i<tamanio;i++){
System.out.println("Dame la cadena nº"+i+": ");
lista[i]= consola.readLine();
}
for(int i=0; i<tamanio;i++){
System.out.println(decimales(lista, tamanio));
}
}catch (IOException e){
System.out.println(e.getMessage());
}
}
public static int decimales(String []lista, int tamanio){
int decimal=0;
for(int i=0; i<tamanio; i++){//estudiamos todos los hexadecimales
del array
String hex = lista[i];
int base = 1;//representa la base a la que se está
convirtiendo
// (en este caso, 16 para dígitos hexadecimales)
int dec = 0;//es el valor decimal actual de la cadena.
for (int j = hex.length() - 1; j >= 0; j--) {
char digit = hex.charAt(j);
int val = 0;
if (digit >= '0' && digit <= '9') {
val = digit - '0';
} else if (digit >= 'A' && digit <= 'F') {
val = 10 + digit - 'A';
} else if (digit >= 'a' && digit <= 'f') {
val = 10 + digit - 'a';
}
dec += val * base;
base *= 16;
}
decimal += dec;
}return decimal; } }

9
7. Calcular C (n,k) siendo: C (n,k)= C (n-1,k) + C (n-1,k-1) si n>k>0, 1 en otro caso.

package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Ejercicio7 {


public static void main(String[] args) throws IOException {
int n=0;
int k=0;
try{
BufferedReader consola= new BufferedReader(new
InputStreamReader(System.in));
System.out.println("Dame el numero de inicio: ");
n= Integer.parseInt(consola.readLine());
System.out.println("Dame el numero de finalizacion: ");
k= Integer.parseInt(consola.readLine());
System.out.println(c(n,k));
}catch (IOException e){
System.out.println(e.getMessage());
}
}

public static int c(int n, int k){


if (n > k && k > 0) {
return c(n - 1, k) + c(n - 1, k - 1);
} else {
return 1;
}
}
}

10
8. Cada tres bases de una cadena de ADN codifican un aminoácido de una proteína. Existe una
secuencia especial de bases de la cadena, ATG, que es una especie de “marca” del principio de un
gen, esto es, una secuencia de bases que codifica una determinada proteína. También existen tres
secuencias diferentes de fin de gen. Podemos a partir de una cadena de ADN contar cuántos genes
tiene contando el número de veces que ocurre la tripleta ATG. Genera una cadena de ADN aleatorio,
y a continuación cuenta cuántos genes contendría si se tratase de una cadena de ADN real.

package org.example;
import java.util.Random;

public class ContadorGenesADN {


public static void main(String[] args) {
String adn = generarADNAleatorio(1000); // Generar cadena de ADN
aleatoria de longitud 1000
System.out.println("Cadena de ADN aleatoria generada: " + adn);

int numGenes = contarGenesADN(adn);


System.out.println("Número de genes en la cadena de ADN: " +
numGenes);
}

// Función que genera una cadena de ADN aleatoria de la longitud


especificada
public static String generarADNAleatorio(int longitud) {
Random r = new Random();
StringBuilder sb = new StringBuilder(longitud);
for (int i = 0; i < longitud; i++) {
int base = r.nextInt(4); // Generar un número aleatorio entre
0 y 3
char c = ' ';
switch (base) {
case 0: c = 'A'; break;
case 1: c = 'C'; break;
case 2: c = 'G'; break;
case 3: c = 'T'; break;
}
sb.append(c);
}
return sb.toString();
}

// Función que cuenta el número de genes en una cadena de ADN


public static int contarGenesADN(String adn) {
int numGenes = 0;
int index = 0;
while (index != -1) {
index = adn.indexOf("ATG", index); // Buscar la siguiente
ocurrencia de la marca ATG
if (index != -1) { // Si se encontró la marca
numGenes++; // Contar un nuevo gen

11
int finGen = encontrarFinGen(adn, index); // Buscar la
secuencia de fin de gen correspondiente
if (finGen == -1) { // Si no se encontró la secuencia de
fin de gen correspondiente
break; // Salir del ciclo while
} else {
index = finGen; // Continuar buscando genes a partir
del fin del gen actual
}
}
}
return numGenes;
}

// Función que busca la secuencia de fin de gen correspondiente a la


secuencia de inicio de gen especificada
public static int encontrarFinGen(String adn, int index) {
String[] finGen = {"TAA", "TAG", "TGA"};
for (String fin : finGen) {
int finIndex = adn.indexOf(fin, index+3); // Buscar la
secuencia de fin de gen a partir de la posición de la marca ATG
if (finIndex != -1 && (finIndex - index) % 3 == 0) { // Si se
encontró la secuencia de fin de gen y su longitud es múltiplo de 3
return finIndex + 2; // Devolver la posición del último
nucleótido de la secuencia de fin de gen
}
}
return -1; // Si no se encontró ninguna secuencia de fin de gen
correspondiente, devolver -1
}
}

12
9. Dado un fichero de texto ordena sus líneas alfabéticamente, generando un archivo nuevo que
contenga el texto ordenado.

package org.example;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

public class OrdenadorLineasTexto {


public static void main(String[] args) {
String archivoEntrada = "texto.txt";
String archivoSalida = "texto_ordenado.txt";

ArrayList<String> lineas = leerLineasArchivo(archivoEntrada); //


Leer las líneas del archivo de entrada
Collections.sort(lineas); // Ordenar las líneas alfabéticamente

boolean exito = escribirLineasArchivo(archivoSalida, lineas); //


Escribir las líneas ordenadas en el archivo de salida
if (exito) {
System.out.println("Archivo " + archivoSalida + " generado
correctamente.");
} else {
System.out.println("Error al generar el archivo " +
archivoSalida + ".");
}
}

// Función que lee las líneas de un archivo y las devuelve en un


ArrayList
public static ArrayList<String> leerLineasArchivo(String archivo) {
ArrayList<String> lineas = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new
FileReader(archivo))) {
String linea;
while ((linea = br.readLine()) != null) {
lineas.add(linea);
}
} catch (IOException e) {
System.out.println("Error al leer el archivo " + archivo +
".");
e.printStackTrace();
}
return lineas;
}

13
// Función que escribe las líneas de un ArrayList en un archivo
public static boolean escribirLineasArchivo(String archivo,
ArrayList<String> lineas) {
try (FileWriter fw = new FileWriter(archivo)) {
for (String linea : lineas) {
fw.write(linea + "\n");
}
return true;
} catch (IOException e) {
System.out.println("Error al escribir en el archivo " +
archivo + ".");
e.printStackTrace();
return false;
}
}
}

14
10. Dado un fichero de texto realizar un programa que permita buscar en él una palabra dada. Las
palabras no tienen por qué estar ordenadas alfabéticamente y ninguna de ellas contiene la letra ñ.

package org.example;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BuscadorPalabras {


public static void main(String[] args) {
String archivo = "texto.txt";
String palabraBuscada = "programa";

int apariciones = buscarPalabraArchivo(archivo, palabraBuscada);

System.out.println("La palabra \"" + palabraBuscada + "\" aparece


" + apariciones + " veces en el archivo " + archivo + ".");
}

// Función que busca una palabra en un archivo y devuelve el número de


apariciones
public static int buscarPalabraArchivo(String archivo, String palabra)
{
int apariciones = 0;
try (BufferedReader br = new BufferedReader(new
FileReader(archivo))) {
String linea;
while ((linea = br.readLine()) != null) {
String[] palabras = linea.split("\\s+"); // Separar las
palabras de la línea por espacios en blanco
for (String p : palabras) {
if (p.equals(palabra)) {
apariciones++;
}
}
}
} catch (IOException e) {
System.out.println("Error al leer el archivo " + archivo +
".");
e.printStackTrace();
}
return apariciones;
}
}

15
11. Modificar el programa anterior para que emplee una búsqueda binaria.

package org.example;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;

public class BuscadorPalabras2 {


public static void main(String[] args) {
String archivo = "texto.txt";
String palabraBuscada = "programa";

int apariciones = buscarPalabraArchivo(archivo, palabraBuscada);

System.out.println("La palabra \"" + palabraBuscada + "\" aparece


" + apariciones + " veces en el archivo " + archivo + ".");
}

// Función que busca una palabra en un archivo y devuelve el número de


apariciones
public static int buscarPalabraArchivo(String archivo, String palabra)
{
int apariciones = 0;
try (BufferedReader br = new BufferedReader(new
FileReader(archivo))) {
String linea;
while ((linea = br.readLine()) != null) {
String[] palabras = linea.split("\\s+"); // Separar las
palabras de la línea por espacios en blanco
int indice = Arrays.binarySearch(palabras, palabra); //
Buscar la palabra en el array de palabras
if (indice >= 0) { // Si la palabra fue encontrada,
incrementar el contador de apariciones
apariciones++;
}
}
} catch (IOException e) {
System.out.println("Error al leer el archivo " + archivo +
".");
e.printStackTrace();
}
return apariciones;
}
}

16
12. Escribir un programa que permita introducir fechas al usuario. El usuario podrá introducir n
fechas, y a continuación podrá listar todas las fechas ordenadas cronológicamente.

package org.example;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

public class OrdenarFechas {


public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
ArrayList<String> fechas = new ArrayList<>();

// Pedir al usuario que introduzca las fechas


System.out.println("Introduce las fechas una por línea (en formato
dd/mm/aaaa), o escribe \"fin\" para terminar:");
String fecha;
while (true) {
fecha = scanner.nextLine();
if (fecha.equalsIgnoreCase("fin")) {
break;
}
fechas.add(fecha);
}

// Ordenar las fechas y mostrarlas al usuario


Collections.sort(fechas);
System.out.println("\nFechas ordenadas cronológicamente:");
for (String f : fechas) {
System.out.println(f);
}
}
}

17
EJERCICIOS
TEMA 6
1. Define una clase "Pareja", que podrá contener una pareja de datos de cualquier tipo (por ejemplo,
dos enteros, dos reales, dos cadenas de caracteres…). Crea un constructor que permita crear una
instancia de la clase, y proporciona dos métodos para acceder a cada uno de los dos datos de la
pareja de datos.

public class Pareja <A,B> {


private A primero;
private B segundo;
public Pareja(A primero, B segundo){
this.primero=primero;
this.segundo=segundo;
}
public B getSegundo() {
return segundo;
}
public A getPrimero() {
return primero;
}
public void setPrimero(A primero) {
this.primero = primero;
}
public void setSegundo(B segundo) {
this.segundo = segundo;
}

@Override
public String toString() {
return primero+", "+segundo;
}
}

19
2. Escribe un programa que cree una lista de datos reales. Introduce 100 valores aleatorios en la lista
y muéstralos por pantalla. Emplea al menos dos implementaciones diferentes de la lista.
package org.example;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class Ejercicio2 {


public static void main(String[] args) {
Random random = new Random();
List<Double> listaArrayList = new ArrayList<>();
List<Double> listaLinkedList = new LinkedList<>();

// Rellenamos las listas con valores aleatorios


for (int i = 0; i < 100; i++) {
double valor = random.nextDouble();
listaArrayList.add(valor);
listaLinkedList.add(valor);
}

// Mostramos los valores de la lista ArrayList


System.out.println("Valores de la lista ArrayList:");
for (double valor : listaArrayList) {
System.out.println(valor);
}

// Mostramos los valores de la lista LinkedList


System.out.println("\nValores de la lista LinkedList:");
for (double valor : listaLinkedList) {
System.out.println(valor);
}
}
}

28
3.Repite el ejercicio anterior, pero en esta ocasión crea una lista con 100 Parejas (clase del ejercicio
1). Las parejas deberán ser de enteros.

package org.example;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Ejercicio3 {


public static void main(String[] args) {
List<Pareja>list= new ArrayList<>(100);
Random random= new Random();
for (int i=0; i<100; i++){
int a= random.nextInt(10);
int b= random.nextInt(10);
Pareja<Integer, Integer> pareja= new Pareja<>(a,b);
list.add(pareja);
}
for (Pareja<Integer,Integer>pareja: list){
System.out.println(pareja);
}
}
}

21
4. Haz un programa que pida cadenas de caracteres al usuario y las vaya introduciendo en una lista.
El usuario tendrá una segunda opción en el menú del programa que consistirá en eliminar el último
dato introducido en la lista. Cada vez que se elimine o se introduzca un dato en la lista, se visualizará
el contenido completo de la lista. ¿Qué tipo de lista has empleado? ¿Por qué?
El motivo de utilizar una lista doblemente enlazada ( LinkedList) es que permite una
inserción y eliminación eficientes en cualquier posición de la lista, sin necesidad de
mover elementos. Además, como la lista puede crecer o decrecer dinámicamente, no es
necesario prever un tamaño máximo para ella. En cambio, si hubiéramos utilizado una
lista con un tamaño fijo (como un array), habría sido más difícil permitir la inserción y
eliminación en cualquier posición sin tener que reordenar los elementos de la lista.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;

public class Ej4T6 {


public static void main(String[] args) {
BufferedReader teclado= new BufferedReader(new
InputStreamReader(System.in));
List<String> lista= new LinkedList<>();
int opcion=0;
int posicion=0;
try {
do{
menu();
opcion=Integer.parseInt(teclado.readLine());
switch (opcion){
case 1:
System.out.println("Dame el caracter: ");
String c;
c=teclado.readLine();
lista.add(c);
break;
case 2:
for (String car: lista){
System.out.println(car);
}
System.out.println("Que posicion quieres
eliminar: ");

posicion=Integer.parseInt(teclado.readLine());
posicion -= 1;
if (posicion >= 0 && posicion <
lista.size()) {

22
lista.remove(posicion);
} else {
System.out.println("Posición inválida");
}
break;
default:
return;
}
}while (opcion!=3);
}catch(IOException e){
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1. Introducir caracter\n2. Eliminar
caracter\nOPCION:");
}
}

23
5. Haz un programa que pida cadenas de caracteres al usuario y las vaya introduciendo en una lista.
El usuario podrá introducir las cadenas de caracteres en cualquier posición de la lista. Cada vez que
introduzca una nueva cadena de caracteres, indicará la posición donde la quiere introducir y la
cadena de caracteres a introducir. El usuario también podrá eliminar cualquier cadena de caracteres
de la lista. ¿Qué tipo de lista has empleado? ¿Por qué?
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Ej5T6 {


public static void main(String[] args) {
BufferedReader teclado= new BufferedReader(new
InputStreamReader(System.in));
String cadena;
int posicion=0;
List <String> lista= new ArrayList<>();
int opcion=0;
try {
do{
menu();
opcion=Integer.parseInt(teclado.readLine());
switch (opcion){
case 1:
System.out.println("En que posicion quieres añadir
la cadena? ");
posicion=Integer.parseInt(teclado.readLine());
if (posicion >= 0 && posicion <= lista.size()) {
System.out.println("Cadena: ");
cadena = teclado.readLine();
lista.add(posicion, cadena);
} else {
System.out.println("Posición inválida.");
}
break;
case 2:
for (String cad: lista){
System.out.println(cad);
}
System.out.println("Posicon de la cadena a
eliminar: ");
posicion=Integer.parseInt(teclado.readLine());
lista.remove(posicion);
break;
default:
return;
}
}while (opcion!=3);
}catch (IOException e){

24
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1.Introducir cadena\n2.Eliminar
cadena\nOPCION:");
}
}
Se utiliza un ArrayList en el programa debido a su tamaño dinámico, acceso rápido por índice y
eficiencia en la inserción y eliminación de elementos. Además, su capacidad para almacenar
elementos duplicados ofrece flexibilidad en caso de que se requiera.

25
6. Haz un programa que pida cadenas de caracteres al usuario y las vaya almacenando. Los datos
deberán estar siempre ordenados. El usuario deberá tener también una opción para eliminar las
cadenas de caracteres. Cada vez que introduzca o elimine una cadena de caracteres, deberá
realizarse un listado de todos los datos. ¿Qué tipo de estructura de datos has empleado? ¿Por qué?

public class Ej6T6 {


public static void main(String[] args) {
BufferedReader teclado= new BufferedReader(new
InputStreamReader(System.in));
String cadena;
int posicion=0;
List<String> lista= new ArrayList<>();
int opcion=0;
try {
do{
menu();
opcion=Integer.parseInt(teclado.readLine());
switch (opcion){
case 1:
System.out.println("Cadena: ");
cadena = teclado.readLine();
lista.add(cadena);
break;
case 2:
for (String cad: lista){
System.out.println(cad);
}
System.out.println("Posicon de la cadena a
eliminar: ");
posicion=Integer.parseInt(teclado.readLine());
lista.remove(posicion);
break;
default:
return;
}
}while (opcion!=3);
}catch (IOException e){
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1.Introducir cadena\n2.Eliminar
cadena\nOPCION:");
}
}

26
7. Crea un mapa que contenga todos los números entre cero y 10 como valor, siendo la clave de
cada uno de ellos el nombre del número en texto. Lista el contenido del mapa.
import java.util.HashMap;
import java.util.Map;

public class Ej7T6 {


public static void main(String[] args) {
//HashMap: se usa para crea un mapa de nombres y numeros.
Map<String, Integer> numberMap = new HashMap<>();

// Agregar los números y sus nombres al mapa


//put(): lo usamos para agregar una entrada al mapa
numberMap.put("cero", 0);
numberMap.put("uno", 1);
numberMap.put("dos", 2);
numberMap.put("tres", 3);
numberMap.put("cuatro", 4);
numberMap.put("cinco", 5);
numberMap.put("seis", 6);
numberMap.put("siete", 7);
numberMap.put("ocho", 8);
numberMap.put("nueve", 9);
numberMap.put("diez", 10);

// Listar el contenido del mapa


//El for-each recorre todas las entradas del mapa
for (Map.Entry<String, Integer> entry : numberMap.entrySet()) {
//Se obtiene el nombre de la entrada
String numberName = entry.getKey();
//Se obtiene el número de esa entrada
int numberValue = entry.getValue();
System.out.println(numberName + ": " + numberValue);
}
}

27
8. Escribe un programa que, empleando un mapa, permita introducir al usuario conjuntos de pares
número/letra. El programa deberá permitir recuperar una letra a partir de su número
correspondiente.
import java.io.BufferedReader; import java.io.IOException;
import java.io.InputStreamReader; import java.util.HashMap; import
java.util.Map;

public class Ej8T6 {


public static void main(String[] args) {
Map<Integer, Character> mapa= new HashMap<>();
int num=0;
char letra=' ';
int op=0;
int numero=0;
BufferedReader teclado= new BufferedReader(new
InputStreamReader(System.in));
try {
do{
menu();
op=Integer.parseInt(teclado.readLine());
switch (op){
case 1:
System.out.println("Dame un numero: ");
num=Integer.parseInt(teclado.readLine());
System.out.println("Dame una letra: ");
letra=teclado.readLine().charAt(0);
mapa.put(num,letra);
break;
case 2:
System.out.println("Dime el numero: ");
numero=Integer.parseInt(teclado.readLine());
char encuentro= mapa.get(numero);
System.out.println("La letra de "+numero+":
"+encuentro);
break;
default:
return;
}
}while (op!=3);
}catch (IOException e){
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1.Introducir una entrada\n" +
"2.Recuperar numero" +"\nOPCION: "); } }

28
9. Empleando un TreeSet, escribe un programa que lea del usuario pares nombre/apellido de una
persona. El nombre completo (nombre y apellido) de la persona deberá representarse mediante una
clase. Empleando el TreeSet, mostrar ordenados alfabéticamente los nombres por apellido.
//Se utiliza impletents Comparable cuando en la clase
//principal tenemos un TreeSet y asi se puede ordenar
//de forma alfabetica.
public class Persona implements Comparable<Persona>{
private String nombre;
private String apellido;
public Persona(String nombre, String apellido){
this.nombre=nombre;
this.apellido=apellido;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellido() {
return apellido;
}
public void setApellido(String apellido) {
this.apellido = apellido;
}
@Override
public String toString() {
return "Nombre: "+nombre+"\nApellido: "+apellido;
}

@Override
public int compareTo(Persona otraPersona) {
// Comparamos por apellido y luego por nombre
int resultado = this.apellido.compareTo(otraPersona.apellido);
if (resultado == 0) {
resultado = this.nombre.compareTo(otraPersona.nombre);
}
return resultado;
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.TreeSet;

public class Ej9T6 {


public static void main(String[] args) {
BufferedReader teclado= new BufferedReader(new
InputStreamReader(System.in));
TreeSet<Persona> personas = new TreeSet<>();

29
String nombre;
String apellido;
int opcion=0;
try{
System.out.println("Introduce pares nombre/apellido (escribe
'fin' para terminar):");
do{
menu();
opcion=Integer.parseInt(teclado.readLine());
switch (opcion){
case 1:
System.out.print("Nombre: ");
nombre = teclado.readLine();
if (nombre.equalsIgnoreCase("fin")) {
break;
}
System.out.print("Apellido: ");
apellido = teclado.readLine();
personas.add(new Persona(nombre, apellido));
break;
case 2:
for (Persona persona : personas) {
System.out.println(persona);
}
break;
default:
return;
}

}while (opcion!=3);

}catch (IOException e){


System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1.Introducir perosna\n2.Ordenar\nOPCION: ");
}
}

30
10. Crea un programa que lea registros de tipo "venta" de la entrada estándar. Una venta constará,
al menos, del nombre del producto vendido, el nombre del cliente que lo ha adquirido, el precio de
la transacción y la fecha en la que se realizó la transacción. El programa deberá permitir listar los
registros introducidos ordenándolos según la fecha en la que se realizó la venta, alfabéticamente por
el nombre del cliente y según el precio de la transacción.
import java.time.LocalDate;

public class Venta {


private int coste;
private String nomProducto;
private String nomComprador;
private int coste_venta;
private LocalDate fecha;
public Venta(int coste, String nomProducto, String nomComprador, int
coste_venta, LocalDate fecha){
this.coste=coste;
this.nomComprador=nomComprador;
this.nomProducto=nomProducto;
this.coste_venta= coste_venta;
this.fecha=fecha;
}
public void setFecha(int dia, int mes, int anio) {
this.fecha = LocalDate.of(dia,mes,anio);
}
public LocalDate getFecha() {
return fecha;
}
public void setCoste(int coste) {
this.coste = coste;
}
public int getCoste() {
return coste;
}
public void setCoste_venta(int coste_venta) {
this.coste_venta = coste_venta;
}
public void setNomComprador(String nomComprador) {
this.nomComprador = nomComprador;
}
public void setNomProducto(String nomProducto) {
this.nomProducto = nomProducto;
}
public String getNomComprador() {
return nomComprador;
}
public int getCoste_venta() {
return coste_venta;
}
public String getNomProducto() {
return nomProducto;

31
}

@Override
public String toString() {
return "Nombre Producto: "+nomProducto+
"\nNombre Comprador: "+nomComprador+
"\nCoste venta: "+coste_venta+
"\nCoste compra: "+coste+
"\nFecha: "+fecha+"\n";
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Comparator;

public class Ej10T6 {


public static void main(String[] args) {
BufferedReader teclado= new BufferedReader(new
InputStreamReader(System.in));
int opcion=0;
int coste;
String nomProducto;
String nomComprador;
int coste_venta;
LocalDate fecha;
int dia;
int mes;
int anio;
List <Venta> ventas= new ArrayList<>();
try{
do{
menu();
opcion=Integer.parseInt(teclado.readLine());
switch (opcion){
case 1:
System.out.println("Dime el nombre del producto:
");
nomProducto=teclado.readLine();
System.out.println("Dime el nombre del comprador:
");
nomComprador=teclado.readLine();
System.out.println("Dime el coste del producto:
");
coste_venta=Integer.parseInt(teclado.readLine());
System.out.println("Dime el coste de compra: ");
coste=Integer.parseInt(teclado.readLine());

32
System.out.println("Dia: ");
dia=Integer.parseInt(teclado.readLine());
System.out.println("Mes: ");
mes=Integer.parseInt(teclado.readLine());
System.out.println("Anio: ");
anio=Integer.parseInt(teclado.readLine());
fecha= LocalDate.of(anio,mes,dia);
Venta venta= new Venta(coste,
nomProducto,nomComprador,coste_venta,fecha);
ventas.add(venta);
break;
case 2:
System.out.println("\nVentas registradas:");
ventas.sort(Comparator.comparing(Venta::getFecha)
.thenComparing(Venta::getNomComprador)
.thenComparingInt(Venta::getCoste_venta));
for (Venta venta1: ventas) {
System.out.println(venta1);
}
break;
default:
return;
}
}while (opcion!=3);
}catch (IOException e){
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1.Registrar venta\n2.Mostrar ventas\nOPCION:
");
}
}

33
11. Escribe un programa que lea todas las líneas de texto de un archivo de texto, a continuación, las
ordene, y escriba un nuevo archivo con el contenido ordenado.
import java.io.*; import java.util.ArrayList; import
java.util.Collections; import java.util.List;

public class Ej11T6 {


public static void main(String[] args) {
String inputFile = "archivo_entrada.txt";
String outputFile = "archivo_salida.txt";

try {
// Leer el archivo de entrada
List<String> lines = leerArchivo(inputFile);

// Ordenar las líneas de texto


Collections.sort(lines);

// Escribir el archivo de salida


escribirArchivo(outputFile, lines);

System.out.println("El archivo se ha ordenado


correctamente.");
} catch (IOException e) {
System.out.println("Error al procesar el archivo: " +
e.getMessage());
}
}
public static List<String> leerArchivo(String fileName) throws
IOException {
List<String> lines = new ArrayList<>();

try (BufferedReader reader = new BufferedReader(new


FileReader(fileName))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
} return lines;
}
public static void escribirArchivo(String fileName, List<String>
lines) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new
FileWriter(fileName))) {
for (String line : lines) {
writer.write(line);
writer.newLine();
}
} }

34
12. Escribe un programa que liste el nombre de todos los archivos de tu disco duro de modo
recursivo y vaya guardando el nombre en memoria, y que asocie este nombre a su ruta completa.
Una vez haya terminado el programa, el usuario podrá introducir el nombre de un archivo, y el
programa le mostrará su ruta completa. El programa también deberá permitir ordenar
alfabéticamente los nombres de todos los archivos y listarlos junto con la ruta completa en la que se
encuentran. (Para que el programa termine en un tiempo razonable, puedes sólo indexar el
contenido de un directorio de tu ordenador y no de todo tu disco duro).
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Ej12T6 {


private static Map<String, String> archivos = new HashMap<>();

public static void main(String[] args) {


BufferedReader teclado = new BufferedReader(new
InputStreamReader(System.in));
try {
// Ruta del directorio a buscar
String directorioRaiz = "C:/DirectorioRaiz";

// Buscar archivos y guardar sus nombres y rutas completas en


memoria
buscarArchivos(directorioRaiz);

// Ordenar alfabéticamente los nombres de los archivos


List<String> nombresArchivos = new
ArrayList<>(archivos.keySet());
Collections.sort(nombresArchivos);

// Mostrar la lista de archivos y rutas completas


System.out.println("Lista de archivos encontrados:");
for (String nombreArchivo : nombresArchivos) {
System.out.println(nombreArchivo + " -> " +
archivos.get(nombreArchivo));
}

// Solicitar al usuario que ingrese el nombre de un archivo


System.out.print("Introduce el nombre de un archivo: ");
String nombreBuscado = teclado.readLine();

// Buscar el archivo en la lista y mostrar su ruta completa


if (archivos.containsKey(nombreBuscado)) {
System.out.println("La ruta completa del archivo '" +
nombreBuscado + "' es: "
+ archivos.get(nombreBuscado));
} else {

35
System.out.println("El archivo '" + nombreBuscado + "' no
fue encontrado.");
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}

public static void buscarArchivos(String directorio) {


File carpeta = new File(directorio);
File[] archivosEnDirectorio = carpeta.listFiles();

if (archivosEnDirectorio != null) {
for (File archivo : archivosEnDirectorio) {
if (archivo.isFile()) {
// Guardar el nombre del archivo y su ruta completa en
memoria
archivos.put(archivo.getName(),
archivo.getAbsolutePath());
} else if (archivo.isDirectory()) {
// Realizar una búsqueda recursiva en los
subdirectorios
buscarArchivos(archivo.getAbsolutePath());
}
}
}
}
}

36
13. Compara el rendimiento de ordenar 1 millón de enteros aleatorios empleando un TreeSet,
colocándolos en una lista y empleando el método de ordenar de la clase Collections, y colocándolos
en un array y empleando el método de ordenar de la clase Arrays

import java.util.*;

public class Ej13T6 {


public static void main(String[] args) {
int n = 1000000;

// Generar una lista de enteros aleatorios


List<Integer> lista = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < n; i++) {
lista.add(random.nextInt());
}
// Ordenar utilizando TreeSet
long startTimeTreeSet = System.currentTimeMillis();
TreeSet<Integer> treeSet = new TreeSet<>(lista);
long endTimeTreeSet = System.currentTimeMillis();
long timeTreeSet = endTimeTreeSet - startTimeTreeSet;
// Ordenar utilizando Collections.sort()
long startTimeCollections = System.currentTimeMillis();
Collections.sort(lista);
long endTimeCollections = System.currentTimeMillis();
long timeCollections = endTimeCollections - startTimeCollections;

// Ordenar utilizando Arrays.sort()


Integer[] array = lista.toArray(new Integer[0]);
long startTimeArrays = System.currentTimeMillis();
Arrays.sort(array);
long endTimeArrays = System.currentTimeMillis();
long timeArrays = endTimeArrays - startTimeArrays;

// Mostrar los tiempos de ejecución


System.out.println("Tiempo TreeSet: " + timeTreeSet + " ms");
System.out.println("Tiempo Collections.sort(): " + timeCollections
+ " ms");
System.out.println("Tiempo Arrays.sort(): " + timeArrays + " ms");
}

37
PREGUNTAS
DESARROLLO
Explica las diferencias entre la recursión lineal final y la recursión lineal no final y nombra un ejemplo
de cada una.

La recursión lineal no final se caracteriza por combinar el resultado de la llamada recursiva en una
expresión para obtener el resultado de la función que la llama. Puede haber operaciones adicionales
después de la llamada recursiva antes de retornar un valor o finalizar la función. Un ejemplo típico
de este tipo de recursión es el cálculo del factorial de un número, donde el factorial de un número n
se obtiene multiplicando n por el factorial de n-1. En este caso, la llamada recursiva ocurre antes de
la operación de multiplicación.

Por otro lado, en la recursión lineal final, el resultado devuelto por la función es directamente el
resultado de la última llamada recursiva. En este caso, la llamada recursiva ocurre al final de la
función después de realizar todas las operaciones necesarias. Un ejemplo de esto es el cálculo del
factorial de un número, donde la llamada recursiva es la última instrucción ejecutada antes de
retornar el resultado.

Explica brevemente el método de la búsqueda binaria ¿Qué prerrequisitos debe cumplir?

La búsqueda binaria es un algoritmo eficiente utilizado para buscar un elemento específico en una
lista ordenada. Funciona dividiendo repetidamente la lista en dos mitades y descartando una de ellas
en función de si el elemento buscado es mayor o menor que el elemento en el medio.
Este proceso de división y descarte continúa hasta que se encuentre el elemento deseado o se
determine que no está presente en la lista.
Tiene los siguientes prerrequisitos:
- La lista debe estar ordenada: La búsqueda binaria solo funciona correctamente en una lista que
esté ordenada de forma ascendente o descendente. Si la lista no está ordenada, el algoritmo no
podrá encontrar el elemento deseado de manera eficiente.
- Acceso aleatorio a los elementos: Para dividir la lista y buscar en las mitades, se necesita un acceso
aleatorio a los elementos. Esto significa que se debe poder acceder directamente a cualquier
elemento de la lista en función de su posición, como en un arreglo o una estructura de datos que
admita el acceso aleatorio, como un vector. Si los prerrequisitos anteriores se cumplen, la búsqueda
binaria puede realizar búsquedas eficientes en una lista ordenada, reduciendo el espacio de
búsqueda a la mitad en cada paso.

38
Explica las diferencias entre una pila y una cola. ¿Cuándo conviene utilizar cada una de estas
estructuras? ¿Cuál es el orden de complejidad computacional de estas estructuras? No redacte el
orden tal y como puede ver en los apuntes, razónelo con sus propias palabras y argumentos.

Una lista es una estructura dinámica de datos constituida por nodos, donde se puede acceder,
eliminar y modificar elementos en cualquier posición. Tiene dos posibles tipos, pila y cola.

La Pila es una estructura de datos LIFO, que quiere decir que el ultimo que el último elemento
insertado es el primero en ser eliminado de la lista. Sus operaciones principales son apilar (push)
para agregar un elemento en la parte superior de la pila, desapilar (pop) para eliminar el elemento
superior de la pila, obtener el elemento del principio de la fila sin sacarlo de ella (top), y comprobar
si está vacía o vaciar la pila. Esta estructura se utiliza cuando se requiere un acceso rápido a los
elementos más recientes y no se necesita acceso directo a elementos intermedios o antiguos. Es útil
en computación para ejecutar programas por los compiladores o intérpretes, por ejemplo.

La Cola tiene una estructura FIFO, que significa, que el primer elemento que se agrega es el primero
es ser eliminado. Sus operaciones principales son añadir o eliminar un elemento, comprobar si la
cola está vacía o vaciarla, y obtener el primer elemento de la cola. Se utiliza cuando se necesita
mantener el orden original de los elementos y se requiere acceso rápido al elemento más antiguo. Es
útil en situaciones como la gestión de tareas en un sistema operativo (planificación de procesos), la
implementación de buffers o la implementación de algoritmos como el recorrido en amplitud de un
grafo.

Las operaciones principales (apilar, desapilar, encolar, desencolar) tienen un orden de complejidad
O(1), es decir, son operaciones de tiempo constante. Esto se debe a que el acceso y modificación de
los elementos se realiza directamente en el extremo correspondiente de la estructura (parte
superior de la pila o principio de la cola).

39
Explica las diferencias entre una tabla Hash y un árbol. ¿Cuándo conviene utilizar cada una de estas
estructuras? Justifique su respuesta y analice todas las posibles operaciones a realizar por las
estructuras. No redacte el orden tal y como puede ver en los apuntes, razónelo con sus propias
palabras y argumentos.

Las tablas hash y los árboles son estructuras de datos con características diferentes y se utilizan en
diferentes situaciones.

Las tablas hash son adecuadas cuando se necesita un acceso rápido a los datos y las colisiones son
mínimas. Son eficientes para buscar elementos por su clave única y también para insertar y eliminar
elementos cuando no hay muchas colisiones. Sin embargo, cuando el número de elementos es alto
en comparación con el tamaño del array base de la tabla, es probable que haya muchas colisiones, lo
que puede degradar el rendimiento. En general, las tablas hash son útiles cuando se necesita un
acceso rápido a los datos y las colisiones son gestionables.

Por otro lado, los árboles son una buena alternativa cuando se requiere encontrar, insertar o
eliminar elementos de manera eficiente sin restricciones en el número de elementos. Los árboles
son estructuras jerárquicas en las que cada nodo tiene un único antecesor y varios sucesores. Los
árboles binarios, en particular, son útiles para almacenar elementos con una relación de orden
definida de manera eficiente. La búsqueda en un árbol binario tiene un tiempo de O(log2(n)), lo que
lo hace bastante eficiente incluso para conjuntos de datos grandes. Los árboles también son
estructuras dinámicas, lo que significa que se pueden insertar y eliminar nodos en cualquier parte
del árbol sin necesidad de reorganizar la estructura completa.

En resumen, las tablas hash son adecuadas cuando se necesita un acceso rápido a los datos y las
colisiones son mínimas, mientras que los árboles son útiles cuando se requiere una búsqueda
eficiente y una estructura dinámica que permita insertar y eliminar elementos en cualquier posición.
La elección entre estas estructuras dependerá de las necesidades específicas del problema y de la
relación entre el número de elementos y el tamaño esperado del conjunto de datos.

40
Explica las diferencias entre una pila y una cola. ¿cuando conviene utilizar cada una de estas
estructuras ¿cual es el orden de complejidad de una operación de (1) insercion, de (2) acceso
aleatoro, y de (3) búsqueda de un dato?

Una pila y una cola son estructuras de datos lineales que se utilizan para organizar y manipular
conjuntos de elementos. La principal diferencia entre una pila y una cola radica en el orden en el que
se añaden y se eliminan los elementos.

Una pila (stack) sigue el principio de "último en entrar, primero en salir" (LIFO, Last-In-First-Out).
Esto significa que el último elemento que se añade a la pila es el primero en ser eliminado. Los
elementos se añaden y se eliminan solo en un extremo de la pila, conocido como la cima (top) de la
pila.

Una cola (queue), por otro lado, sigue el principio de "primero en entrar, primero en salir" (FIFO,
First-In-First-Out). Esto significa que el primer elemento que se añade a la cola es el primero en ser
eliminado.

- Inserción:
o Pila: La inserción en una pila (push) tiene un tiempo de complejidad O(1), ya que
simplemente se añade un elemento en la cima de la pila.
o Cola: La inserción en una cola también tiene un tiempo de complejidad O(1), ya que
se añade un elemento en la parte trasera de la cola.
- Acceso aleatorio:
o Pila: No hay acceso aleatorio en una pila. Solo se puede acceder al elemento que se
encuentra en la cima de la pila (top), y tiene un tiempo de complejidad O(1).
o Cola: Al igual que en una pila, no hay acceso aleatorio en una cola. Se puede acceder
al elemento que se encuentra en el frente o en la parte trasera de la cola, ambos con
un tiempo de complejidad O(1).
- Búsqueda de un dato:
o Pila: Las pilas no son eficientes para la búsqueda de un dato en particular, ya que no
proporcionan acceso aleatorio a sus elementos. Se tendría que realizar una
eliminación secuencial de elementos hasta encontrar el dato deseado, lo cual
tendría un tiempo de complejidad O(n), donde n es el número de elementos en la
pila.
o Cola: Al igual que en una pila, las colas tampoco son eficientes para la búsqueda de
un dato en particular. Se tendría que realizar una eliminación secuencial de
elementos hasta encontrar el dato deseado, lo cual tendría un tiempo de
complejidad O(n), donde n es el número de elementos en la cola.

41
Explica las diferencias entre una tabla Hash y un árbol. ¿Cuando conviene utilizar cada una de estas
estructuras? ¿cual es el orden de complejidad de una operacion de (1) insercion, de (2) acceso
aleatorio, de (3) búsqueda de un dato, y de (4) crecimiento de la estructura?

Las tablas hash y los árboles son estructuras de datos con características diferentes y se utilizan en
diferentes situaciones.

Las tablas hash son adecuadas cuando se necesita un acceso rápido a los datos y las colisiones son
mínimas. Son eficientes para buscar elementos por su clave única y también para insertar y eliminar
elementos cuando no hay muchas colisiones. Sin embargo, cuando el número de elementos es alto
en comparación con el tamaño del array base de la tabla, es probable que haya muchas colisiones, lo
que puede degradar el rendimiento. En general, las tablas hash son útiles cuando se necesita un
acceso rápido a los datos y las colisiones son gestionables.

Por otro lado, los árboles son una buena alternativa cuando se requiere encontrar, insertar o
eliminar elementos de manera eficiente sin restricciones en el número de elementos. Los árboles
son estructuras jerárquicas en las que cada nodo tiene un único antecesor y varios sucesores. Los
árboles binarios, en particular, son útiles para almacenar elementos con una relación de orden
definida de manera eficiente. La búsqueda en un árbol binario tiene un tiempo de O(log2(n)), lo que
lo hace bastante eficiente incluso para conjuntos de datos grandes. Los árboles también son
estructuras dinámicas, lo que significa que se pueden insertar y eliminar nodos en cualquier parte
del árbol sin necesidad de reorganizar la estructura completa.

Inserción:

- Tabla Hash: En el caso promedio, la inserción en una tabla hash tiene una complejidad de
O(1), es decir, constante. Sin embargo, en el peor caso, cuando hay muchas colisiones, la
complejidad puede llegar a ser O(n), donde n es el número de elementos.
- Árbol: La inserción en un árbol balanceado, tiene una complejidad de O(log n), donde n es el
número de elementos.

Acceso aleatorio:

- Tabla Hash: En el caso promedio, el acceso aleatorio en una tabla hash tiene una
complejidad de O(1), es decir, constante. Sin embargo, en el peor caso, cuando hay
colisiones, la complejidad puede ser O(n), donde n es el número de elementos.
- Árbol: El acceso aleatorio en un árbol balanceado tiene una complejidad de O(log n), donde
n es el número de elementos.

Búsqueda de un dato:

- Tabla Hash: En el caso promedio, la búsqueda en una tabla hash tiene una complejidad de
O(1), es decir, constante. Sin embargo, en el peor caso, cuando hay colisiones, la
complejidad puede ser O(n), donde n es el número de elementos.
- Árbol: La búsqueda en un árbol balanceado tiene una complejidad de O(log n), donde n es el
número de elementos.

Crecimiento de la estructura:

42
- Tabla Hash: El crecimiento de una tabla hash implica redimensionar la tabla, lo que puede
tener una complejidad de O(n), donde n es el número de elementos. Esto se debe a que se
deben recalcular las funciones de hash y reorganizar los elementos en la nueva tabla.
- Árbol: Los árboles pueden crecer dinámicamente sin necesidad de redimensionamiento,
pero algunos tipos de árboles balanceados pueden requerir operaciones de reequilibrio,
como rotaciones, con una complejidad de O(log n), donde n es el número de elementos.

¿Cuáles son las limitaciones del análisis O para medir la complejidad Computacional?

Existen limitaciones en el análisis "O" que se deben tener en cuenta:

- No es adecuado para pequeñas cantidades de datos: El análisis "O" se enfoca en el


comportamiento asintótico a medida que el tamaño de entrada tiende al infinito. Para
tamaños de entrada pequeños, el tiempo de ejecución real puede diferir significativamente
de la complejidad estimada. Algunos algoritmos con una complejidad aparentemente alta en
términos de "O" pueden ser más eficientes en la práctica para tamaños de entrada
pequeños.
- Constantes y factores ocultos: El análisis "O" no tiene en cuenta las constantes y factores
ocultos en el tiempo de ejecución de un algoritmo. En algunos casos, las constantes
asociadas a operaciones básicas pueden ser significativas y afectar el rendimiento general.
Además, algunos algoritmos pueden tener una notación "O" más baja pero ser menos
eficientes en la práctica debido a factores ocultos.
- Recursos de hardware y memoria: El análisis "O" asume que se dispone de recursos de
hardware y memoria infinitos. Sin embargo, en la realidad, los recursos son limitados y
pueden afectar el rendimiento de un algoritmo. Por ejemplo, el acceso a disco puede ser
significativamente más lento que el acceso a memoria, lo que puede impactar en la
eficiencia del algoritmo.
- No considera estructuras de datos específicas: El análisis "O" se centra en la complejidad de
los algoritmos y no tiene en cuenta las estructuras de datos utilizadas. Algunas estructuras
de datos pueden ser más eficientes en ciertos escenarios y mejorar el rendimiento de los
algoritmos.

En conclusión, el análisis "O" es una herramienta valiosa para evaluar la complejidad computacional,
pero tiene limitaciones en términos de tamaños de entrada pequeños, constantes ocultas, recursos
limitados y consideraciones específicas de las estructuras de datos. Es importante complementar el
análisis "O" con pruebas empíricas y considerar estos factores adicionales para comprender
completamente el rendimiento de un algoritmo en la práctica.

43
EJERCICIOS
PRÁCTICOS
Una lista reversa consiste en la ordenación de los elementos de la forma inversa. Diseña e
implementa el pseudocódigo de una función recursiva que devuelva una nueva lista inversa a la lista
introducida como parámetro S(n) u ordene la lista recibida como parámetro de forma inversa S(1).
La implementación del algoritmo con una nueva lista S(n) en lugar de la ordenación en la propia lista
S(1) será puntuable hasta 1 punto.

public static LinkedList<Integer> listaInversa(LinkedList<Integer> lista)


{
if (lista.isEmpty()) {
return new LinkedList<>();
} else {
int elemento = lista.removeLast();
LinkedList<Integer> nuevaLista = listaInversa(lista);
nuevaLista.add(elemento);
return nuevaLista;
}
}
En este pseudocódigo, utilizamos una implementación de lista enlazada (LinkedList) para representar
la lista de elementos. La función listaInversa toma como parámetro la lista original y utiliza recursión
para construir una nueva lista inversa. La función verifica si la lista original está vacía. Si es así, se
crea una nueva lista vacía y se devuelve. De lo contrario, se elimina el último elemento de la lista
original y se llama recursivamente a la función listaInversa con la lista restante. Luego, se agrega el
elemento eliminado a la nueva lista y se devuelve.

44
Calcular de forma recursiva la suma de los dígitos de un número siendo un ejemplo el número 102 ->
1 + 0 + 2 = 3. Recuerde que puede realizar los cambios de tipo que crea necesarios para facilitar la
resolución:

¿Qué tipo de recursividad se está empleando? ¿Qué complejidad computacional tiene el algoritmo?

public class SumaDigitos {


public static int sumaDigitos(int numero) {
if (numero < 10) {
return numero;
} else {
int ultimoDigito = numero % 10;
int restoNumero = numero / 10;
return ultimoDigito + sumaDigitos(restoNumero);
}
}
}
En este pseudocódigo, la función sumaDigitos toma un número como parámetro. La función verifica
si el número es menor que 10. Si es así, significa que el número tiene un solo dígito y se devuelve el
propio número.

De lo contrario, se obtiene el último dígito del número tomando el módulo 10 (numero % 10). Luego,
se divide el número entre 10 para eliminar el último dígito (numero / 10) y se llama recursivamente
a la función sumaDigitos con el número restante.

La llamada recursiva se repite hasta que el número sea menor que 10 y, en cada llamada, se suman
los últimos dígitos obtenidos.

El tipo de recursividad empleada es la recursividad directa. Es decir, la función se llama a sí misma de


forma directa. En cuanto a la complejidad computacional del algoritmo, en O(n), donde n
representa el número de dígitos del número insertado, ya que en cada llamada recursiva, se divide el
número por 10 y se realiza una operación constante.

45
a) Escribir una función recursiva que calcule xy mediante multiplicaciones sucesivas, siendo x e y

dos números enteros. ¿Cuál es el tipo de recursividad empleado?

b) Escribir una función recursiva que calcule xy con un orden computacional de O log(n)
aprovechando la propiedad

Xn*m = (Xn)m

Por ejemplo:

216 = (22)8 =(24)4

Y aplicando la metodología de divide y vencerás

x17 = x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x = x8 *x*x8 =(x4 *x4)*x*(x4 *x4)=(x2


*x2)*(x2 *x2)*x*(x2 *x2)*(x2 *x2) =(x*x)*(x*x)*(x*x)*(x*x)*x * (x * x) * (x * x) * (x * x) * (x * x) = (x2)17//2
* x = (x2*2)17//4 * x = (x2*2*2)17//8 * x = (x2*2*2*2)17//16 * x

a)

public static int powerRecursive(int x, int y) {


if (y == 0) {
return 1;
} else {
return x * powerRecursive(x, y - 1);
}
}

El tipo de recursividad empleado en esta función es la recursividad descendente, ya que la función se


llama a sí misma con un parámetro reducido en cada llamada hasta llegar al caso base (cuando y es
igual a 0).

b)

public static int powerOptimized(int x, int y) {


if (y == 0) {
return 1;
} else if (y % 2 == 0) {
int temp = powerOptimized(x, y / 2);
return temp * temp;
} else {
int temp = powerOptimized(x, y / 2);
return x * temp * temp;
}
}
Esta función utiliza la metodología de "divide y vencerás" y la propiedad X^(n*m) = (X^n)^m para
reducir el número de multiplicaciones necesarias. Divide y en mitades y realiza llamadas recursivas
utilizando la propiedad mencionada. Si y es par, se calcula la potencia de x elevado a la mitad de y y
se multiplica consigo mismo. Si y es impar, se realiza lo mismo pero se multiplica por x
adicionalmente. Esta función tiene un orden computacional de O(log n) debido a que en cada
llamada recursiva se divide y a la mitad, reduciendo la cantidad de multiplicaciones necesarias.

46
Calcular de forma recursiva C (n,k) siendo:

C (n,k)= C (n-1,k) + C (n-1,k-1) si n>k>0 , 1 en otro caso.

¿Qué tipo de recursividad se está empleando?

La función se define en función de sí misma y utiliza una recursividad múltiple, ya que hay dos
llamadas recursivas dentro del cuerpo de la función.
La recursión múltiple se produce porque se realiza una llamada recursiva para calcular C(n-1, k) y
otra llamada recursiva para calcular C(n-1, k-1). Estas dos llamadas recursivas se combinan mediante
la suma para obtener el resultado final.

Calcula la complejidad computacional de las siguientes funciones y ordénalas según orden creciente:

a) 4n * log(n) + 2n

El término dominante es 4n * log(n) ya que crece más rápido que 2n. Complejidad: O(n * log(n))

b) 24

Este es un valor constante, no depende del tamaño de entrada n. Complejidad: O(1)

c) 2n

La función tiene un crecimiento exponencial en función de n. Complejidad: O(2n)

d) 3n + 100 log(n)

El término dominante es 100 log(n) ya que crece más rápido que 3n. Complejidad: O(log(n))

e) n3 + n2 + 10

El término dominante es n3 ya que crece más rápido que n2 y 10. Complejidad: O(n^3)

f) log(n)

La función tiene un crecimiento logarítmico en función de n. Complejidad: O(log(n))

Ordenando las funciones de menor a mayor complejidad: f) - b) - d) - a) - e) - c)

47
Dadas dos funciones y su número de operaciones:

A =200n

B = 2n2

Calcular a partir de qué tamaño de entrada A es más eficiente que B.

Para determinar a partir de qué tamaño de entrada la función A es más eficiente que la función B,
necesitamos encontrar el punto en el cual el número de operaciones de A es menor que el número
de operaciones de B. Dado que A = 200n y B = 2n 2, podemos establecer la siguiente desigualdad:
200n < 2n2

200 < 2n

100 < n

Por lo tanto, para valores de n mayores a 100, la función A (200n) será más eficiente que la función B
(2n2).

Dadas dos funciones y su número de operaciones:

A = 1024n

B = 16n3

Calcular a partir de qué tamaño de entrada n A es más eficiente que B.

Para determinar a partir de qué tamaño de entrada la función A es más eficiente que la función B,
necesitamos encontrar el punto en el cual el número de operaciones de A es menor que el número
de operaciones de B. Dado que A = 1024n y B = 16n3, podemos establecer la siguiente desigualdad:
1024n < 16n3

1024 < 16n2

64 < n2

Tomamos la raíz cuadrada de ambos lados de la desigualdad: 8 < n Por lo tanto, para valores de n
mayores a 8, la función A (1024n) será más eficiente que la función B (16n3).

Dibuja las diferencias iteraciones del algoritmo de Quicksort con un ejemplo práctico donde se
elegirá como pivote el primer elemento de la lista para el siguiente conjunto de entrada.

50 20 84 13 22 16 89 8

48
Dado el siguiente código, suponiendo que todas las clases implicadas están correctamente
implementadas y que el método obtenerFechas () devuelve un array de tipo LocalDate de cualquier
longitud, implemente (sin retocar mas
líneas de las indicadas):

(a) Un código equivalente al de las líneas 13-15, utilizando un bucle for clásico.
public static void main(String[] args) {
LocalDate[] fechas = obtenerFechas();
for (int i = 0; i < fechas.length; i++) {
LocalDate fecha = fechas[i];
System.out.println("Fecha: " + fecha);
}
}

(b) Un código equivalente al de las líneas 13-15, utilizando un bucle while.


public static void main(String[] args) {
LocalDate[] fechas = obtenerFechas();
int i = 0;
while (i < fechas.length) {
LocalDate fecha = fechas[i];
System.out.println("Fecha: " + fecha);
i++;
}
}

49
Entre las clases que implementan el juego : The Iron Throne®,GAME OF THRONES , se encuentra una
denominada DataStore. Esta clase es la responsable de mantener todos los datos de la aplicación
(jugadores, ciudades, armas, etcétera) en memoria principal. En el juego, sólo existe una instancia de
un objeto de tipo DataStore que se carga (y almacena) automáticamente en disco, de manera
binaria, cada vez que la aplicación arranca (y es cerrada).

El método que realiza el almacenamiento de un objeto de tipo DataStore se denomina


saveDataStore, se encuentra en la clase GotIO y es el siguiente:

En base a estas especificaciones, se solicita que:

(a) Complete la clase GotIO implementando un método que recupere un objeto de tipo DataStore
(que ha sido previamente almacenado en el disco duro mediante el método saveDataStore). La
signatura del método que debe escribir es:

public static DataStore loadDataStore()

(b) Tenga en cuenta que el código mostrado para saveDataStore está incompleto. En el código de su
método loadDataStore deberá cerrar los recursos abiertos y capturar todas las excepciones que
ocurran durante el proceso.

a)

public static DataStore loadDataStore() {


DataStore data = null;
File file = new File("c:/got/data.dat");
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;

try {
fileInputStream = new FileInputStream(file);
objectInputStream = new ObjectInputStream(fileInputStream);
data = (DataStore) objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace(); // Manejo básico de excepciones, puedes
ajustarlo según tus necesidades
} finally {
try {
if (objectInputStream != null) {
objectInputStream.close();
}
if (fileInputStream != null) {
fileInputStream.close();

50
}
} catch (IOException e) {
e.printStackTrace();
}
}

return data;
}

b) En el código anterior se ha tenido en cuenta el cierre de los recursos abiertos en el bloque finally.
Además, se ha agregado la captura de excepciones para manejar cualquier error que pueda ocurrir
durante el proceso de lectura y deserialización del archivo.

Preguntas:

a) ¿Qué imprime el código? En caso de que no cumplile di que el motivo y arregle el programa

como considere conveniente. Explique su solución de manera concisa.

b) Explica brevemente qué cálculo está haciendo y qué tipo de recursividad está empleando.

Respuestas:

a)
public class Main {
public static void main(String[] args) {
int a=recursive (1,2);
System.out.println(a);
}
public static int recursive(int a, int b){
if (b==0){
return 1;
} else if (a==0) {
return 0;
}else {
return a*recursive(a,b-1);

St
}
}
}
El código imprime el valor resultante de la llamada al método recursive (1, -2). Sin embargo, esta
llamada causará una excepción debido a que la variable b está decrementando en cada llamada
recursiva sin llegar a alcanzar el valor de 0. Además, el valor de b inicialmente es -2, lo que no
permitirá que la condición b == 0 se cumpla. Para corregir esto, se puede ajustar los valores iniciales
de a y b en la llamada recursive (1, -2).

b) El cálculo que está haciendo el método recursive() es una exponenciación de “a” a la potencia b.
El método utiliza una recursividad descendente, donde en cada llamada se multiplica el valor actual
de a por el resultado de la llamada recursiva con b decrementado en 1. En caso de que b sea 0, se
devuelve 1. Si a es 0, se devuelve 0.

Explica el significado del siguiente diagrama UML. Céntrate especialmente en el significado de las
relaciones entre las clases.

Este diagrama UML nos muestra que los polígonos puesden estar compuestos por círculos,
rectángulos y triángulos. Y este último puede ser de tres tipos: escaleno, isoceles, equilatero.

¿Qué relación existe entre cada par de clases que se muestran bajo estas líneas? Explica la diferencia
entre ambas relaciones.

El primer diagrama nos muestra que los coches pueden tener un motor y cuatro ruedas. Pero al
estar la flecha en blanco nos indica que si no tienen alguna de estas características, igualmente,
seguirá siendo un coche.

En cambio, en el diagrama de la clase ordenador vemos que tiene que estar imperativamente
compuesto por una memoria y un procesador por que si no el dispositivo deja de tener sentido.

52
A partir del diagrama UML que se le muestra a continuación, y centrándose en el significado de los
símbolos UML que se utilizan para representar elementos software, responda a las siguientes
cuestiones:

(a) Describa detalladamente el tipo de relación existente entre las clases Persona, Médico y Técnico.

(b) Describa detalladamente el tipo de relación existente entre las clases Persona y Cita. Indique qué
significan las cardinalidades asociadas a la relación.

(c) Describa detalladamente el tipo de relación existente entre Persona y Dni.

a) Una persona es un trabajador: médico y/o técnico. Siendo esto una herencia múltiple.

b) Una persona puede tener 0 a infinitas citas. Esta herencia es una herencia simple y la cardinalidad
indica la cantidad de citas que puede tener una persona.

c) Como el enlace entre la persona y el DNI no es una flecha, sabemos que estas clases tienen una
relación bidireccional, lo que significa que hay una relación entre ellas pero no no específica la
naturaleza exacta.

La asociación entre "Persona" y "DNI" podría representar que una persona tiene un número de
identificación único asociado a ella, lo cual es comúnmente representado por un DNI (Documento
Nacional de Identidad).

53
Teniendo en cuenta las clases definidas en este ejercicio, explique y justifique (basándose en
conceptos de programación orientada a objetos como: herencia, polimorfismo, sobrescritura, . . . ),
las siguientes cuestiones:

Recuerde que debe explicar y justificar, utilizando los conceptos aprendidos, sus respues tas.
Respuestas no justificadas no serán puntuadas.

(a) ¿Qué sucede en las líneas 25-26 de la clase Test? ¿Esa parte del código compila?¿Por qué?

Si compila, ¿qué salida se imprimirá en pantalla al ejecutar esas líneas? ¿Por qué?.

En las líneas 25-26 de la clase Test, se crea un objeto de la clase Dinosaur utilizando el constructor
sin parámetros y luego se llama al método print() de ese objeto. Esta parte del código compila sin
problemas, ya que la clase Dinosaur tiene definido el método print(). La salida impresa en pantalla
será: “I’m a dinosaur.”

Esto se debe a que el objeto creado es de tipo Dinosaur y al llamar al método print(), se ejecuta la
implementación de la clase Dinosaur, que imprime "* I'm a dinosaur.".

(b) ¿Qué sucede en las líneas 27-28 de la clase Test? ¿Esa parte del código compila? ¿Por qué?

Si compila, ¿qué salida se imprimirá en pantalla al ejecutar esas líneas? ¿Por qué?.

En las líneas 27-28 de la clase Test, se crea un objeto de la clase CarnivorousDinosaur utilizando el
constructor sin parámetros y se asigna a la referencia dinosaur. Esta parte del código compila sin

54
problemas, ya que la clase CarnivorousDinosaur es una subclase de Dinosaur y puede ser asignada a
una referencia de tipo Dinosaur. La salida impresa en pantalla será: “* I'm a dinosaur.” “ I'm a
carnivorous dinosaur.” Esto se debe a que al llamar al método print() en la línea 28, se ejecuta la
implementación del método en la clase CarnivorousDinosaur, que a su vez llama al método print() de
la superclase Dinosaur usando super.print(), y luego imprime "* I'm a carnivorous dinosaur.".

(c) ¿Qué sucede en las líneas 29-30 de la clase Test? ¿Esa parte del código compila? ¿Por qué?

Si compila, ¿qué salida se imprimirá en pantalla al ejecutar esas líneas? ¿Por qué?.

En las líneas 29-30 de la clase Test, se crea un objeto de la clase TyrannosaurusRex utilizando el
constructor sin parámetros y se asigna a la referencia dinosaur. Esta parte del código compila sin
problemas, ya que la clase TyrannosaurusRex es una subclase de CarnivorousDinosaur, que a su vez
es una subclase de Dinosaur. Por lo tanto, un objeto de tipo TyrannosaurusRex también se puede
asignar a una referencia de tipo Dinosaur. La salida impresa en pantalla será: “I'm a dinosaur.” “ I'm
a carnivorous dinosaur.” “I'm a TyrannosaurusRex.”

(d)¿Qué sucede en las líneas 31-32 de la clase Test? ¿Esa parte del código compila? ¿Por qué?

Si compila, ¿qué salida se imprimirá en pantalla al ejecutar esas líneas? ¿Por qué?.

En las líneas 31-32 de la clase Test, se crea un objeto de la clase Spinosaurus utilizando el
constructor sin parámetros y se asigna a la referencia dinosaur. Esta parte del código compila sin
problemas, ya que la clase Spinosaurus es una subclase de CarnivorousDinosaur, que a su vez es una
subclase de Dinosaur. Por lo tanto, un objeto de tipo Spinosaurus también se puede asignar a una
referencia de tipo Dinosaur. La salida impresa en pantalla será: “* I'm a dinosaur.” “I'm a
carnivorous dinosaur.” “ I'm a Spinosaurus with 0 years.”

(e) ¿Qué sucede en la línea 33 de la clase Test? ¿Esa parte del código compila? ¿Por qué? Si compila,
¿qué salida se imprimirá en pantalla al ejecutar esa línea? ¿Por qué?.

En la línea 33 de la clase Test, se intenta acceder al atributo years de la referencia dinosaur. Sin
embargo, esto dará un error de compilación porque el atributo years es privado en la clase
Spinosaurus y no es accesible desde fuera de la clase.

(f) ¿Qué sucede en las líneas 34-40 de la clase Test? ¿ Esa parte del código compila? ¿Por qué?

Si compila, ¿qué salida se imprimirá en pantalla al ejecutar estas líneas? ¿Por qué?.

En las líneas 34-40 de la clase Test, se crea un array de objetos de tipo Dinosaur llamado dinosaurs.
Se inicializan dos elementos del array con objetos de las clases TyrannosaurusRex y Spinosaurus,
respectivamente. Luego se llama al método setYears() en el objeto de la clase Spinosaurus para
establecer el valor de la variable years en 5. A continuación, se realiza un bucle for-each para iterar
sobre los elementos del array y llamar al método print() en cada uno de ellos. Esta parte del código
compila sin problemas, ya que las clases TyrannosaurusRex y Spinosaurus son subclases de Dinosaur
y, por lo tanto, se pueden asignar a elementos del array de tipo Dinosaur. La salida impresa en
pantalla será: “ I'm a dinosaur.” “I'm a carnivorous dinosaur.” “I'm a TyrannosaurusRex.” “I'm a
dinosaur.” “I'm a carnivorous dinosaur.” “I'm a Spinosaurus with 5 years.”

55
EJERCICIOS
DESARROLLO
1) En la aplicación de contactos de un móvil con sistema operativo Android, el usuario tiene una
Agenda que incluye una lista de contactos. Cada Contacto almacena, entre otras características, el
nombre, el apodo, una lista de direcciones de email, una lista de Teléfonos y un valor lógico
(true/false) que indica si las llamadas de este contacto hay que silenciarlas.

Por su parte, cada Teléfono incluye su tipología (casa, trabajo, móvil, otros), el número de teléfono al
que hace referencia y un valor lógico que indica si es el teléfono preferido para llamar a esa persona.

No es necesario implementar el docString correspondiente a las funciones y métodos desarrollados,


aunque se recomienda hacerlo para facilitar el trabajo del estudiante. En base a estas
especificaciones se solicita que:

a) Programe la clase Teléfono.


- Incluya sus atributos y establezca la visibilidad adecuada (público, privado, protegido).
- Si necesitase algún enumerado, prográmelo.
- Programe un constructor que reciba los datos necesarios para crear un teléfono. Por
defecto, el teléfono se creará como no preferido.
- Programe los métodos setters y getters para el atributo número de teléfono.
- Programe el método def setPreferido(valor). Este método establece si el teléfono se marca
como preferido (o no). Se debe hacer el control de excepciones para este método.
- Programe el método __eq__(self, otherTelefon) de la clase Teléfono.
- Instancie un objeto de la clase Teléfono en un método main, sin realizar pruebas.
- Asuma que la función def isCorrectNumber(telephoneNumber) está ya implementada y
devuelve un valor booleano (True o False) si el número es correcto o incorrecto, haciendo
comprobaciones de tipo y controlando en su interior todas las posibles excepciones (esto es,
esta función no lanza excepciones).
b) Programe la clase Contacto.
- Incluya sus atributos y establezca la visibilidad adecuada.
- Suponga que la clase Email está correctamente implementada.
- Programe un constructor que reciba los datos necesarios para crear un contacto.
- Programe el método def silenciar(). Este método anota que las llamadas de este contacto
hay que silenciarlas.
- Programe el método def telefonoFavorito(telefono). Este método busca entre todos los
teléfonos del contacto y marca el teléfono indicado como preferido (y desmarca el resto de
teléfonos). Al finalizar este método, sólo puede quedar un teléfono marcado como
preferido. Tenga en cuenta que, si el teléfono que se intenta marcar como preferido no está
en la lista del usuario, debería lanzar una excepción indicándolo.
- Para realizar este apartado, puede dar por supuesto que ha implementado correctamente la
clase Teléfono del apartado anterior.
c) Programe la clase Agenda.
- Incluya sus atributos y establezca la visibilidad adecuada.
- Programe un constructor por defecto para esta clase.
- def preferidoTelefonoContacto(contacto, telefono) es un método que se utiliza para marcar
el teléfono teléfono del contacto contacto de la agenda como favorito. Programe el método
y gestione las excepciones que puedan surgir.
- Para realizar este apartado, puede dar por supuesto que ha implementado correctamente
las clases Contacto y Teléfono de los apartados anteriores.
- Añada, si lo necesita, los métodos auxiliares que crea convenientes.

56
package org.example.aplicacionContactos;
import java.util.List;

public class Telefono {


public enum Tipo {
CASA,
TRABAJO,
MOVIL,
OTROS
}

private String numeroTelefono;


private Tipo tipoTelefono;
private boolean preferido;

public Telefono(String numeroTelefono, Tipo tipoTelefono) {


this.numeroTelefono = numeroTelefono;
this.tipoTelefono = tipoTelefono;
this.preferido = false;
}

public String getNumeroTelefono() {


return numeroTelefono;
}

public void setNumeroTelefono(String numeroTelefono) {


this.numeroTelefono = numeroTelefono;
}

public Tipo getTipoTelefono() {


return tipoTelefono;
}

public void setTipoTelefono(Tipo tipoTelefono) {


this.tipoTelefono = tipoTelefono;
}

public boolean isPreferido() {


return preferido;
}

public void setPreferido(boolean valor) {


this.preferido = valor;
}

public boolean equals(Telefono otherTelefono) {


return
this.numeroTelefono.equals(otherTelefono.getNumeroTelefono());
}

57
}

package org.example.aplicacionContactos;
import java.util.ArrayList;
import java.util.List;

public class Contacto {


private String nombre;
private String apodo;
private List<String> direccionesEmail;
private List<Telefono> telefonos;
private boolean silenciarLlamadas;

public Contacto(String nombre, String apodo) {


this.nombre = nombre;
this.apodo = apodo;
this.direccionesEmail = new ArrayList<>();
this.telefonos = new ArrayList<>();
this.silenciarLlamadas = false;
}

public void silenciar() {


this.silenciarLlamadas = true;
}

public void telefonoFavorito(Telefono telefono) throws Exception {


if (!telefonos.contains(telefono)) {
throw new Exception("El teléfono no está en la lista del
contacto.");
}

for (Telefono t : telefonos) {


if (t.equals(telefono)) {
t.setPreferido(true);
} else {
t.setPreferido(false);
}
}
}
}

package org.example.aplicacionContactos;
import java.util.ArrayList;
import java.util.List;

public class Agenda {


private List<Contacto> contactos;

public Agenda() {

58
this.contactos = new ArrayList<>();
}

public void preferidoTelefonoContacto(Contacto contacto, Telefono


telefono) {
try {
contacto.telefonoFavorito(telefono);
} catch (Exception e) {
e.printStackTrace();
}
}
}

2) Se desea introducir una nueva funcionalidad a los contactos de la agenda especificada en el


ejercicio anterior. Se quiere introducir distintos tipos de contactos que pueden clasificarse como:
trabajo, amigo, familia.

- Los contactos de tipo trabajo, además de la información habitual de un contacto: o Incluyen un


atributo acerca de la dirección de su casa.

- Los contactos de tipo amigo, además de la información habitual de un contacto: o Incluyen un


atributo acerca de la dirección de su casa.

o Incluyen la fecha de su cumpleaños.

- Los contactos de tipo familia, además de la información habitual de un contacto:

o Incluyen un atributo acerca de la dirección de su casa.

o Incluyen la fecha de su cumpleaños.

o Incluyen un atributo acerca del tipo de relación que les une: hermanos, padres, suegros, cuñados.

En base a estas especificaciones se solicita que:

a) Modifique la clase Contacto para incluir el tipo de contacto (familia, amigos, trabajo) e
implemente las clases necesarias para representar los distintos tipos de contactos.

- Escoja la jerarquía que permita reutilizar el máximo código posible.

- Añada los atributos que se especifican en el enunciado y establezca su visibilidad.

b) Implemente los constructores de las clases reutilizando al máximo todo el código disponible.
Tenga en cuenta que:

- Deberá lanzar una excepción si en el constructor de un contacto de tipo amigo o familia recibe una
fecha de cumpleaños que es mayor que la fecha actual. Se puede suponer implementada el método
de comparación de fecha (fecha1 >/==/< fecha2)

c) Implemente el método __str__(self) de tal manera que se muestren todas las características de
cada contacto. Este método deberá estar disponible para todos los tipos de contactos. Suponga que
el método __str__(self) de la clase Contacto está implementado correctamente y puede instanciarse
mediante la sintaxis super().__str__(). Reutilice al máximo todo el código disponible.

59
d) En la clase Contacto, implemente un método que pueda recibir cualquier tipo de contacto de los
definidos anteriormente (se deben recordar las características de Python y el tipado..). Este método
invocará al __str__() y posteriormente, lo sacará por pantalla. Reutilice todo el código posible.
Explique cómo ha implementado el método y de qué características de Python y de la programación
orientada a objetos se ha beneficiado para realizar esta tarea.

public class Contacto2 {


private String nombre;
private String apodo;
private String direcciónCasa;
private boolean silenciarLlamadas;

public Contacto2(String nombre, String apodo, String direcciónCasa) {


this.nombre = nombre;
this.apodo = apodo;
this.direcciónCasa = direcciónCasa;
this.silenciarLlamadas = false;
}

public void silenciar() {


this.silenciarLlamadas = true;
}

@Override
public String toString() {
return "Nombre: " + nombre + "\n" +
"Apodo: " + apodo + "\n" +
"Dirección de casa: " + direcciónCasa;
}

public void mostrarInformación() {


System.out.println(this.toString());
}
}
public class TrabajoContacto extends Contacto2{
private String direccionCasa;
private String direccionTrabajo;

public TrabajoContacto(String nombre, String apodo, String


direcciónCasa, String direcciónTrabajo) {
super(nombre, apodo, direcciónCasa);
this.direccionCasa = direcciónCasa;
this.direccionTrabajo = direcciónTrabajo;
}

@Override
public String toString() {
return super.toString() + "\n" +
"Dirección de trabajo: " + direccionTrabajo;
}

60
}

import java.time.LocalDate;

public class FamiliaContacto extends Contacto2 {


private String direcciónCasa;
private LocalDate fechaCumpleaños;
private String tipoRelación;
public FamiliaContacto(String nombre, String apodo, String
direcciónCasa, LocalDate fechaCumpleaños, String tipoRelación) {
super(nombre, apodo, direcciónCasa);
this.direcciónCasa = direcciónCasa;
this.fechaCumpleaños = fechaCumpleaños;
this.tipoRelación = tipoRelación;
validarFechaCumpleaños();
}
private void validarFechaCumpleaños() {
LocalDate fechaActual = LocalDate.now();
if (fechaCumpleaños.isAfter(fechaActual)) {
throw new IllegalArgumentException("La fecha de cumpleaños es
mayor que la fecha actual.");
}
}
@Override
public String toString() {
return super.toString() + "\n" +
"Fecha de cumpleaños: " + fechaCumpleaños + "\n" +
"Tipo de relación: " + tipoRelación;
}
}
import java.time.LocalDate;

public class AmigoContacto extends Contacto2 {


private String direcciónCasa;
private LocalDate fechaCumpleaños;

public AmigoContacto(String nombre, String apodo, String


direcciónCasa, LocalDate fechaCumpleaños) {
super(nombre, apodo, direcciónCasa);
this.direcciónCasa = direcciónCasa;
this.fechaCumpleaños = fechaCumpleaños;
validarFechaCumpleaños();
}

private void validarFechaCumpleaños() {


LocalDate fechaActual = LocalDate.now();
if (fechaCumpleaños.isAfter(fechaActual)) {
throw new IllegalArgumentException("La fecha de cumpleaños es
mayor que la fecha actual.");
}

61
}

@Override
public String toString() {
return super.toString() + "\n" +
"Fecha de cumpleaños: " + fechaCumpleaños;
}
}

62
1) En la red social Twitter, cada usuario es propietario de una cuenta (UserAccount) en la que,
básicamente, se especifica un alias (que cumple las funciones de identificador único) y un email de
contacto. En la cuenta, además, se incluye el conjunto de tweets que el propietario va publicando a
lo largo del tiempo.
Como la cantidad de mensajes que maneja la red es inmensa, una característica original de Twitter
es que cada usuario puede seleccionar la información que le interesa recibir. De esta manera, el
propietario de una UserAccount puede convertirse en seguidor (follower) de otros usuarios,
mostrando su interés en los tweets que ellos publiquen. Así, cada vez que un usuario publica un
tweet, éste es incluido en el timeline de la UserAccount de cada uno de sus followers (es decir, el
timeline se corresponde con el conjunto de tweets recibidos).

En base a estas especificaciones se solicita que:

a) Programe la clase UserAccount y su constructor. Incluya todos sus atributos (alias, email, tweets,
followers, timeline) y establezca la visibilidad adecuada. Indica el tipo de datos de todos los atributos
y parámetros del constructor y suponga que ya tiene implementadas correctamente las clases Tweet
y Email.

Justifique, brevemente, porqué ha seleccionado cada estructura de datos para los atributos. No es
necesario realizar control de excepciones ni pruebas.

b) Implemente, en UserAccount, un método que permita a un usuario seguir a otro:

• def follow(user2)

• Al ejecutar “user.follow(user2)”, el usuario user se convertirá en follower de user2.

• Añada, si lo necesita, métodos auxiliares (por ejemplo, para manejar los followers de

user2).

• No es necesario realizar control de excepciones ni pruebas. Se debe indicar el tipo de

datos que recibe cada método (con un comentario)

c) Implemente, en UserAccount, un método que permita a un usuario publicar un Tweet:

• def tweet(tweet1)

• Después de ejecutar el método “user.tweet(tweet1)”, se deberá actualizar adecuadamente el


atributo tweets de user. Además, todos los followers de user habrán recibido el tweet1 en su
timeline.

• Añada, si lo necesita, métodos auxiliares (por ejemplo, para manejar el timeline de los followers).

import java.util.ArrayList;
import java.util.List;

public class UserAccount {


private String alias;
private String email;
private List<Tweet> tweets;
private List<UserAccount> followers;
private List<Tweet> timeline;

63
public UserAccount(String alias, String email) {
this.alias = alias;
this.email = email;
this.tweets = new ArrayList<>();
this.followers = new ArrayList<>();
this.timeline = new ArrayList<>();
}

public void follow(UserAccount user2) {


user2.addFollower(this);
}

private void addFollower(UserAccount follower) {


followers.add(follower);
}

public void tweet(Tweet tweet1) {


tweets.add(tweet1);
updateFollowersTimeline(tweet1);
}

private void updateFollowersTimeline(Tweet tweet) {


for (UserAccount follower : followers) {
follower.addToTimeline(tweet);
}
}

private void addToTimeline(Tweet tweet) {


timeline.add(tweet);
}
}
Para el atributo alias se utiliza el tipo String, ya que representa un identificador único para la cuenta
de usuario y se almacena como texto.

El atributo email también se utiliza como tipo String, ya que representa la dirección de correo
electrónico del usuario y se almacena como texto.

El atributo tweets se implementa como una lista (List) de objetos Tweet, lo que permite almacenar
múltiples tweets en el orden en que se publicaron.

El atributo followers es una lista de objetos UserAccount, que representa a los seguidores de la
cuenta de usuario. Almacenar los seguidores en una lista permite un fácil acceso y manejo de los
seguidores.

El atributo timeline también se implementa como una lista de objetos Tweet, ya que representa los
tweets que el usuario ha recibido de sus seguidores. Almacenarlos en una lista permite mantener el
orden cronológico de los tweets en el timeline del usuario.

El método follow() permite a un usuario seguir a otro. Al llamar a este método, el usuario actual
(user) se agrega a la lista de seguidores del usuario al que desea seguir (user2). Esto se logra
mediante el método addFollower() que agrega el seguidor a la lista de seguidores del usuario2.

64
El método tweet() permite a un usuario publicar un tweet. Al llamar a este método, el tweet se
agrega a la lista de tweets del usuario (tweets). Luego, se llama al método
updateFollowersTimeline() para actualizar el timeline de todos los seguidores del usuario con el
nuevo tweet. Este método itera sobre la lista de seguidores y llama al método addToTimeline() de
cada seguidor para agregar el tweet al timeline de cada uno.

2) En la red social Twitter, la unidad básica de información se denomina Tweet. Un Tweet es creado
en un instante de tiempo concreto (time), contiene un mensaje (message) con un máximo de 140
caracteres de longitud y es publicado por un

usuario (conocido como sender). Además, existen dos tipos de Tweet especiales:

• DirectMessage: Los mensajes directos son Tweets que permiten comunicarse, de manera privada,
a dos usuarios dentro de la red. Estos DirectMessage son como Tweets ya que contienen un mensaje
(message), son publicados por un emisor (sender) y son creados en un instante de tiempo
determinado (time); la única diferencia es que incluyen a otro usuario como receptor (receiver) del
tweet.

• Retweet: Cuando un usuario lee un tweet interesante que le ha llegado a su timeline, y quiere
reenviarlo a su lista de followers, crea un retweet. Este Retweet es como un Tweet, es decir, el
usuario que lo publica (sender) puede poner un mensaje (message) y lo crea en un tiempo
determinado (time); la única diferencia es que el Retweet incluye una referencia al Tweet que se
reenvía.

En base a estas especificaciones se solicita que:

a) Implemente las clases Tweet, Retweet y DirectMessage escogiendo la jerarquía más adecuada.
Añada los atributos que se especifican en el enunciado y establezca su visibilidad.

• Reutilice todo el código que pueda. Para el atributo time, se recomienda utilizar la clase Date de la
librería estándar de Java.

• Suponga que ya tiene implementada correctamente la clase UserAccount.

b) Implemente los constructores de las clases reutilizando al máximo todo el código disponible.

• Además, compruebe las restricciones de datos (por ejemplo, el constructor debería lanzar

una excepción si el mensaje que se le pasa, contuviese más de 140 caracteres).

• Recuerde que la librería estándar tiene una función len(string) que devuelve la longitud de un

string.

c) Implemente el método __str__(self) en las tres clases, reutilizando al máximo todo el código
disponible. Suponga que las clases date y UserAccount ya tiene este método implementado
correctamente.

d) Responda a las siguiente preguntas:

• ¿Deberá modificar los atributos timeline y tweets de la clase UserAccount (definida en el

ejercicio 1) para que contenga elementos de la clase hija Retweet? Justifique su

65
razonamiento y, si cree que hay que modificarlos, explique también cómo lo haría.

• ¿Deberá modificar el método def tweet(Tweet tweet1) de la clase UserAccount (definida en el


ejercicio 1) para que pueda enviar también objetos de tipo Retweet? Justifique su

razonamiento y, si cree que hay que modificarlo, explique también cómo lo haría.

import java.util.Date;

public class Tweet {


private Date time;
private String message;
private UserAccount sender;

public Tweet(Date time, String message, UserAccount sender) {


this.time = time;
this.message = message;
this.sender = sender;
}

public String toString() {


return "[" + time + "] @" + sender.getAlias() + ": " + message;
}
}

public class Retweet extends Tweet {


private Tweet originalTweet;

public Retweet(Date time, String message, UserAccount sender, Tweet


originalTweet) {
super(time, message, sender);
this.originalTweet = originalTweet;
}

public String toString() {


return super.toString() + " (Retweet of @" +
originalTweet.getSender().getAlias() + ": " +
originalTweet.getMessage() + ")";
}
}
public class DirectMessage extends Tweet {
private UserAccount receiver;

public DirectMessage(Date time, String message, UserAccount sender,


UserAccount receiver) {
super(time, message, sender);
this.receiver = receiver;
}

public String toString() {


return super.toString() + " (Direct Message to @" +

56
receiver.getAlias() + ")";
}
}
a) En la jerarquía de clases, se utiliza la herencia para definir las clases Retweet y DirectMessage
como subclases de la clase base Tweet. Esto permite reutilizar código y heredar los atributos y
métodos de la clase Tweet.

b) Los constructores de las clases Tweet, Retweet y DirectMessage se implementan reutilizando el


código existente en la clase base Tweet y agregando los atributos adicionales necesarios para cada
tipo de tweet. Además, se realiza una comprobación en el constructor para verificar que el mensaje
no supere los 140 caracteres, lanzando una excepción en caso contrario.

c) El método toString() se implementa en las tres clases para representar los tweets, retweets y
mensajes directos en forma de texto. Se reutiliza el código de la clase base Tweet y se agrega la
información específica de cada tipo de tweet.

d) En cuanto a las preguntas planteadas:

No es necesario modificar los atributos timeline y tweets de la clase UserAccount para que
contengan elementos de la clase Retweet. La clase Retweet es una subclase de la clase Tweet, por lo
que se puede almacenar en los atributos existentes sin necesidad de modificaciones adicionales.

No es necesario modificar el método tweet(Tweet tweet1) de la clase UserAccount para permitir el


envío de objetos de tipo Retweet. Dado que Retweet es una subclase de Tweet, se puede pasar un
objeto Retweet como argumento al método existente sin necesidad de modificaciones adicionales.
El método simplemente agregará el Retweet a la lista de tweets y actualizará el timeline de los
seguidores de manera adecuada.

67
Suponga que la clase Utils contiene los métodos: leerCadena, leerEntero, leerFloat y leerDouble
correctamente implementados. Utilícelos para leer por consola como lo hace en los ejercicios de
clase.

Implemente una clase main que permita al iniciarse Cargue una lista de usuarios de un

fichero. Utilice las estructuras de datos que considere más adecuadas y justifique su uso. Puede
crear las funciones y métodos que considere convenientes o necesarias en las clases
correspondientes previamente solicitadas. Sólo debe especificar qué clases son en caso de que lo
haga, no debe redifinir la clase completa. Posteriormente habrá un menú que permita:

a) Cargar un usuario en memoria realizando una búsqueda secuencial con centinela sobre el
conjunto de datos previamente cargado. Puede crear las funciones y métodos que considere
convenientes o necesarias en las clases correspondientes previamente solicitadas. Sólo debe
especificar qué clases son en caso de que lo haga, no debe redifinir la clase completa.

b) Publicar un tweet por el usuario cargado previamente en memoria. Puede asumir que siempre
habrá un usuario cargado en memoria. Debe gestionar que si el tweet sobrepasa los 140 caracteres
se debe mostrar un error al usuario. Para ello, utilice la gestión de excepciones adecuada y no haga
comprobaciones extra de entrada/salida. Puede crear las clases, funciones y métodos que considere
convenientes o necesarias. Sólo debe especificar qué clases son en caso de que las modifique, no
debe redifinir la clase completa. Utilice las estructuras de datos que considere más adecuadas y
justifique su uso.

c) Ordenar los diferentes usuarios en base a 2 criterios diferentes: 1. El email de forma ascendente 2.
El nombre de usuario de forma descendente.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main2 {


private static final String FILE_PATH = "usuarios.txt";
private static List<UserAccount> userList;

public static void main(String[] args) {


// Cargar la lista de usuarios desde un archivo
loadUserList();

// Menú principal
boolean exit = false;
while (!exit) {
System.out.println("Seleccione una opción:");
System.out.println("1. Cargar un usuario en memoria");
System.out.println("2. Publicar un tweet");

68
System.out.println("3. Ordenar usuarios por email
(ascendente)");
System.out.println("4. Ordenar usuarios por nombre de usuario
(descendente)");
System.out.println("5. Salir");

int option = Utils.leerEntero();


switch (option) {
case 1:
loadUser();
break;
case 2:
publishTweet();
break;
case 3:
sortByEmail();
break;
case 4:
sortByUsername();
break;
case 5:
exit = true;
break;
default:
System.out.println("Opción inválida. Por favor,
seleccione nuevamente.");
}
}
}

private static void loadUserList() {


userList = new ArrayList<>();

try (BufferedReader br = new BufferedReader(new


FileReader(FILE_PATH))) {
String line;
while ((line = br.readLine()) != null) {
// Parsear los datos de cada línea y crear una instancia
de UserAccount
// Agregar la instancia a la lista de usuarios
// userList.add(userAccount);
}
} catch (IOException e) {
System.out.println("Error al leer el archivo de usuarios: " +
e.getMessage());
}
}

private static void loadUser() {


System.out.println("Ingrese el nombre de usuario a buscar:");

69
String username = Utils.leerCadena();

// Realizar búsqueda secuencial en la lista de usuarios


// UserAccount user = buscarUsuario(username);

if (user != null) {
// Cargar el usuario en memoria para su posterior uso
// usuarioCargado = user;
System.out.println("Usuario cargado en memoria: " +
user.getUsername());
} else {
System.out.println("El usuario no existe en la lista.");
}
}

private static void publishTweet() {


System.out.println("Ingrese el mensaje del tweet:");
String message = Utils.leerCadena();

try {
// Validar la longitud del mensaje
if (message.length() > 140) {
throw new IllegalArgumentException("El mensaje supera los
140 caracteres.");
}

// Publicar el tweet utilizando el usuario cargado en memoria


// usuarioCargado.tweet(new Tweet(...));

System.out.println("Tweet publicado correctamente.");


} catch (IllegalArgumentException e) {
System.out.println("Error al publicar el tweet: " +
e.getMessage());
}
}

private static void sortByEmail() {


// Ordenar la lista de usuarios por email de forma ascendente
Collections.sort(userList,
Comparator.comparing(UserAccount::getEmail));

System.out.println("Usuarios ordenados por email (ascendente):");


for (UserAccount user : userList) {
System.out.println(user);
}
}

private static void sortByUsername() {


// Ordenar la lista de usuarios por nombre de usuario de forma

70
descendente
Collections.sort(userList,
Comparator.comparing(UserAccount::getUsername).reversed());

System.out.println("Usuarios ordenados por nombre de usuario


(descendente):");
for (UserAccount user : userList) {
System.out.println(user);
}
}
}

H
1. Aprovechando el tirón de la última temporada de la serie, HBO quiere sacar al mercado el juego :
The Iron Throne®, en el que cualquier persona podrá conquistar el Trono de Hierro y gobernar los
Sietes Reinos que conforman Poniente.
Para llevar a cabo este proyecto, han contratado al estudio de programación en el que usted está
haciendo prácticas (!!qué suerte, con lo que le gusta la serie!!), y un creativo de la empresa ha
comenzado a describir las especicaciones que tienen en mente:

Los nuevos jugadores deben registrarse en la aplicación antes de comenzar a desarrollar las partidas.
Cada Jugador tiene un alias (que servirá para identicarle), la fecha en la que se registró́, pertenecerá
a alguna de las grandes casas del juego (Arryn, Baratheon, Lannister o Stark) y acumulará un número
de monedas que consigue a través de la colección de capitales de los reinos que domina. Por último,
tendrá una colección de armas que podrá utilizar para conquistar otras capitales (lo que le acercará a
poseer el Trono de Hierro).

En el juego existen cuatro capitales que tienen las siguientes características:

Todas las capitales tienen un número que utilizan como identicador, pertenecen a una familia y una
población que determina su riqueza y defensa.

- La población está formada por un número de agricultores, un número de soldados, un numero de


comerciantes y un número de habitantes menores de edad.

Bastión de Tormentas: Es la capital de Las Tierras de la Tormenta, la residencia principal de la casa


Baratheon.

Son tierras fértiles y fácilmente cultivables. La población inicial de esta ciudad es la siguiente: 50
agricultores, 40 soldados y 10 comerciantes.

Invernalia: Es la capital de El Norte, donde se asienta la fortaleza de la casa Stark. Tiene un gran
cúmulo de soldados, puesto que debe defender al resto de reinos de las amenazas que provengan
de más allá del muro.

La población inicial de esta ciudad es de: 20 agricultores, 70 soldados y 10 co- merciantes.

Nido de Águilas: Es la capital de El Valle de Arryn, residencia de la casa Arryn. Esta ciudad se
caracteriza por tener un comercio y agricultura moderados. Inicialmente, la ciudad cuenta con: 40
agricultores, 20 soldados y 40 comerciantes. Roca Casterly: Es la capital de Las Tierras del Oeste,
asentamiento de la rama principal de la casa Lannister. Son tierras muy ricas en oro, origen del
poder de esta casa.

La población inicial de la ciudad es la siguiente: 20 agricultores, 40 soldados y 40 comerciantes.

Durante el transcurso de la partida, los jugadores irán acumulando monedas en función de las
capitales que controlen. Utilizarán esas monedas para alimentar a su población y para comprar
armas. Dichas armas le servirán para conquistar nuevas capitales que no posean. El objetivo nal de
cada jugador es conquistar las capitales de todos los reinos, haciendo sus vasallos a los habitantes de
cada una de las casas y las ciudades. El jugador que primero llegue a controlar todos los reinos,
tomará su lugar en el Trono de Hierro.

En base a estas especicaciones se solicita que:

(a) Implemente las clases: Capital, BastionDeTormentas, Invernalia, NidoDeAguilas y RocaCasterly.

Añada los atributos que se especican en el enunciado y establezca su visi- bilidad

72
- Las clases Agricultor, Soldado, Comerciante y MenorEdad heredan de la clase Habitante;
suponga que todas ya están correctamente implementadas.
- Suponga que también existe una clase Utilidades que contiene el método public static int
generaIdentificador() que genera el identicador de cada una de las capitales.

Genere los constructores necesarios para inicializar los atributos de las capitales. Deberán ser
acordes con las restricciones de población indicadas anteriormente.

No se permite el uso de arrays, utilice la colección de Java que mejor se adapte al problema y
reutilice todo el código que pueda. Implemente los siguientes métodos:

- En la clase Capital, implemente los métodos necesarios para añadir y eliminar un habitante a
la población (ya sea agricultor, comerciante, soldado o menor de edad).
- Dena, en Capital, el método: public void generarPoblacion().

La implementación de este método depende del tipo concreto de capital. Cada vez que se ejecute
este método, se añadirá un nuevo habitante a la población de la ciudad. Para ello, se generarán dos
números aleatorios (men y tipo) entre 0 y 100. Una vez obtenidos ambos números:

1. Si men < 50 ó no hay ningún habitante menor de edad, se crea un habitante menor de edad.

2. Si men ≥ 50, un habitante menor de edad se transformará en un habitante agricultor, comerciante


o soldado. Para ello, se eliminará un habitante menor de edad (por ejemplo, el primero) y,
dependiendo del valor obtenido en tipo se creará el habitante adecuado.

Cada tipo de capital denirá los valores umbrales de tipo, de tal manera que se mantenga la
proporción de habitantes iniciales. Por ejemplo, para BastionDeTormentas, si 0 ≤ tipo < 50 se gene-
rará un agricultor, si 50 ≤ tipo < 90 se generará un soldado y si 90 ≤ tipo ≤ 100 se generará un nuevo
comerciante.

- Si tiene que añadir habitantes a la población, utilice el/los método/s que ha implementado
anteriormente.

Implemente, en la clase Capital, un método que permita calcular cuántas monedas son necesarias
para alimentar a toda su población.

public int costeAlimentoPoblacion()

Cada habitante de la población tiene unas necesidades alimenticias, por lo que deberá calcular el
coste total reuniendo los costes parciales de cada uno de ellos.

- Suponga que la clase Habitante tiene un método public int costeAlimento() que devuelve la
cantidad de monedas necesarias para alimentar a dicho habitante.

(b) Implemente la clase Jugador.

Añada los atributos que se especican en el enunciado y establezca su visi- bilidad.

- Suponga que la clase Arma y todas las clases del apartado anterior se encuentran
correctamente programadas.

Genere el/los constructor/es, inicializando correctamente los atributos.

73
- Tenga en cuenta que cuando se crea un jugador: tiene 10000 monedas iniciales; en la
colección de capitales de los reinos que domina está la capital asociada a la casa que ha
seleccionado; la colección de armas se encuentra vacía.

No se permite el uso de arrays. Utilice la colección de Java que mejor se adapte al problema.

Implemente el método public void alimentarPoblaciones().

Este método permite a un jugador alimentar todas sus poblaciones. Para ello, se calcularán las
monedas necesarias para proporcionar alimento a todas las capitales que controla y, esa cantidad,
se descontará de las monedas que posee el usuario.

- Implemente y use la excepción NoMonedasException si no hay monedas sucientes para


alimentar a todas las capitales.

Implemente el método: public void generarPoblacionCapitales(). Este método incrementará la


población de todas y cada una de las capitales que controla.

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

class Utilidades {
public static int generaIdentificador() {
Random rand = new Random();
return rand.nextInt(1000); // Genera un identificador
aleatorio entre 0 y 999
}
}

abstract class Habitante {


// Atributos comunes a todos los habitantes
}

class Agricultor extends Habitante {


// Implementación específica para agricultor
}

class Soldado extends Habitante {


// Implementación específica para soldado
}

class Comerciante extends Habitante {


// Implementación específica para comerciante
}

class MenorEdad extends Habitante {


// Implementación específica para menor de edad
}

class Capital {
private int identificador;

74
private String familia;
private int poblacion;
private List<Agricultor> agricultores;
private List<Soldado> soldados;
private List<Comerciante> comerciantes;
private List<MenorEdad> menoresEdad;

public Capital(String familia, int agricultoresInicial, int


soldadosInicial, int comerciantesInicial) {
this.identificador = Utilidades.generaIdentificador();
this.familia = familia;
this.poblacion = agricultoresInicial + soldadosInicial +
comerciantesInicial;
this.agricultores = new ArrayList<>();
this.soldados = new ArrayList<>();
this.comerciantes = new ArrayList<>();
this.menoresEdad = new ArrayList<>();

// Generar población inicial


for (int i = 0; i < agricultoresInicial; i++) {
this.agricultores.add(new Agricultor());
}
for (int i = 0; i < soldadosInicial; i++) {
this.soldados.add(new Soldado());
}
for (int i = 0; i < comerciantesInicial; i++) {
this.comerciantes.add(new Comerciante());
}
}

public void agregarHabitante(Habitante habitante) {


if (habitante instanceof Agricultor) {
this.agricultores.add((Agricultor) habitante);
} else if (habitante instanceof Soldado) {
this.soldados.add((Soldado) habitante);
} else if (habitante instanceof Comerciante) {
this.comerciantes.add((Comerciante) habitante);
} else if (habitante instanceof MenorEdad) {
this.menoresEdad.add((MenorEdad) habitante);
}
this.poblacion++;
}

public void eliminarHabitante(Habitante habitante) {


if (habitante instanceof Agricultor) {
this.agricultores.remove(habitante);
} else if (habitante instanceof Soldado) {
this.soldados.remove(habitante);
} else if (habitante instanceof Comerciante) {
this.comerciantes.remove(habitante);

75
} else if (habitante instanceof MenorEdad) {
this.menoresEdad.remove(habitante);
}
this.poblacion--;
}

public void generarPoblacion() {


Random rand = new Random();
int men = rand.nextInt(100);
int tipo = rand.nextInt(100);

if (men < 50 || this.menoresEdad.isEmpty()) {


agregarHabitante(new MenorEdad());
} else {
eliminarHabitante(this.menoresEdad.get(0));
if (tipo < 50) {
agregarHabitante(new Agricultor());
} else if (tipo < 90) {
agregarHabitante(new Soldado());
} else {
agregarHabitante(new Comerciante());
}
}
}

public int costeAlimentoPoblacion() {


int costeTotal = 0;
for (Agricultor agricultor : this.agricultores) {
costeTotal += agricultor.costeAlimento();
}
for (Soldado soldado : this.soldados) {
costeTotal += soldado.costeAlimento();
}
for (Comerciante comerciante : this.comerciantes) {
costeTotal += comerciante.costeAlimento();
}
for (MenorEdad menorEdad : this.menoresEdad) {
costeTotal += menorEdad.costeAlimento();
}
return costeTotal;
}
}

class BastionDeTormentas extends Capital {


public BastionDeTormentas() {
super("Baratheon", 50, 40, 10);
}
}

class Invernalia extends Capital {

76
public Invernalia() {
super("Stark", 20, 70, 10);
}
}

class NidoDeAguilas extends Capital {


public NidoDeAguilas() {
super("Arryn", 40, 20, 40);
}
}

class RocaCasterly extends Capital {


public RocaCasterly() {
super("Lannister", 20, 40, 40);
}
}

class Arma {
// Implementación de la clase Arma
}

class Jugador {
private String alias;
private String fechaRegistro;
private String casa;
private int monedas;
private List<Capital> capitales;
private List<Arma> armas;

public Jugador(String alias, String fechaRegistro, String casa) {


this.alias = alias;
this.fechaRegistro = fechaRegistro;
this.casa = casa;
this.monedas = 10000;
this.capitales = new ArrayList<>();
this.armas = new ArrayList<>();

// Agregar capital asociada a la casa seleccionada


if (casa.equals("Baratheon")) {
this.capitales.add(new BastionDeTormentas());
} else if (casa.equals("Stark")) {
this.capitales.add(new Invernalia());
} else if (casa.equals("Arryn")) {
this.capitales.add(new NidoDeAguilas());
} else if (casa.equals("Lannister")) {
this.capitales.add(new RocaCasterly());
}
}

77
public void alimentarPoblaciones() throws NoMonedasException {
int costeTotal = 0;
for (Capital capital : this.capitales) {
costeTotal += capital.costeAlimentoPoblacion();
}

if (costeTotal <= this.monedas) {


this.monedas -= costeTotal;
} else {
throw new NoMonedasException("No hay suficientes monedas
para alimentar a todas las capitales.");
}
}

public void generarPoblacionCapitales() {


for (Capital capital : this.capitales) {
capital.generarPoblacion();
}
}
}

class NoMonedasException extends Exception {


public NoMonedasException(String message) {
super(message);
}
}

2. Una de las partes más interesantes de : The Iron Throne®es, precisamente, las armas que los
jugadores podrán utilizar para conquistar las distintas capitales de los reinos que desea. Cada Arma
tiene como características: un identicador único, el precio por el que un usuario puede adquirirla, el
tipo de medio en el que se utiliza (terrestre, aéreo, acuático), la composición de sus proyectiles
(fuego, acero valirio, piedra), las unidades de daño que provoca, el jugador que la atesora y la capital
en la que se encuentra situada.

Actualmente, existen cuatro armas diferentes que el jugador puede comprar:

Espada: arma terrestre, con un precio de 1000 monedas, compuesta por acero valirio y que provoca
un daño de 500 unidades.

Catapulta: arma terrestre, con un precio de 2500 monedas, compuesta por piedra y que provoca un
daño de 1500 unidades.

Carabela: arma acuática, con un precio de 5000 monedas, compuesta por fuego y que provoca un
daño de 3000 unidades.

Dragón: arma aérea, con un precio de 20000 monedas, compuesta por fuego y que provoca un daño
de 5000 unidades.

En base a estas especicaciones se solicita que:

(a) Implemente las clases necesarias para representar los distintos tipos de arma en el videojuego.

78
Añada los atributos que se especican en el enunciado y establezca su visibilidad.

Reutilice todo el código que pueda.

(b) Implemente los constructores de las clases reutilizando al máximo todo el código disponible.

Tenga en cuenta que:

- Existirá un constructor que recibirá como parámetros los necesarios para inicializar el arma,
a excepción del jugador y la capital.
- Existirá un constructor que esté parametrizado con todos los parámetros necesarios
(incluyendo el jugador y la capital).

◦ Lance una excepción si la capital en la que se establece un arma no se encuentra entre las
controladas por el jugador. Suponga que la clase Jugador ya tiene el método public boolean
duenioCapital(Capital c) correctamente implementado.

- Deberá utilizar la clase Date de JAVA como identicador único.

(c) Implemente el método toString de tal manera que se muestren todas las carac- terísticas del
arma. Este método deberá estar disponible para todos los tipos de arma. Reutilice al máximo todo el
código disponible.

(d) Responda a la siguiente pregunta:

¿Deberá modicar el atributo correspondiente a la colección de armas de la clase Jugador (denida en


el ejercicio 1 apartado b) para que contenga distintos tipos de armas? Justique su razonamiento y, si
cree que hay que modicarlos, explique también cómo lo haría.

import java.util.Date;

class Arma {
private String identificador;
private int precio;
private String tipoMedio;
private String composicionProyectiles;
private int unidadesDanio;
private Jugador jugador;
private Capital capital;

public Arma(String identificador, int precio, String tipoMedio, String


composicionProyectiles, int unidadesDanio) {
this.identificador = identificador;
this.precio = precio;
this.tipoMedio = tipoMedio;
this.composicionProyectiles = composicionProyectiles;
this.unidadesDanio = unidadesDanio;
}

public Arma(String identificador, int precio, String tipoMedio, String


composicionProyectiles, int unidadesDanio, Jugador jugador, Capital
capital) throws CapitalNoControladaException {
this(identificador, precio, tipoMedio, composicionProyectiles,

79
unidadesDanio);

if (!jugador.duenioCapital(capital)) {
throw new CapitalNoControladaException("El jugador no controla
la capital en la que se establece el arma.");
}

this.jugador = jugador;
this.capital = capital;
}

public String getIdentificador() {


return identificador;
}

public int getPrecio() {


return precio;
}

public String getTipoMedio() {


return tipoMedio;
}

public String getComposicionProyectiles() {


return composicionProyectiles;
}

public int getUnidadesDanio() {


return unidadesDanio;
}

public Jugador getJugador() {


return jugador;
}

public Capital getCapital() {


return capital;
}

@Override
public String toString() {
return "Arma{" +
"identificador='" + identificador + '\'' +
", precio=" + precio +
", tipoMedio='" + tipoMedio + '\'' +
", composicionProyectiles='" + composicionProyectiles +
'\'' +
", unidadesDanio=" + unidadesDanio +
", jugador=" + jugador.getAlias() +

88
", capital=" + capital.getNombre() +
'}';
}
}

class Espada extends Arma {


public Espada(Jugador jugador, Capital capital) throws
CapitalNoControladaException {
super(new Date().toString(), 1000, "terrestre", "acero valirio",
500, jugador, capital);
}
}

class Catapulta extends Arma {


public Catapulta(Jugador jugador, Capital capital) throws
CapitalNoControladaException {
super(new Date().toString(), 2500, "terrestre", "piedra", 1500,
jugador, capital);
}
}

class Carabela extends Arma {


public Carabela(Jugador jugador, Capital capital) throws
CapitalNoControladaException {
super(new Date().toString(), 5000, "acuático", "fuego", 3000,
jugador, capital);
}
}

class Dragon extends Arma {


public Dragon(Jugador jugador, Capital capital) throws
CapitalNoControladaException {
super(new Date().toString(), 20000, "aéreo", "fuego", 5000,
jugador, capital);
}
}

class CapitalNoControladaException extends Exception {


public CapitalNoControladaException(String message) {
super(message);
}
}

3. A lo largo del juego, los jugadores de : The Iron Throne®deben conocer cuáles son las capitales
más ricas o más poderosas que poseen, para defenderlas o para lanzar ataques desde ellas.

Para ello, en la clase Jugador (especicada en el ejercicio 1 apartado b), deberíamos incluir los
métodos:

public void muestraCapitalesPorRiqueza(List<Capital> capital)

81
public void muestraCapitalesPorPoder(List<Capital> capital)

El cálculo de la riqueza y el poder de una capital se realiza utilizando las siguientes fórmulas:

riquezacapital = numeroagricultores ∗ 100 + numerocomerciantes ∗ 200 podercapital =


numerosoldados ∗ 100 + potenciaarmas

En este ejercicio se le solicitará que implemente métodos en las clases Jugador y Capital, no es
necesario que escriba las clases de nuevo, sólo escriba aquellas líneas o métodos que cambien o
nuevos métodos auxiliares que crea adecuado añadir.

Suponga que la clase Capital ya tiene los métodos public String toString() y public int hashCode()
implementados.

Para facilitar la implementación de las fórmulas, suponga también que la clase Capital ya tiene
implementados correctamente los métodos:

public int getNumeroAgricultores(),public int getNumeroComerciantes(), public int


getNumeroSoldados() y public int getPotenciaArmas().

En base a esta especicación, se le solicita que:

(a) En la clase Capital implemente los métodos public int getRiqueza() y public int getPoder() que
calcule la riqueza y el poder, respectivamente, de una capital, basándose en las fórmulas arriba
expuestas.

(b) Dena como método de ordenación natural, la ordenación de las capitales por riqueza. Para ello,
implemente la interfaz Comparable<Capital> en la clase Capital. Con ese n, además de
añadir/modicar lo que crea necesario en la clase Capital, implemente el método:

public int compareTo(Capital capital2)

Este método deberá comparar, dos capitales teniendo en cuenta la riqueza de cada una de ellas.

Así, el método compareTo devolverá: 0 si ambas capitales tienen la misma riqueza, un entero
negativo si capital2 es más rica, o un entero positivo si capital2 es más pobre.

(c) Implemente el método equals en la clase Capital. Utilice el identicador de cada capital.

public boolean equals(Object o)

(c) Implemente el método muestraCapitalesPorRiqueza solicitado, respetando la signatura


indicada. Dentro de este método, ordene por riqueza la lista de capitales recibida y,
posteriormente, imprímalas por pantalla.

Para ordenar la lista de capitales opte por una de las siguientes opciones: utilice un TreeSet, utilice
algún método de la clase Collections ó implemente algún método de ordenación que conozca.

(e) Implemente el método muestraCapitalesPorPoder solicitado, respetando la sig- natura indicada.


Dentro de este método, ordene por poder la lista de capitales recibida y, posteriormente,
imprímalas por pantalla.

Para la implementación de este método utilice un TreeSet y cree una clase que implemente la
interfaz Comparator<Capital> que posee el método:

82
public int compare(Capital capital1, Capital capital2)

En este método compare ambas capitales mediante su poder. El método devol- verá: 0 si ambas
capitales tienen el mismo poder, un entero negativo si capital2 es más poderosa, o un entero
positivo si capital2 es más débil.

(f) Implemente el método existeCapital de forma que, dada una lista de capitales y una capital,
devuelva cierto si la capital se encuentra en la lista.

public boolean existeCapital (List<Capital> capitales, Capital capital)

Para realizar la búsqueda opte por una de las siguientes opciones: utilice algún método de la clase
Collections ó implemente algún método de búsqueda que conozca. En ningún caso utilice el método
contains de la lista.

a)

public int getRiqueza() {


return getNumeroAgricultores() * 100 + getNumeroComerciantes() * 200;
}

public int getPoder() {


return getNumeroSoldados() * 100 + getPotenciaArmas();
}

b)

public class Capital implements Comparable<Capital> {


// ...

@Override
public int compareTo(Capital capital2) {
return Integer.compare(this.getRiqueza(), capital2.getRiqueza());
}
}

c)
public class Capital {
// ...

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Capital capital = (Capital) o;
return Objects.equals(identificador, capital.identificador);
}
}

d)

83
public void muestraCapitalesPorRiqueza(List<Capital> capitales) {
Collections.sort(capitales);
for (Capital capital : capitales) {
System.out.println(capital);
}
}

e)
import java.util.Comparator;
import java.util.TreeSet;

public void muestraCapitalesPorPoder(List<Capital> capitales) {


TreeSet<Capital> capitalsSet = new TreeSet<>(new
ComparadorCapitalesPorPoder());
capitalsSet.addAll(capitales);

for (Capital capital : capitalsSet) {


System.out.println(capital);
}
}

class ComparadorCapitalesPorPoder implements Comparator<Capital> {


@Override
public int compare(Capital capital1, Capital capital2) {
return Integer.compare(capital1.getPoder(), capital2.getPoder());
}
}

f)
public boolean existeCapital(List<Capital> capitales, Capital capital) {
for (Capital c : capitales) {
if (c.equals(capital)) {
return true;
}
}
return false;
}

84
Respuestas correctas suman 0.5 puntos. Se debe responder a 4 preguntas de las 5 disponibles. Las 4
preguntas a contestar son elegidas libremente por el estudiante.
1) Debemos diseñar un programa para un sistema de dispositivos portables aplicados en
telemedicina que contienen una cantidad de memoria muy limitada (4 KB). Tenemos diferentes
alternativas que hacen diferente uso de la computación y la memoria.

a) La primera de ellas tiene un orden de complejidad computacional O(n) y va a ocupar un espacio


S(1).

b) La segunda de ellas tiene un orden de complejidad computacional O(1) pero lo hace mediante una
técnica denominada para guardar muchos datos en memoria, por lo que ocupa un espacio de S(N2).

c) La tercera y última tiene una complejidad computacional de O(log2(N)) y ocupa un poco más de
espacio en memoria S(O(log2(N))).

Debemos analizar la eficiencia computacional de las mismas y justificar la elección. Teniendo en


cuenta que el programa va a tener un tamaño de datos de entrada de 64 KB.

a) La primera alternativa tiene un orden de complejidad computacional O(n) y ocupa un espacio


constante S(1). Esto significa que la eficiencia de tiempo aumentará linealmente con el tamaño de
los datos de entrada, mientras que el espacio en memoria utilizado será constante e independiente
del tamaño de los datos. Dado que el tamaño de datos de entrada es de 64 KB, la complejidad O(n)
podría resultar poco eficiente en términos de tiempo. Además, el hecho de ocupar un espacio
constante en memoria puede ser beneficioso para conservar la limitada memoria disponible.

b) La segunda alternativa tiene un orden de complejidad computacional O(1), lo que implica que el
tiempo de ejecución es constante e independiente del tamaño de los datos de entrada. Sin embargo,
utiliza una técnica para guardar muchos datos en memoria, lo que ocupa un espacio de S(N^2). Dado
que el tamaño de datos de entrada es de 64 KB, ocupar un espacio de S(N^2) podría resultar muy
ineficiente en términos de memoria. A pesar de tener una complejidad O(1), el alto consumo de
memoria puede ser problemático en un sistema con recursos limitados.

c) La tercera alternativa tiene una complejidad computacional de O(log2(N)) y ocupa un poco más de
espacio en memoria, S(O(log2(N))). La complejidad O(log2(N)) indica que el tiempo de ejecución
aumentará de manera logarítmica con el tamaño de los datos de entrada, lo que significa una
eficiencia bastante buena en términos de tiempo. En cuanto al espacio en memoria, ocupará un
poco más de espacio a medida que aumenta el tamaño de los datos, pero la complejidad logarítmica
sugiere que el aumento no será exponencial. Dado que el programa tiene una cantidad limitada de
memoria (4 KB) y una entrada de datos de 64 KB, una complejidad logarítmica puede ser una opción
más eficiente tanto en términos de tiempo como de espacio.

Teniendo en cuenta la restricción de memoria limitada y la eficiencia tanto en tiempo como en


espacio, la tercera alternativa con una complejidad O(log2(N)) y un espacio en memoria de
S(O(log2(N))) parece ser la opción más adecuada. Aunque ocupa un poco más de espacio en
memoria que la primera alternativa (S(1)), su eficiencia en tiempo mejora significativamente,
especialmente cuando se considera el tamaño de los datos de entrada. La segunda alternativa, a
pesar de tener una complejidad O(1), presenta un consumo de memoria excesivo y no es viable dada
la limitación de memoria. Por lo tanto, la tercera alternativa es la elección preferida en este
escenario.

85
Crear una clase que represente un número complejo que permita, al menos, sumar, restar y
multiplicar números complejos. Proporcionar un constructor por defecto, un constructor de copia
(esto es, un constructor al que se le pasa una instancia de la clase número racional y crea otro
número racional idéntico), y otro que permita indicar los valores de la parte real y de la parte
imaginaria. Usa la clase desde un programa de ejemplo (main).

public class NumeroComplejo {


private double parteReal;
private double parteImaginaria;

public NumeroComplejo() {
parteReal = 0.0;
parteImaginaria = 0.0;
}

public NumeroComplejo(NumeroComplejo otro) {


parteReal = otro.getParteReal();
parteImaginaria = otro.getParteImaginaria();
}

public NumeroComplejo(double real, double imaginaria) {


parteReal = real;
parteImaginaria = imaginaria;
}

public double getParteReal() {


return parteReal;
}

public double getParteImaginaria() {


return parteImaginaria;
}

public NumeroComplejo sumar(NumeroComplejo otro) {


double real = parteReal + otro.getParteReal();
double imaginaria = parteImaginaria + otro.getParteImaginaria();
return new NumeroComplejo(real, imaginaria);
}

public NumeroComplejo restar(NumeroComplejo otro) {


double real = parteReal - otro.getParteReal();
double imaginaria = parteImaginaria - otro.getParteImaginaria();
return new NumeroComplejo(real, imaginaria);
}

public NumeroComplejo multiplicar(NumeroComplejo otro) {


double real = parteReal * otro.getParteReal() - parteImaginaria *
otro.getParteImaginaria();
double imaginaria = parteReal * otro.getParteImaginaria() +
parteImaginaria * otro.getParteReal();

86
return new NumeroComplejo(real, imaginaria);
}

@Override
public String toString() {
return parteReal + " + " + parteImaginaria + "i";
}

public static void main(String[] args) {


NumeroComplejo num1 = new NumeroComplejo(2.0, 3.0);
NumeroComplejo num2 = new NumeroComplejo(1.0, -2.0);

NumeroComplejo suma = num1.sumar(num2);


System.out.println("Suma: " + suma);

NumeroComplejo resta = num1.restar(num2);


System.out.println("Resta: " + resta);

NumeroComplejo multiplicacion = num1.multiplicar(num2);


System.out.println("Multiplicación: " + multiplicacion);
}
}

87
a) (1.0 puntos) Escribir un método estático que busque raíces de una función empleando el
algoritmo de Newton-Raphson para encontrar una raíz de una función. Este método parte de una
estimación inicial de la raíz, x0, que deberá ser un parámetro del método estático y va calculando
aproximaciones sucesivas al valor de la misma utilizando la fórmula:
xi+1=xi-f(xi)/f'(xi)

siendo f(xi) el valor de la función en el punto xi y siendo f'(xi) el valor de la derivada de la función en
el punto xi. Al ser un método iterativo, es necesario tener un criterio para que termine. Puede
establecerse como criterio de terminación que la diferencia entre f(x) y 0 sea menor que 0.001.

Las funciones deberán representarse mediante esta interfaz:

public interface FunctionInterface {

float evaluar (float x);

float evaluarDerivada (float x);

El método estático para hallar las raíces recibirá como argumentos la función y el valor inicial sobre
el cual se comenzará a aplicar la fórmula de Newton-Raphson:

public static float solve(FunctionInterface function, float x0){...}

Nota: se recomienda hacer la parte a y b de este ejercicio conjuntamente.

b) (0.75 puntos) Por seguridad, se debe establecer un número máximo de iteraciones para que el
algoritmo termine aunque no converja a una solución (de lo contrario, al ejecutarlo en el ordenador
éste podría quedarse bloqueado). No deberá aplicarse la fórmula recursiva de Newton-Raphson más
de 1000 veces. En caso de tener que aplicar más de 1000 veces la fórmula recursiva, deberá lanzarse
una excepción que indique que no converge el algoritmo.

c)(0.75 puntos) Crea una implementación de FunctionInterface para funciones del tipo ax2 + bx+c y
ex –bx y busca raíces para dos ecuaciones de este tipo con parámetros a=1, b=2, c=-4 desde el
método main. Las clases que implementen ambas funciones deberán tener un constructor al que se
le pasen sus respectivos parámetros (a, b y c; y b, respectivamente).

public interface FunctionInterface {


float evaluate(float x);
float evaluateDerivative(float x);
}

public class NewtonRaphsonSolver {


public static float solve(FunctionInterface function, float x0) throws
Exception {
int maxIterations = 1000;
float epsilon = 0.001f;
float xi = x0;

for (int i = 0; i < maxIterations; i++) {


float fxi = function.evaluate(xi);
float fxiPrime = function.evaluateDerivative(xi);

88
float xiPlus1 = xi - fxi / fxiPrime;

if (Math.abs(xiPlus1 - xi) < epsilon) {


return xiPlus1;
}

xi = xiPlus1;
}

throw new Exception("The algorithm did not converge.");


}
}

public class QuadraticFunction implements FunctionInterface {


private float a;
private float b;
private float c;

public QuadraticFunction(float a, float b, float c) {


this.a = a;
this.b = b;
this.c = c;
}

public float evaluate(float x) {


return a * x * x + b * x + c;
}

public float evaluateDerivative(float x) {


return 2 * a * x + b;
}
}

public class ExponentialFunction implements FunctionInterface {


private float a;
private float b;

public ExponentialFunction(float a, float b) {


this.a = a;
this.b = b;
}

public float evaluate(float x) {


return (float) (a * Math.exp(x) - b * x);
}

public float evaluateDerivative(float x) {


return (float) (a * Math.exp(x) - b);
}

89
}

public class Main {


public static void main(String[] args) {
// Solving the quadratic function: f(x) = x^2 + 2x - 4
QuadraticFunction quadraticFunction = new QuadraticFunction(1, 2,
-4);
try {
float root = NewtonRaphsonSolver.solve(quadraticFunction,
1.0f);
System.out.println("Root of the quadratic function: " + root);
} catch (Exception e) {
System.out.println("The algorithm did not converge for the
quadratic function.");
}

// Solving the exponential function: f(x) = e^x - 2x


ExponentialFunction exponentialFunction = new
ExponentialFunction(1, 2);
try {
float root = NewtonRaphsonSolver.solve(exponentialFunction,
1.0f);
System.out.println("Root of the exponential function: " +
root);
} catch (Exception e) {
System.out.println("The algorithm did not converge for the
exponential function.");
}
}
}

98
Escribe un programa que lea los datos que ha escrito este otro programa y los muestre por consola.
El programa deberá cerrar adecuadamente los recursos que haya empleado para la lectura de los
datos, cosa que no se hace correctamente en el programa que escribe los datos.
import java.io.*; import java.util.Date;

public class Ejercicio {

static public void main(String[] args) throws IOException {

FileOutputStream fileOutputStream = null, fileOutputStream2 = null;

DataOutputStream dataOutputStream = null;

ObjectOutputStream objectOutputStream = null;

File file1 = new File("C:/a/datos.dat"); File file2 = new File("C:/a/objetos.dat");

fileOutputStream2 = new FileOutputStream(file2); objectOutputStream = new


ObjectOutputStream(fileOutputStream2); for (int i = 0; i < 10; i++) {

objectOutputStream.writeObject(new Date()); }

fileOutputStream = new FileOutputStream(file1); dataOutputStream = new


DataOutputStream(fileOutputStream);

dataOutputStream.writeBoolean(true); dataOutputStream.writeDouble(2.3); for (int i = 0; i < 10; i++)


{

dataOutputStream.writeInt(i); }

for (int i = 0; i < 10; i++) { dataOutputStream.writeFloat(i);

}}

import java.io.*;
import java.util.Date;

public class EjercicioLectura {

public static void main(String[] args) {


FileInputStream fileInputStream = null;
DataInputStream dataInputStream = null;
ObjectInputStream objectInputStream = null;

try {
File file1 = new File("C:/a/datos.dat");
File file2 = new File("C:/a/objetos.dat");

fileInputStream = new FileInputStream(file1);


dataInputStream = new DataInputStream(fileInputStream);

91
boolean booleanValue = dataInputStream.readBoolean();
double doubleValue = dataInputStream.readDouble();

System.out.println("Boolean value: " + booleanValue);


System.out.println("Double value: " + doubleValue);

System.out.println("Int values:");
for (int i = 0; i < 10; i++) {
int intValue = dataInputStream.readInt();
System.out.print(intValue + " ");
}
System.out.println();

System.out.println("Float values:");
for (int i = 0; i < 10; i++) {
float floatValue = dataInputStream.readFloat();
System.out.print(floatValue + " ");
}
System.out.println();

fileInputStream = new FileInputStream(file2);


objectInputStream = new ObjectInputStream(fileInputStream);

System.out.println("Dates:");
for (int i = 0; i < 10; i++) {
Date date = (Date) objectInputStream.readObject();
System.out.println(date);
}

} catch (IOException | ClassNotFoundException e) {


e.printStackTrace();
} finally {
// Cerrar los recursos adecuadamente
try {
if (dataInputStream != null) {
dataInputStream.close();
}
if (objectInputStream != null) {
objectInputStream.close();
}
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

92
Escribe un programa que lea los datos que ha escrito este otro programa y los muestre por consola.
Antes de mostrar los datos por pantalla éstos deberán convertirse al tipo de dato original; es decir,
no se debe leer texto del archivo y escribir texto a la consola. Debe leerse texto del archivo,
convertirse al tipo de dato primitivo correspondiente, y mostrarse el dato en la consola. El programa
deberá cerrar adecuadamente los recursos que haya empleado para la lectura de los datos, cosa que
no se hace correctamente en el programa que escribe los datos.
import java.io.*;

public class Ejemplo2 {

File file = new File("C:/a/datos.txt"); PrintWriter printWriter = null; printWriter = new


PrintWriter(file);

printWriter.println("Hello"); printWriter.println(3.6F);

for (int i = 0; i < 10; i++) { boolean b = (i%2==0); printWriter.println(b);

printWriter.println(true); printWriter.println(5.5);

printWriter.close(); }

import java.io.*;

public class Ejemplo2Lectura {

public static void main(String[] args) {


File file = new File("C:/a/datos.txt");
BufferedReader bufferedReader = null;

try {
FileReader fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);

String line;
while ((line = bufferedReader.readLine()) != null) {
Object value = convertToOriginalType(line);
System.out.println(value);
}

} catch (IOException e) {
e.printStackTrace();
} finally {
// Cerrar el recurso adecuadamente
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {

93
e.printStackTrace();
}
}
}

private static Object convertToOriginalType(String line) {


if (line.equals("true") || line.equals("false")) {
return Boolean.parseBoolean(line);
} else if (line.contains(".")) {
return Float.parseFloat(line);
} else {
try {
return Integer.parseInt(line);
} catch (NumberFormatException e) {
return line;
}
}
}
}

94
En la aplicación de contactos de un móvil con sistema operativo Android, el usuario tie- ne una
Agenda que incluye un array de contactos. Cada Contacto almacena, entre otras características: el
nombre, el apodo, una dirección de email, un número de teléfono y un valor lógico que indica si las
llamadas de este contacto hay que silenciarlas. Además, los contactos pueden clasicarse como:
trabajo, amigo, familia. En la aplicación también se distinguen los contactos favoritos, a los que se
permite añadir un domicilio y una fecha de cumpleaños.
A lo largo de este ejercicio, añada, si lo necesita, los métodos auxiliares que crea convenientes para
estructurar su código correctamente. En base a estas especicaciones se solicita que:

(a) Programe la clase Contacto.

Suponga que ya tiene una clase Email correctamente implementada y que su constructor por
defecto crea un email por defecto consistente.

Deberá contener los siguientes atributos (elija el tipo más adecuado e implemente tipos
enumerados si los necesita): nombre, apodo, dirección de email, número de teléfono, silenciado y
clasicación.

Programe un constructor que inicialize los atributos con valores por defecto (asig- ne los valores por
defecto que considere oportunos y justifíquelos si lo considera necesario).

Programe un constructor que reciba todos los datos necesarios para crear un con- tacto.

Programe getters y setters para los atributos nombre, número de teléfono y clasicación.

Programe el método public void silenciar(). Este método anota que las lla- madas de este contacto
hay que silenciarlas.

Programe un método que permita obtener un String con la representación del contacto.

Tenga en cuenta que, el usuario siempre debe contener un número de teléfono válido, en caso
contrario, se debería lanzar una excepción indi- cándolo. Los números de teléfono válidos comienzan
por 3 y tienen una longitud de 6 números

(b) Programe la clase Agenda.

Deberá contener los siguientes atributos (elija el tipo más adecuado): contactos. Programe un
constructor por defecto para esta clase.

Programe el método public void anadeContacto(Contacto c) que añade un contacto a la agenda.

Programe el método public void eliminaContacto(String n) que elimina un contacto de la agenda si


su nombre coincide con n.

Programe el método public void silenciaContacto(String n) que se utiliza para silenciar al contacto de
la agenda que tiene como nombre n.

Programe el método public String nombreContactos() que devuelve un String con los nombres de
todos los contactos de la agenda.

Para realizar este apartado, puede dar por supuesto que ha implementado correc- tamente la clase
Contacto del apartado anterior.

(c) Programe una clase ContactoFavorito.

Escoja una solución que permita reutilizar el máximo código posible.

95
Añada, a los atributos propios de un Contacto, los atributos propios de Contac- toFavorito: domicilio
y cumpleaños. Para cada uno, use el tipo de dato que crea más adecuado1.

Implemente un constructor que reciba todos los parámetros necesarios para gene- rar un
ContactoFavorito. Reutilice al máximo todo el código disponible.

Implemente el método toString para que se muestren todas las características de cada contacto
favorito. Reutilice al máximo todo el código disponible.

Para realizar este apartado, puede dar por supuesto que ha implementado correc- tamente las
clases Contacto y Agenda de los apartados anteriores.

(d) Desearíamos almacenar contactos del tipo ContactoFavorito en la clase Agenda. Des- criba cómo
podríamos conseguirlo.

Si cree que es necesario realizar algún cambio, indique e implemente las líneas que deberían ser
modicadas (no lo haga sobre la solución que ha dado para Agenda en el apartado anterior).

Si cree que no es necesario hacer cambios, razone detalladamente su respuesta.

(e) Desearíamos guardar la Agenda de manera persistente. Para ayudar a la realización de este
ejercicio se facilita determinado código en su parte nal. Se deben cerrar los recursos
adecuadamente.

¿Qué tipo de flujo de salida has seleccionado?

Según el tipo de ujo de salida seleccionado, represente de forma gráca el formato de dicho chero
especique las modicaciones necesarias a realizar en las clases Agenda, Contacto y ContactoFavorito
respectivamente.

Crea una función auxiliar que recibiendo una agenda y un nombre de chero, guarde los datos de la
agenda en dicho chero. Asume que se puede sobreescribir el chero cada vez que se guarda una
agenda y gestione todas las excepciones mediante el relanzamiento de forma explícita en la
cabecera.

import java.util.ArrayList;
import java.util.List;

enum Clasificacion {
TRABAJO,
AMIGO,
FAMILIA
}

class Contacto {
private String nombre;
private String apodo;
private String direccionEmail;
private String numeroTelefono;
private boolean silenciado;
private Clasificacion clasificacion;

public Contacto() {

96
this.nombre = "";
this.apodo = "";
this.direccionEmail = "";
this.numeroTelefono = "";
this.silenciado = false;
this.clasificacion = null;
}

public Contacto(String nombre, String apodo, String direccionEmail,


String numeroTelefono, Clasificacion clasificacion) {
this.nombre = nombre;
this.apodo = apodo;
this.direccionEmail = direccionEmail;
this.numeroTelefono = numeroTelefono;
this.silenciado = false;
this.clasificacion = clasificacion;
}

public String getNombre() {


return nombre;
}

public void setNombre(String nombre) {


this.nombre = nombre;
}

public String getNumeroTelefono() {


return numeroTelefono;
}

public void setNumeroTelefono(String numeroTelefono) {


if (numeroTelefono.startsWith("3") && numeroTelefono.length() ==
6) {
this.numeroTelefono = numeroTelefono;
} else {
throw new IllegalArgumentException("Número de teléfono no
válido");
}
}

public Clasificacion getClasificacion() {


return clasificacion;
}

public void setClasificacion(Clasificacion clasificacion) {


this.clasificacion = clasificacion;
}

public void silenciar() {

97
this.silenciado = true;
}

@Override
public String toString() {
return "Contacto{" +
"nombre='" + nombre + '\'' +
", apodo='" + apodo + '\'' +
", direccionEmail='" + direccionEmail + '\'' +
", numeroTelefono='" + numeroTelefono + '\'' +
", silenciado=" + silenciado +
", clasificacion=" + clasificacion +
'}';
}
}

class Agenda {
private List<Contacto> contactos;

public Agenda() {
this.contactos = new ArrayList<>();
}

public void anadeContacto(Contacto c) {


contactos.add(c);
}

public void eliminaContacto(String n) {


for (int i = 0; i < contactos.size(); i++) {
if (contactos.get(i).getNombre().equals(n)) {
contactos.remove(i);
break;
}
}
}

public void silenciaContacto(String n) {


for (Contacto contacto : contactos) {
if (contacto.getNombre().equals(n)) {
contacto.silenciar();
break;
}
}
}

public String nombreContactos() {


StringBuilder nombres = new StringBuilder();
for (Contacto contacto : contactos) {
nombres.append(contacto.getNombre()).append(", ");

98
}
return nombres.toString().trim();
}
}

class ContactoFavorito extends Contacto {


private String domicilio;
private String cumpleanios;

public ContactoFavorito() {
super();
this.domicilio = "";
this.cumpleanios = "";
}

public ContactoFavorito(String nombre, String apodo, String


direccionEmail, String numeroTelefono,
Clasificacion clasificacion, String domicilio,
String cumpleanios) {
super(nombre, apodo, direccionEmail, numeroTelefono,
clasificacion);
this.domicilio = domicilio;
this.cumpleanios = cumpleanios;
}

@Override
public String toString() {
return super.toString() +
", domicilio='" + domicilio + '\'' +
", cumpleanios='" + cumpleanios + '\'';
}
}

(d) Para almacenar contactos del tipo ContactoFavorito en la clase Agenda, se puede modificar la
lista de contactos para que pueda contener tanto objetos de tipo Contacto como objetos de tipo
ContactoFavorito. Esto se puede lograr utilizando polimorfismo, ya que la clase ContactoFavorito es
una subclase de Contacto. Por lo tanto, la lista contactos en la clase Agenda puede ser del tipo
List<Contacto>, lo que permite almacenar tanto objetos de Contacto como objetos de
ContactoFavorito.

(e) Para guardar la agenda de manera persistente, se puede utilizar un flujo de salida de tipo
ObjectOutputStream. Este flujo de salida nos permite escribir objetos en un archivo. Para cerrar
adecuadamente los recursos, podemos utilizar un bloque try-with-resources, que se encargará de
cerrar automáticamente los recursos al finalizar su uso. Aquí tienes una función auxiliar que guarda
los datos de la agenda en un archivo:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

99
public class AgendaPersistence {

public static void guardarAgenda(Agenda agenda, String nombreArchivo)


throws IOException {
try (FileOutputStream fileOutputStream = new
FileOutputStream(nombreArchivo);
ObjectOutputStream objectOutputStream = new
ObjectOutputStream(fileOutputStream)) {

objectOutputStream.writeObject(agenda);
}
}
}
El formato del archivo de salida dependerá de la forma en que se escriban los datos en el archivo. En
este caso, se utiliza ObjectOutputStream, que guarda los objetos en un formato binario. Por lo tanto,
el archivo de salida contendrá los datos de la instancia de Agenda y los objetos relacionados en un
formato binario. No es posible representar el formato binario de manera gráfica.

100
EJERCICIOS
EXTRA
Crea una clase Estudiante con los atributos nombre y edad. Luego, crea una lista de estudiantes y
ordénalos por edad de forma ascendente utilizando el método Collections.sort(). Muestra por
pantalla la lista de estudiantes ordenada

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Estudiantes {


private String nombre;
private int edad;
public Estudiantes(String nombre, int edad){
this.nombre=nombre;
this.edad=edad;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public int getEdad() {
return edad;
}
public void setEdad(int edad) {
this.edad = edad;
}

@Override
public String toString() {
return "Nombre: "+nombre+"\nEdad: "+edad+"\n";
}

public static void main(String[] args) {


BufferedReader teclado= new BufferedReader(new
InputStreamReader(System.in));
List<Estudiantes> clase= new ArrayList<>();
int opcion=0;
String nombre;
int edad;
try {
do{
menu();
opcion=Integer.parseInt(teclado.readLine());
switch (opcion){
case 1:
System.out.println("Nombre: ");
nombre= teclado.readLine();
System.out.println("Edad: ");

101
edad=Integer.parseInt(teclado.readLine());
Estudiantes estudiante= new
Estudiantes(nombre,edad);
clase.add(estudiante);
break;
case 2:
Collections.sort(clase,
Comparator.comparing(Estudiantes::getEdad));
System.out.println("Clase: ");
for (Estudiantes estudiantes: clase){
System.out.println(estudiantes);
}
break;
default:
return;
}
}while(opcion!=3);
}catch (IOException e){
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1.Añadir estudiante\n2.Ordenar\nOPCION:");
}
}

102
Supongamos que necesitamos desarrollar un programa en Java para llevar un control de ventas de
una tienda. Cada venta está compuesta por un código de producto, una cantidad vendida y un precio
unitario. Se pide implementar una clase Venta con los siguientes métodos:
- Constructor: que recibe como parámetros el código de producto, la cantidad vendida y el
precio unitario. Métodos getters y setters para los atributos de la venta.
- Método calcularTotal: que devuelve el total de la venta (cantidad vendida x precio unitario).
- Método toString: que devuelve una cadena con los datos de la venta en el formato
"Producto: [código], cantidad: [cantidad], precio unitario: [precio], total: [total]".
Además, se pide implementar una clase Tienda que contenga una lista de ventas y que tenga los
siguientes métodos:
- Constructor: que inicializa la lista de ventas.
- Método agregarVenta: que recibe como parámetros los datos de una venta y la agrega a la
lista.
- Método calcularTotalVentas: que devuelve el total de todas las ventas realizadas en la
tienda.
- Método promedioVentas: que devuelve el promedio de ventas realizadas en la tienda.
- Método ordenarPorPrecio: que ordena la lista de ventas por precio unitario de forma
ascendente.
- Método ordenarPorCodigo: que ordena la lista de ventas por código de producto de forma
ascendente.
- Método toString: que devuelve una cadena con los datos de todas las ventas realizadas en la
tienda en el formato "Venta [i]: Producto: [código], cantidad: [cantidad], precio unitario:
[precio], total: [total]".
public class Venta {
private String codigo;
private int cantidad_venta;
private int precio;
public Venta(String codigo, int cantidad_venta, int precio){
this.codigo=codigo;
this.cantidad_venta=cantidad_venta;
this.precio=precio;
}

public int getPrecio() {


return precio;
}
public int getCantidad_venta() {
return cantidad_venta;
}
public String getCodigo() {
return codigo;
}
public void setPrecio(int precio) {
this.precio = precio;
}
public void setCantidad_venta(int cantidad_venta) {
this.cantidad_venta = cantidad_venta;
}

103
public void setCodigo(String codigo) {
this.codigo = codigo;
}
@Override
public String toString() {
return "Codigo: "+codigo+"Cantidad venta:
"+cantidad_venta+"Cantidad unidad: "+precio;
}
public static int cantidadTotal(int cantidad_unida, int
cantidad_venta){
return cantidad_unida*cantidad_venta;
}
}
import java.util.Comparator;
import java.util.List;

public class Tienda {


private List<Venta> ventas;
public Tienda (List<Venta>ventas){
this.ventas=ventas;
}
public static Venta agregar (String codigo, int cantidad_venta, int
cantidad_unida){
Venta venta= new Venta(codigo,cantidad_venta,cantidad_unida);
return venta;
}
public static int calcularTotalVentas(List<Venta> ventas){
int num= ventas.size();
return num;
}
public static int promedioVentas(List<Venta>ventas){
int promedio=0;
int suma=0;
for (int i=0; i<ventas.size();i++){
suma+=ventas.get(i).getPrecio();
}
return suma/ventas.size();
}
public static List<Venta> ordenarPorPrecio(List<Venta>ventas){
ventas.sort(Comparator.comparing(Venta::getPrecio));
return ventas;
}
public static List<Venta> ordenarPorCodigo(List<Venta>ventas){
ventas.sort(Comparator.comparing(Venta::getCodigo));
return ventas;
}
}

104
Supongamos que tenemos un conjunto de figuras geométricas (círculos, cuadrados y triángulos)
representadas por objetos de sus respectivas clases: Circulo, Cuadrado y Triangulo. Se pide
implementar una clase ColeccionFiguras que contenga un arreglo de figuras y que tenga los
siguientes métodos:
• Constructor: que recibe como parámetro el tamaño del arreglo.
• Método agregarFigura: que recibe como parámetro una figura y la agrega al arreglo.
• Método areaTotal: que devuelve el área total de todas las figuras en la colección.
• Método promedioPerimetro: que devuelve el promedio de los perímetros de todas las figuras en la
colección.
• Método toString: que devuelve una cadena con los datos de todas las figuras en la colección.
• Método toCSV: que devuelve una cadena con los datos de todas las figuras en la colección.

import java.util.ArrayList;
import java.util.List;

public class ColeccionFiguras {


private List<Figura> figuras;

public ColeccionFiguras() {
figuras = new ArrayList<>();
}

public void agregarFigura(Figura figura) {


figuras.add(figura);
}

public double areaTotal() {


double areaTotal = 0;
for (Figura figura : figuras) {
areaTotal += figura.calcularArea();
}
return areaTotal;
}

public double promedioPerimetro() {


double perimetroTotal = 0;
for (Figura figura : figuras) {
perimetroTotal += figura.calcularPerimetro();
}
return perimetroTotal / figuras.size();
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Figura figura : figuras) {
sb.append(figura.toString()).append("\n");
}
return sb.toString();
}

public String toCSV() {

155
StringBuilder sb = new StringBuilder();
for (Figura figura : figuras) {
sb.append(figura.toCSV()).append("\n");
}
return sb.toString();
}

public abstract class Figura {


private double area;
private double perimetro;
//El hecho de que los métodos "calcularArea" y "calcularPerimetro"
// sean declarados como abstractos implica que no se les proporciona
// una implementación en la clase actual y que su implementación
// ser proporcionada en las subclases que extienden la clase actual.
public abstract double calcularArea();
public abstract double calcularPerimetro();
public double getArea() {
return area;
}
public void setArea(double area) {
this.area = area;
}
public double getPerimetro() {
return perimetro;
}
public void setPerimetro(double perimetro) {
this.perimetro = perimetro;
}
}
public class Triangulo extends Figura{
private double lado1;
private double lado2;
private double lado3;
private double altura;
public Triangulo(double lado1, double lado2, double lado3,
double altura) {
this.lado1 = lado1;
this.lado2 = lado2;
this.lado3 = lado3;
this.altura = altura;
calcularArea();
calcularPerimetro();
}
public double getLado1() {
return lado1;
}
public void setLado1(double lado1) {
this.lado1 = lado1;
calcularArea();
calcularPerimetro();
}
public double getLado2() {
return lado2;

106
}
public void setLado2(double lado2) {
this.lado2 = lado2;
calcularArea();
calcularPerimetro();
}
public double getLado3() {
return lado3;
}

public void setLado3(double lado3) {


this.lado3 = lado3;
calcularArea();
calcularPerimetro();
}
public double getAltura() {
return altura;
}
public void setAltura(double altura) {
this.altura = altura;
calcularArea();
calcularPerimetro();
}
@Override
public double calcularArea() {
setArea((lado1 * altura) / 2);
return getArea();
}
@Override
public double calcularPerimetro() {
setPerimetro(lado1 + lado2 + lado3);
return getPerimetro();
}
}
public class Cuadrado extends Figura {
private double lado;
public Cuadrado(double lado) {
this.lado = lado;
calcularArea();
calcularPerimetro();
}
public double getLado() {
return lado;
}
public void setLado(double lado) {
this.lado = lado;
calcularArea();
calcularPerimetro();
}
@Override
public double calcularArea() {
setArea(lado * lado);
return getArea();
}
@Override

107
public double calcularPerimetro() {
setPerimetro(4 * lado);
return getPerimetro();
}
}
public class Circulo extends Figura {
private double radio;

public Circulo(double radio) {


this.radio = radio;
calcularArea();
calcularPerimetro();
}
public double getRadio() {
return radio;
}
public void setRadio(double radio) {
this.radio = radio;
calcularArea();
calcularPerimetro();
}
@Override
public double calcularArea() {
setArea(Math.PI * Math.pow(radio, 2));
return getArea();
}
@Override
public double calcularPerimetro() {
setPerimetro(2 * Math.PI * radio);
return getPerimetro();
}
}

108
Supongamos que se desea desarrollar una aplicación para una biblioteca que permita llevar el
control de los préstamos de libros. Se pide implementar las siguientes clases:
• Una clase Libro que contenga los siguientes atributos: Título del libro (String), Autor del libro
(String), ISBN del libro (String), Cantidad de ejemplares del libro (int).
• Una clase Usuario que contenga los siguientes atributos: Nombre del usuario (String) y DNI del
usuario (String).
• Una clase Prestamo que contenga los siguientes atributos: Libro prestado (objeto de la clase Libro),
Usuario que ha solicitado el préstamo (objeto de la clase Usuario), Fecha de inicio del préstamo
(objeto de la clase LocalDate), Fecha de fin del préstamo (objeto de la clase LocalDate).
• Una clase Biblioteca que contenga los siguientes métodos:
o Método agregarLibro: que recibe como parámetro un objeto de la clase Libro y lo agrega a una
lista de libros disponibles.
o Método agregarUsuario: que recibe como parámetro un objeto de la clase Usuario y lo agrega a
una lista de usuarios registrados en la biblioteca.
o Método prestarLibro: que recibe como parámetro el ISBN de un libro y el DNI de un usuario y, si el
libro está disponible y el usuario está registrado en la biblioteca, crea un objeto de la clase Prestamo
y lo agrega a una lista de préstamos realizados.
o Método devolverLibro: que recibe como parámetro el ISBN de un libro y, si existe un préstamo
asociado al libro y aún no ha vencido la fecha de devolución, actualiza la cantidad de ejemplares
disponibles y elimina el préstamo de la lista de préstamos realizados.
o Método listarLibrosDisponibles: que devuelve una cadena con el título, autor, ISBN y cantidad de
ejemplares disponibles de todos los libros disponibles en la biblioteca.
o Método listarUsuariosRegistrados: que devuelve una cadena con el nombre y DNI de todos los
usuarios registrados en la biblioteca.
o Método listarPrestamos: que devuelve una cadena con el título, autor, ISBN, nombre y DNI de los
usuarios y fechas de inicio y fin de todos los préstamos realizados en la biblioteca.
oMétodo para ordenar alfabeticamente los libros

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Biblioteca {


private List<Libro> librosDisponibles;
private List<Usuario> usuariosRegistrados;
private List<Prestamo> prestamosRealizados;

public Biblioteca() {
librosDisponibles = new ArrayList<>();
usuariosRegistrados = new ArrayList<>();
prestamosRealizados = new ArrayList<>();
}

public void agregarLibro(Libro libro) {


librosDisponibles.add(libro);
}

public void agregarUsuario(Usuario usuario) {


usuariosRegistrados.add(usuario);

109
}

public void prestarLibro(String isbn, String dni, LocalDate fechaInicio,


LocalDate fechaFin) {
Libro libro = buscarLibroPorISBN(isbn);
Usuario usuario = buscarUsuarioPorDNI(dni);

if (libro != null && usuario != null && libro.getCantidadEjemplares() > 0)


{
Prestamo prestamo = new Prestamo(libro, usuario, fechaInicio,
fechaFin);
prestamosRealizados.add(prestamo);
libro.setCantidadEjemplares(libro.getCantidadEjemplares() - 1);
}
}

public void devolverLibro(String isbn) {


Prestamo prestamo = buscarPrestamoPorISBN(isbn);

if (prestamo != null && !prestamo.getFechaFin().isBefore(LocalDate.now()))


{
Libro libro = prestamo.getLibroPrestado();
libro.setCantidadEjemplares(libro.getCantidadEjemplares() + 1);
prestamosRealizados.remove(prestamo);
}
}

public String listarLibrosDisponibles() {


StringBuilder sb = new StringBuilder();
for (Libro libro : librosDisponibles) {
sb.append("Título: ").append(libro.getTitulo())
.append(", Autor: ").append(libro.getAutor())
.append(", ISBN: ").append(libro.getIsbn())
.append(", Ejemplares Disponibles:
").append(libro.getCantidadEjemplares())
.append("\n");
}
return sb.toString();
}

public String listarUsuariosRegistrados() {


StringBuilder sb = new StringBuilder();
for (Usuario usuario : usuariosRegistrados) {
sb.append("Nombre: ").append(usuario.getNombre())
.append(", DNI: ").append(usuario.getDni())
.append("\n");
}
return sb.toString();
}

public String listarPrestamos() {


StringBuilder sb = new StringBuilder();
for (Prestamo prestamo : prestamosRealizados) {
Libro libro = prestamo.getLibroPrestado();

10
Usuario usuario = prestamo.getUsuario();
sb.append("Título: ").append(libro.getTitulo())
.append(", Autor: ").append(libro.getAutor())
.append(", ISBN: ").append(libro.getIsbn())
.append(", Nombre Usuario: ").append(usuario.getNombre())
.append(", DNI Usuario: ").append(usuario.getDni())
.append(", Fecha Inicio: ").append(prestamo.getFechaInicio())
.append(", Fecha Fin: ").append(prestamo.getFechaFin())
.append("\n");
}
return sb.toString();
}

public void ordenarLibrosAlfabeticamente() {


librosDisponibles.sort(Comparator.comparing(Libro::getTitulo));
}

private Libro buscarLibroPorISBN(String isbn) {


for (Libro libro : librosDisponibles) {
if (libro.getIsbn().equals(isbn)) {
return libro;
}
}
return null;
}

private Usuario buscarUsuarioPorDNI(String dni) {


for (Usuario usuario : usuariosRegistrados) {
if (usuario.getDni().equals(dni)) {
return usuario;
}
}
return null;
}

private Prestamo buscarPrestamoPorISBN(String isbn) {


for (Prestamo prestamo : prestamosRealizados) {
if (prestamo.getLibroPrestado().getIsbn().equals(isbn)) {
return prestamo;
}
}
return null;
}
}
public class Libro {
private String titulo;
private String autor;
private String isbn;
private int cantidadEjemplares;

public Libro(String titulo, String autor, String isbn, int cantidadEjemplares)


{
this.titulo = titulo;
this.autor = autor;

111
this.isbn = isbn;
this.cantidadEjemplares = cantidadEjemplares;
}

public String getTitulo() {


return titulo;
}

public String getAutor() {


return autor;
}

public String getIsbn() {


return isbn;
}

public int getCantidadEjemplares() {


return cantidadEjemplares;
}

public void setCantidadEjemplares(int cantidadEjemplares) {


this.cantidadEjemplares = cantidadEjemplares;
}
}

import java.time.LocalDate;

public class Prestamo {


private Libro libroPrestado;
private Usuario usuario;
private LocalDate fechaInicio;
private LocalDate fechaFin;

public Prestamo(Libro libroPrestado, Usuario usuario, LocalDate fechaInicio,


LocalDate fechaFin) {
this.libroPrestado = libroPrestado;
this.usuario = usuario;
this.fechaInicio = fechaInicio;
this.fechaFin = fechaFin;
}

public Libro getLibroPrestado() {


return libroPrestado;
}

public Usuario getUsuario() {


return usuario;
}

public LocalDate getFechaInicio() {


return fechaInicio;
}

112
public LocalDate getFechaFin() {
return fechaFin;
}
}
public class Usuario {
private String nombre;
private String dni;

public Usuario(String nombre, String dni) {


this.nombre = nombre;
this.dni = dni;
}

public String getNombre() {


return nombre;
}

public String getDni() {


return dni;
}
}

113
Se te pide que implementes una clase en Java llamada CardDeck que represente una baraja de
cartas. La clase debe tener los siguientes métodos públicos:
• void shuffle(): método que mezcla las cartas de la baraja.
• Card drawCard(): método que devuelve la carta en la parte superior de la baraja y la elimina de la
misma.
• void returnCard(Card card): método que devuelve una carta a la parte inferior de la baraja. La clase
Card ya está definida y tiene los siguientes atributos:
• String suit: palo de la carta (por ejemplo, "spades", "hearts", "diamonds" o "clubs").
• int rank: valor de la carta (del 1 al 13).

public class Card {


private String suit;
private int rank;

public Card(String suit, int rank) {


this.suit = suit;
this.rank = rank;
}
public String getSuit() {
return suit;
}
public int getRank() {
return rank;
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CardDeck {


private List<Card> deck;

public CardDeck() {
initializeDeck();
}

private void initializeDeck() {


deck = new ArrayList<>();
String[] suits = {"spades", "hearts", "diamonds", "clubs"};

for (String suit : suits) {


for (int rank = 1; rank <= 13; rank++) {
Card card = new Card(suit, rank);
deck.add(card);
}
}
}

public void shuffle() {


Collections.shuffle(deck);
}

114
public Card drawCard() {
if (deck.isEmpty()) {
return null; // Si la baraja está vacía, no se puede extraer ninguna
carta
}

return deck.remove(0); // Se extrae y retorna la carta en la parte


superior de la baraja
}

public void returnCard(Card card) {


deck.add(card); // Se agrega la carta al final de la baraja
}
}

115
Supongamos que ahora se te pide que implementes un programa en Java para una tienda online que
venda productos. Cada producto tendrá un código único, nombre, descripción, precio y cantidad
disponible en inventario. Además, deberás implementar una clase CarritoDeCompras que permita a
los clientes añadir productos a su carrito y hacer la compra final. La clase Producto tiene los
siguientes atributos:
• int codigo: código único del producto.
• String nombre: nombre del producto.
• String descripcion: descripción del producto.
• double precio: precio del producto.
• int cantidad: cantidad disponible en inventario del producto.
La clase TiendaOnline tiene los siguientes métodos públicos:
• void agregarProducto(Producto producto): método que agrega un nuevo producto al inventario de
la tienda.
• void eliminarProducto(int codigo): método que elimina un producto del inventario de la tienda.
• Producto buscarProducto(int codigo): método que busca un producto por su código y devuelve un
objeto Producto.
• void imprimirInventario(): método que imprime en consola el inventario actual de la tienda. La
clase
CarritoDeCompras tiene los siguientes métodos públicos:
• void agregarProducto(Producto producto, int cantidad): método que añade una cantidad
determinada de un producto al carrito de compras del cliente.
• void eliminarProducto(Producto producto, int cantidad): método que elimina una cantidad
determinada de un producto del carrito de compras del cliente.
• double calcularTotal(): método que calcula el total de la compra en base a los productos y
cantidades en el carrito de compras.
• void imprimirCarrito(): método que imprime en consola los productos y cantidades en el carrito de
compras del cliente. Se te pide también implementar una clase Descuento que actúe como gestor de
la tienda y permita aplicar descuentos en base a ciertos criterios.
La clase Descuento tiene los siguientes métodos públicos:
• void establecerDescuentoPorCantidad(int cantidad, double descuento): método que establece un
descuento en porcentaje para los productos que se compren en una cantidad determinada.
• void establecerDescuentoPorPrecio(double precio, double descuento): método que establece un
descuento en porcentaje para los productos que tengan un precio superior a un valor determinado.
• double calcularDescuento(Producto producto, int cantidad): método que calcula el descuento a
aplicar a un producto en base a la cantidad que se vaya a comprar.
• double calcularDescuento(Producto producto): método que calcula el descuento a aplicar a un
producto en base a su precio.
import java.util.HashMap;
import java.util.Map;

public class TiendaOnline {


private Map<Integer, Producto> inventario;

public TiendaOnline() {
inventario = new HashMap<>();
}

116
public void agregarProducto(Producto producto) {
inventario.put(producto.getCodigo(), producto);
}

public void eliminarProducto(int codigo) {


inventario.remove(codigo);
}

public Producto buscarProducto(int codigo) {


return inventario.get(codigo);
}

public void imprimirInventario() {


System.out.println("Inventario de la tienda:");
for (Producto producto : inventario.values()) {
System.out.println(producto);
}
}
}

public class Producto {


private int codigo;
private String nombre;
private String descripcion;
private double precio;
private int cantidad;

public Producto(int codigo, String nombre, String descripcion, double


precio, int cantidad) {
this.codigo = codigo;
this.nombre = nombre;
this.descripcion = descripcion;
this.precio = precio;
this.cantidad = cantidad;
}

public int getCodigo() {


return codigo;
}

public String getNombre() {


return nombre;
}

public String getDescripcion() {


return descripcion;
}

public double getPrecio() {

117
return precio;
}

public int getCantidad() {


return cantidad;
}

@Override
public String toString() {
return "Código: " + codigo + ", Nombre: " + nombre + ", Precio: "
+ precio + ", Cantidad: " + cantidad;
}
}
import java.util.HashMap;
import java.util.Map;

public class Descuento {


private Map<Integer, Double> descuentoPorCantidad;
private Map<Double, Double> descuentoPorPrecio;

public Descuento() {
descuentoPorCantidad = new HashMap<>();
descuentoPorPrecio = new HashMap<>();
}

public void establecerDescuentoPorCantidad(int cantidad, double


descuento) {
descuentoPorCantidad.put(cantidad, descuento);
}

public void establecerDescuentoPorPrecio(double precio, double


descuento) {
descuentoPorPrecio.put(precio, descuento);
}

public double calcularDescuento(Producto producto, int cantidad) {


double descuento = 0.0;
for (Map.Entry<Integer, Double> entry :
descuentoPorCantidad.entrySet()) {
int cantidadLimite = entry.getKey();
double descuentoPorcentaje = entry.getValue();
if (cantidad >= cantidadLimite) {
descuento = Math.max(descuento, descuentoPorcentaje);
}
}
return descuento;
}

public double calcularDescuento(Producto producto) {

118
double descuento = 0.0;
for (Map.Entry<Double, Double> entry :
descuentoPorPrecio.entrySet()) {
double precioLimite = entry.getKey();
double descuentoPorcentaje = entry.getValue();
if (producto.getPrecio() > precioLimite) {
descuento = Math.max(descuento, descuentoPorcentaje);
}
}
return descuento;
}
}

import java.util.HashMap;
import java.util.Map;

public class CarritoDeCompras {


private Map<Producto, Integer> carrito;

public CarritoDeCompras() {
carrito = new HashMap<>();
}
public void agregarProducto(Producto producto, int cantidad) {
int cantidadActual = carrito.getOrDefault(producto, 0);
carrito.put(producto, cantidadActual + cantidad);
}
public void eliminarProducto(Producto producto, int cantidad) {
int cantidadActual = carrito.getOrDefault(producto, 0);
int nuevaCantidad = Math.max(cantidadActual - cantidad, 0);
if (nuevaCantidad > 0) {
carrito.put(producto, nuevaCantidad);
} else {
carrito.remove(producto);
}
}
public double calcularTotal() {
double total = 0.0;
for (Map.Entry<Producto, Integer> entry : carrito.entrySet()) {
Producto producto = entry.getKey();
int cantidad = entry.getValue();
total += producto.getPrecio() * cantidad;
}
return total;
}

public void imprimirCarrito() {


System.out.println("Carrito de compras:");
for (Map.Entry<Producto, Integer> entry : carrito.entrySet()) {
Producto producto = entry.getKey();
int cantidad = entry.getValue();

119
System.out.println(producto + " - Cantidad: " + cantidad);
} } }
Crearemos una supeclase llamada Electrodomestico con las siguientes características:
. Sus atributos son precio base, color, consumo energético (letras entre A y F) y peso. Indica que se
podrán heredar.
. Por defecto, el color sera blanco, el consumo energético sera F, el precioBase es de 100 € y el peso
de 5 kg. Usa constantes para ello.
. Los colores disponibles son blanco, negro, rojo, azul y gris. No importa si el nombre esta en
mayúsculas o en minúsculas.
Los constructores que se implementaran serán :
- Un constructor por defecto.
- Un constructor con el precio y peso. El resto por defecto.
- Un constructor con todos los atributos.
. Los métodos que implementara serán:
- Métodos get de todos los atributos.
- comprobarConsumoEnergetico(char letra): comprueba que la letra es correcta, sino es
correcta usara la letra por defecto. Se invocara al crear el objeto y no sera visible.
- comprobarColor(String color): comprueba que el color es correcto, sino lo es usa el color por
defecto. Se invocara al crear el objeto y no sera visible.
- precioFinal(): según el consumo energético, aumentara su precio, y según su tamaño,
también.

Crearemos una subclase llamada Lavadora con las siguientes características:


. Su atributo es carga, ademas de los atributos heredados.
. Por defecto, la carga es de 5 kg. Usa una constante para ello. Los constructores que se
implementaran serán:
- Un constructor por defecto.
- Un constructor con el precio y peso. El resto por defecto.
- Un constructor con la carga y el resto de atributos heredados.Recuerda que debes llamar al
constructor de la clase padre.
. Los métodos que se implementara serán:
- Método get de carga.
- precioFinal():, si tiene una carga mayor de 30 kg, aumentara el precio 50 €, sino es así no se
incrementara el precio. Llama al método padre y añade el código necesario. Recuerda que
las condiciones que hemos visto en la clase Electrodomestico también deben afectar al
precio.
Crearemos una subclase llamada Television con las siguientes características:
. Sus atributos son resolución (en pulgadas) y sintonizador TDT (booleano), ademas de los atributos
heredados.
. Por defecto, la resolución sera de 20 pulgadas y el sintonizador sera false.
. Los constructores que se implementaran serán:
- Un constructor por defecto.
- Un constructor con el precio y peso. El resto por defecto.
- Un constructor con la resolución, sintonizador TDT y el resto de atributos heredados.
Recuerda que debes llamar al constructor de la clase padre.
. Los métodos que se implementara serán:
- Método get de resolución y sintonizador TDT.

120
- precioFinal(): si tiene una resolución mayor de 40 pulgadas, se incrementara el precio un
30% y si tiene un sintonizador TDT incorporado, aumentara 50 €. Recuerda que las
condiciones que hemos visto en la clase Electrodomestico también deben afectar al precio.
Ahora crea una clase ejecutable que realice lo siguiente:
. Crea un array de Electrodomesticos de 10 posiciones.
. Asigna a cada posición un objeto de las clases anteriores con los valores que desees. Ahora, recorre
este array y ejecuta el método precioFinal().
. Deberás mostrar el precio de cada clase, es decir, el precio de todas las televisiones por un lado, el
de las lavadoras por otro y la suma de los Electrodomesticos (puedes crear objetos
Electrodomestico, pero recuerda que Television y Lavadora también son electrodomésticos).
Recuerda el uso operador instanceof.

Por ejemplo, si tenemos un Electrodomestico con un precio final de 300, una lavadora de 200 y una
televisión de 500, el resultado final sera de 1000 (300+200+500) para electrodomésticos, 200 para
lavadora y 500 para televisión.

class Electrodomestico {
protected double precioBase;
protected String color;
protected char consumoEnergetico;
protected double peso;

protected static final String COLOR_DEFECTO = "blanco";


protected static final char CONSUMO_ENERGETICO_DEFECTO = 'F';
protected static final double PRECIO_BASE_DEFECTO = 100.0;
protected static final double PESO_DEFECTO = 5.0;

protected static final String[] COLORES_DISPONIBLES = {"blanco",


"negro", "rojo", "azul", "gris"};

public Electrodomestico() {
this(PRECIO_BASE_DEFECTO, PESO_DEFECTO, COLOR_DEFECTO,
CONSUMO_ENERGETICO_DEFECTO);
}

public Electrodomestico(double precioBase, double peso) {


this(precioBase, peso, COLOR_DEFECTO, CONSUMO_ENERGETICO_DEFECTO);
}

public Electrodomestico(double precioBase, double peso, String color,


char consumoEnergetico) {
this.precioBase = precioBase;
this.peso = peso;
this.color = comprobarColor(color);
this.consumoEnergetico =
comprobarConsumoEnergetico(consumoEnergetico);
}

public double getPrecioBase() {

121
return precioBase;
}

public String getColor() {


return color;
}

public char getConsumoEnergetico() {


return consumoEnergetico;
}

public double getPeso() {


return peso;
}

private char comprobarConsumoEnergetico(char letra) {


char consumo = Character.toUpperCase(letra);
if (consumo >= 'A' && consumo <= 'F') {
return consumo;
}
return CONSUMO_ENERGETICO_DEFECTO;
}

private String comprobarColor(String color) {


String colorLowerCase = color.toLowerCase();
for (String colorDisponible : COLORES_DISPONIBLES) {
if (colorDisponible.equals(colorLowerCase)) {
return colorDisponible;
}
}
return COLOR_DEFECTO;
}

public double precioFinal() {


double precioFinal = precioBase;

switch (consumoEnergetico) {
case 'A':
precioFinal += 100;
break;
case 'B':
precioFinal += 80;
break;
case 'C':
precioFinal += 60;
break;
case 'D':
precioFinal += 50;
break;

122
case 'E':
precioFinal += 30;
break;
case 'F':
precioFinal += 10;
break;
}

if (peso >= 0 && peso < 20) {


precioFinal += 10;
} else if (peso >= 20 && peso < 50) {
precioFinal += 50;
} else if (peso >= 50 && peso < 80) {
precioFinal += 80;
} else if (peso >= 80) {
precioFinal += 100;
}

return precioFinal;
}
}
class Lavadora extends Electrodomestico {
private static final double CARGA_DEFECTO = 5.0;
private double carga;

public Lavadora() {
this(PRECIO_BASE_DEFECTO, PESO_DEFECTO, COLOR_DEFECTO,
CONSUMO_ENERGETICO_DEFECTO, CARGA_DEFECTO);
}

public Lavadora(double precioBase, double peso) {


this(precioBase, peso, COLOR_DEFECTO, CONSUMO_ENERGETICO_DEFECTO,
CARGA_DEFECTO);
}

public Lavadora(double precioBase, double peso, String color, char


consumoEnergetico, double carga) {
super(precioBase, peso, color, consumoEnergetico);
this.carga = carga;
}

public double getCarga() {


return carga;
}

@Override
public double precioFinal() {
double precioFinal = super.precioFinal();

122
if (carga > 30) {
precioFinal += 50;
}

return precioFinal;
}
}
class Television extends Electrodomestico {
private static final double RESOLUCION_DEFECTO = 20.0;
private static final boolean SINTONIZADOR_TDT_DEFECTO = false;

private double resolucion;


private boolean sintonizadorTDT;

public Television() {
this(PRECIO_BASE_DEFECTO, PESO_DEFECTO, COLOR_DEFECTO,
CONSUMO_ENERGETICO_DEFECTO, RESOLUCION_DEFECTO, SINTONIZADOR_TDT_DEFECTO);
}

public Television(double precioBase, double peso) {


this(precioBase, peso, COLOR_DEFECTO, CONSUMO_ENERGETICO_DEFECTO,
RESOLUCION_DEFECTO, SINTONIZADOR_TDT_DEFECTO);
}

public Television(double precioBase, double peso, String color, char


consumoEnergetico, double resolucion, boolean sintonizadorTDT) {
super(precioBase, peso, color, consumoEnergetico);
this.resolucion = resolucion;
this.sintonizadorTDT = sintonizadorTDT;
}

public double getResolucion() {


return resolucion;
}

public boolean hasSintonizadorTDT() {


return sintonizadorTDT;
}

@Override
public double precioFinal() {
double precioFinal = super.precioFinal();

if (resolucion > 40) {


precioFinal *= 1.3;
}

if (sintonizadorTDT) {
precioFinal += 50;

123
}

return precioFinal;
}
}

public class Ejecutable {


public static void main(String[] args) {
Electrodomestico[] electrodomesticos = new Electrodomestico[10];

electrodomesticos[0] = new Lavadora(200, 30);


electrodomesticos[1] = new Television(500, 10);
electrodomesticos[2] = new Electrodomestico(150, 20, "rojo", 'C');
electrodomesticos[3] = new Lavadora(300, 25, "gris", 'D', 15);
electrodomesticos[4] = new Television(400, 35, "negro", 'E', 45,
true);
electrodomesticos[5] = new Electrodomestico(180, 15, "azul", 'B');
electrodomesticos[6] = new Lavadora(250, 40);
electrodomesticos[7] = new Television(550, 50);
electrodomesticos[8] = new Electrodomestico(120, 10, "blanco",
'A');
electrodomesticos[9] = new Lavadora(280, 50, "rojo", 'F', 35);

double sumaElectrodomesticos = 0;
double sumaLavadoras = 0;
double sumaTelevisores = 0;

for (Electrodomestico electrodomestico : electrodomesticos) {


if (electrodomestico instanceof Lavadora) {
sumaLavadoras += electrodomestico.precioFinal();
} else if (electrodomestico instanceof Television) {
sumaTelevisores += electrodomestico.precioFinal();
}

sumaElectrodomesticos += electrodomestico.precioFinal();
}

System.out.println("Precio total de Electrodomesticos: " +


sumaElectrodomesticos);
System.out.println("Precio total de Lavadoras: " + sumaLavadoras);
System.out.println("Precio total de Televisores: " +
sumaTelevisores);
}
}

124
Crearemos una clase llamada SERIE con las siguientes características:
. Sus atributos son titulo, numero de temporadas, entregado, genero y creador.
. Por defecto, el numero de temporadas es de 3 temporadas y entregado false. El resto de atributos
serán valores por defecto según el tipo del atributo.
. Los constructores que se implementaran serán:
- Un constructor por defecto.
- Un constructor con el titulo y creador. El resto por defecto.
- Un constructor con todos los atributos, excepto de entregado.
. Los métodos que se implementara serán:
- Métodos get de todos los atributos, excepto de entregado.
- Métodos set de todos los atributos, excepto de entregado.
- Sobrescribe los métodos toString.
Crearemos una clase Videojuego con las siguientes características:
. Sus atributos son titulo, horas estimadas, entregado, genero y compañia.
. Por defecto, las horas estimadas serán de 10 horas y entregado false. El resto de atributos serán
valores por defecto según el tipo del atributo.
. Los constructores que se implementaran serán:
- Un constructor por defecto.
- Un constructor con el titulo y horas estimadas. El resto por defecto.
- Un constructor con todos los atributos, excepto de entregado.
. Los métodos que se implementara serán:
- Métodos get de todos los atributos, excepto de entregado.
- Métodos set de todos los atributos, excepto de entregado.
- Sobrescribe los métodos toString.
Como vemos, en principio, las clases anteriores no son padre-hija, pero si tienen en común, por eso
vamos a hacer una interfaz llamada Entregable con los siguientes métodos:
- entregar(): cambia el atributo prestado a true.
- devolver(): cambia el atributo prestado a false.
- isEntregado(): devuelve el estado del atributo prestado.
- Método compareTo (Object a), compara las horas estimadas en los videojuegos y en las
series el numero de temporadas. Como parámetro que tenga un objeto, no es necesario que
implementes la interfaz Comparable. Recuerda el uso de los casting de objetos.
Implementa los anteriores métodos en las clases Videojuego y Serie. Ahora crea una aplicación
ejecutable y realiza lo siguiente:
. Crea dos arrays, uno de Series y otro de Videojuegos, de 5 posiciones cada uno.
. Crea un objeto en cada posición del array, con los valores que desees, puedes usar distintos
constructores.
. Entrega algunos Videojuegos y Series con el método entregar().
. Cuenta cuantos Series y Videojuegos hay entregados. Al contarlos, devuélvelos.
. Por último, indica el Videojuego tiene más horas estimadas y la serie con mas temporadas.
Muestralos en pantalla con toda su información (usa el método toString()).

public abstract class Entregable implements Comparable<Entregable> {


protected String titulo;
protected boolean entregado;

public Entregable(String titulo) {


this.titulo = titulo;

125
this.entregado = false;
}

public void entregar() {


entregado = true;
}

public void devolver() {


entregado = false;
}

public boolean isEntregado() {


return entregado;
}

public abstract int getValorComparativo();

@Override
public int compareTo(Entregable otro) {
return Integer.compare(this.getValorComparativo(),
otro.getValorComparativo());
}

@Override
public String toString() {
return "Título: " + titulo + ", Entregado: " + entregado;
}

public class Serie extends Entregable {


private int numTemporadas;
private String genero;
private String creador;

public Serie(String titulo, int numTemporadas, String genero, String


creador) {
super(titulo);
this.numTemporadas = numTemporadas;
this.genero = genero;
this.creador = creador;
}

public int getNumTemporadas() {


return numTemporadas;
}

@Override
public int getValorComparativo() {
return numTemporadas;
}

126
@Override
public String toString() {
return "Serie - " + super.toString() + ", Num Temporadas: " +
numTemporadas +
", Género: " + genero + ", Creador: " + creador;
}
}
public class Videojuego extends Entregable {
private int horasEstimadas;
private String genero;
private String compañia;

public Videojuego(String titulo, int horasEstimadas, String genero,


String compañia) {
super(titulo);
this.horasEstimadas = horasEstimadas;
this.genero = genero;
this.compañia = compañia;
}

public int getHorasEstimadas() {


return horasEstimadas;
}

@Override
public int getValorComparativo() {
return horasEstimadas;
}

@Override
public String toString() {
return "Videojuego - " + super.toString() + ", Horas Estimadas: "
+ horasEstimadas +
", Género: " + genero + ", Compañía: " + compañia;
}
}

import java.util.ArrayList;
import java.util.List;

public class MainSerie {


public static void main(String[] args) {
List<Serie> series = new ArrayList<>();
List<Videojuego> videojuegos = new ArrayList<>();

// Crear objetos y agregarlos a las listas


series.add(new Serie("Serie 1", 5, "Drama", "Creador 1"));
series.add(new Serie("Serie 2", 3, "Comedia", "Creador 2"));

127
videojuegos.add(new Videojuego("Videojuego 1", 20, "Acción",
"Compañía 1"));
videojuegos.add(new Videojuego("Videojuego 2", 10, "Aventura",
"Compañía 2"));

// Entregar algunos objetos


series.get(0).entregar();
videojuegos.get(1).entregar();

// Contar objetos entregados


int numSeriesEntregadas = countEntregados(series);
int numVideojuegosEntregados = countEntregados(videojuegos);

System.out.println("Series entregadas: " + numSeriesEntregadas);


System.out.println("Videojuegos entregados: " +
numVideojuegosEntregados);

// Encontrar la serie con más temporadas


Serie serieMasTemporadas = encontrarMaximo(series);
System.out.println("Serie con más temporadas: " +
serieMasTemporadas);

// Encontrar el videojuego con más horas estimadas


Videojuego videojuegoMasHoras = encontrarMaximo(videojuegos);
System.out.println("Videojuego con más horas estimadas: " +
videojuegoMasHoras);
}

// Método para contar objetos entregados en una lista


private static <T extends Entregable> int countEntregados(List<T>
lista) {
int count = 0;
for (T item : lista) {
if (item.isEntregado()) {
count++;
}
}
return count;
}

// Método para encontrar el objeto con el máximo valor en una lista


private static <T extends Entregable> T encontrarMaximo(List<T> lista)
{
T maximo = lista.get(0);
for (T item : lista) {
if (item.compareTo(maximo) > 0) {
maximo = item;
} } return maximo; } }

128
Queremos representar con programación orientada a objetos, un aula con estudiantes y un
profesor.
Tanto de los estudiantes como de los profesores necesitamos saber su nombre, edad y sexo. De los
estudiantes, queremos saber también su calificación actual (entre 0 y 10) y del profesor que materia
da.
Las materias disponibles son matemáticas, filosofía y física.
Los estudiantes tendrán un 50% de hacer novillos, por lo que si hacen novillos no van a clase pero
aunque no vayan quedara registrado en el aula (como que cada uno tiene su sitio). El profesor tiene
un 20% de no encontrarse disponible (reuniones, baja, etc.)
Las dos operaciones anteriores deben llamarse igual en Estudiante y Profesor (polimorfismo). El aula
debe tener un identificador numérico, el número máximo de estudiantes y para que esta destinada
(matemáticas, filosofía o física). Piensa que más atributos necesita.
Un aula para que se pueda dar clase necesita que el profesor esté disponible, que el profesor de la
materia correspondiente en el aula correspondiente (un profesor de filosofía no puede dar en un
aula de matemáticas) y que haya más del 50% de alumnos.
El objetivo es crear un aula de alumnos y un profesor y determinar si puede darse clase, teniendo en
cuenta las condiciones antes dichas.
Si se puede dar clase mostrar cuantos alumnos y alumnas (por separado) están aprobados de
momento (imaginad que les están entregando las notas).
NOTA: Los datos pueden ser aleatorios (nombres, edad, calificaciones, etc.) siempre y cuando
tengan sentido (edad no puede ser 80 en un estudiante o calificación ser 12).

public enum Materia {


MATEMATICAS,
FILOSOFIA,
FISICA
}
public abstract class Persona {
protected String nombre;
protected int edad;
protected char sexo;

public Persona(String nombre, int edad, char sexo) {


this.nombre = nombre;
this.edad = edad;
this.sexo = sexo;
}

public String getNombre() {


return nombre;
}

public int getEdad() {


return edad;
}

public char getSexo() {


return sexo;

129
}

public abstract boolean estaDisponible();

@Override
public String toString() {
return "Nombre: " + nombre + ", Edad: " + edad + ", Sexo: " +
sexo;
}
}
import java.util.Random;

public class Estudiante extends Persona {


private double calificacion;

public Estudiante(String nombre, int edad, char sexo) {


super(nombre, edad, sexo);
this.calificacion = generarCalificacion();
}

public double getCalificacion() {


return calificacion;
}

@Override
public boolean estaDisponible() {
// Los estudiantes tienen un 50% de probabilidad de hacer novillos
Random random = new Random();
return random.nextDouble() > 0.5;
}

@Override
public String toString() {
return "Estudiante - " + super.toString() + ", Calificación: " +
calificacion;
}

private double generarCalificacion() {


Random random = new Random();
return random.nextDouble() * 10;
}
}
import java.util.Random;

public class Profesor extends Persona {


private Materia materia;

public Profesor(String nombre, int edad, char sexo, Materia materia) {


super(nombre, edad, sexo);

130
this.materia = materia;
}

public Materia getMateria() {


return materia;
}

@Override
public boolean estaDisponible() {
// Los profesores tienen un 20% de probabilidad de no estar
disponibles
Random random = new Random();
return random.nextDouble() > 0.2;
}

@Override
public String toString() {
return "Profesor - " + super.toString() + ", Materia: " + materia;
}
}
import java.util.ArrayList;
import java.util.List;

public class Aula {


private int identificador;
private int numeroMaxEstudiantes;
private Materia materia;
private Profesor profesor;
private List<Estudiante> estudiantes;

public Aula(int identificador, int numeroMaxEstudiantes, Materia


materia, Profesor profesor) {
this.identificador = identificador;
this.numeroMaxEstudiantes = numeroMaxEstudiantes;
this.materia = materia;
this.profesor = profesor;
this.estudiantes = new ArrayList<>();
}

public int getIdentificador() {


return identificador;
}

public Materia getMateria() {


return materia;
}

public Profesor getProfesor() {


return profesor;

131
}

public List<Estudiante> getEstudiantes() {


return estudiantes;
}

public boolean puedeDarClase() {


int numEstudiantes = estudiantes.size();
int numAprobados = contarAprobados();

// Verificar condiciones para dar clase


return profesor.estaDisponible() && profesor.getMateria() ==
materia
&& numEstudiantes > numeroMaxEstudiantes / 2 &&
numAprobados > numEstudiantes / 2;
}

public int contarAprobados() {


int count = 0;
for (Estudiante estudiante : estudiantes) {
if (estudiante.getCalificacion() >= 5) {
count++;
}
}
return count;
}
}
public class Main {
public static void main(String[] args) {
// Crear profesor y estudiantes
Profesor profesor = new Profesor("Juan Pérez", 35, 'M',
Materia.MATEMATICAS);

Estudiante estudiante1 = new Estudiante("Ana López", 18, 'F');


Estudiante estudiante2 = new Estudiante("Pedro Gómez", 19, 'M');
Estudiante estudiante3 = new Estudiante("María Rodríguez", 20,
'F');
Estudiante estudiante4 = new Estudiante("Carlos Ramírez", 21,
'M');
Estudiante estudiante5 = new Estudiante("Laura Sánchez", 22, 'F');

// Crear aula y agregar estudiantes


Aula aula = new Aula(1, 10, Materia.MATEMATICAS, profesor);
aula.getEstudiantes().add(estudiante1);
aula.getEstudiantes().add(estudiante2);
aula.getEstudiantes().add(estudiante3);
aula.getEstudiantes().add(estudiante4);
aula.getEstudiantes().add(estudiante5);

132
// Verificar si se puede dar clase
if (aula.puedeDarClase()) {
System.out.println("Se puede dar clase en el aula " +
aula.getIdentificador());

int numAprobados = aula.contarAprobados();


int numAprobadas = aula.getEstudiantes().size() -
numAprobados;

System.out.println("Estudiantes aprobados: " + numAprobados);


System.out.println("Estudiantes aprobadas: " + numAprobadas);
} else {
System.out.println("No se puede dar clase en el aula " +
aula.getIdentificador());
}
}
}

133
Vamos a hacer una baraja de cartas españolas orientado a objetos. Una carta tiene un número entre
1 y 12 (el 8 y el 9 no los incluimos) y un palo (espadas, bastos, oros y copas)
La baraja estará compuesta por un conjunto de cartas, 40 exactamente.
Las operaciones que podrá realizar la baraja son:
- barajar: cambia de posición todas las cartas aleatoriamente
- siguienteCarta: devuelve la siguiente carta que está en la baraja, cuando no haya más o se
haya llegado al final, se indica al usuario que no hay más cartas.
- cartasDisponibles: indica el número de cartas que aún puede repartir
- darCartas: dado un número de cartas que nos pidan, le devolveremos ese número de cartas
(piensa que puedes devolver). En caso de que haya menos cartas que las pedidas, no
devolveremos nada pero debemos indicárselo al usuario.
- cartasMonton: mostramos aquellas cartas que ya han salido, si no ha salido ninguna
indicárselo al usuario
- mostrarBaraja: muestra todas las cartas hasta el final. Es decir, si se saca una carta y luego se
llama al método, este no mostrara esa primera carta.
public enum Palo {
ESPADAS,
BASTOS,
OROS,
COPAS
}
public class Carta {
private int numero;
private Palo palo;

public Carta(int numero, Palo palo) {


this.numero = numero;
this.palo = palo;
}

public int getNumero() {


return numero;
}

public Palo getPalo() {


return palo;
}

@Override
public String toString() {
return numero + " de " + palo;
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Baraja {

134
private List<Carta> cartas;
private List<Carta> monton;

public Baraja() {
cartas = new ArrayList<>();
monton = new ArrayList<>();

// Crear las cartas de la baraja


for (Palo palo : Palo.values()) {
for (int numero = 1; numero <= 12; numero++) {
if (numero != 8 && numero != 9) {
cartas.add(new Carta(numero, palo));
}
}
}
}

public void barajar() {


Collections.shuffle(cartas);//shuffle sirve para mezclar
aleatoriamente los elementos de la lista
}

public Carta siguienteCarta() {


if (cartas.isEmpty()) {
System.out.println("No hay más cartas en la baraja.");
return null;
}

Carta carta = cartas.remove(0);


monton.add(carta);
return carta;
}

public int cartasDisponibles() {


return cartas.size();
}

public List<Carta> darCartas(int cantidad) {


if (cantidad > cartasDisponibles()) {
System.out.println("No hay suficientes cartas en la baraja.");
return null;
}

List<Carta> cartasRepartidas = new ArrayList<>();

for (int i = 0; i < cantidad; i++) {


Carta carta = siguienteCarta();
if (carta != null) {
cartasRepartidas.add(carta);

135
}
}

return cartasRepartidas;
}

public void cartasMonton() {


if (monton.isEmpty()) {
System.out.println("Aún no ha salido ninguna carta del
montón.");
return;
}

System.out.println("Cartas en el montón:");
for (Carta carta : monton) {
System.out.println(carta);
}
}

public void mostrarBaraja() {


if (cartas.isEmpty()) {
System.out.println("No hay cartas en la baraja.");
return;
}

System.out.println("Cartas en la baraja:");
for (Carta carta : cartas) {
System.out.println(carta);
}
}
}

import java.util.List;

public class MainCartas {


public static void main(String[] args) {
Baraja baraja = new Baraja();

System.out.println("Barajando las cartas...");


baraja.barajar();

System.out.println("Cartas disponibles en la baraja: " +


baraja.cartasDisponibles());

System.out.println("\nRepartiendo 5 cartas...");
List<Carta> cartasRepartidas = baraja.darCartas(5);
if (cartasRepartidas != null) {
System.out.println("Cartas repartidas:");
for (Carta carta : cartasRepartidas) {

136
System.out.println(carta);
}
}

System.out.println("\nMostrando el montón de cartas:");


baraja.cartasMonton();

System.out.println("\nMostrando las cartas restantes en la


baraja:");
baraja.mostrarBaraja();
}
}

137
Vamos a hacer el juego de la ruleta rusa en Java.
Como muchos sabéis, se trata de un número de jugadores que con un revolver con un sola bala en el
tambor se dispara en la cabeza.
Las clases a hacer son:
- Revolver:
o Atributos:
▪ posición actual (posición del tambor donde se dispara, puede que esté la
bala o no)
▪ posición bala (la posición del tambor donde se encuentra la bala)
Estas dos posiciones, se generaran aleatoriamente.

- Funciones:
o disparar(): devuelve true si la bala coincide con la posición actual
o siguienteBala(): cambia a la siguiente posición del tambor
o toString(): muestra información del revolver (posición actual y donde está la bala)
- Jugador:
o Atributos
▪ id (representa el número del jugador, empieza en 1)
▪ nombre (Empezara con Jugador más su ID, «Jugador 1» por ejemplo)
▪ vivo (indica si está vivo o no el jugador)
o Funciones:
▪ disparar(Revolver r): el jugador se apunta y se dispara, si la bala se dispara,
el jugador muere.
o Juego:
▪ Atributos:
• Jugadores (conjunto de Jugadores)
• Revolver
▪ Funciones:
• finJuego(): cuando un jugador muere, devuelve true
• ronda(): cada jugador se apunta y se dispara, se informara del
estado de la partida (El jugador se dispara, no ha muerto en esa
ronda, etc.)
El número de jugadores será decidido por el usuario, pero debe ser entre 1 y 6. Si no está en este
rango, por defecto será 6.
En cada turno uno de los jugadores, dispara el revólver, si este tiene la bala el jugador muere y el
juego termina.
Aunque no lo haya comentado, recuerda usar una clase ejecutable para probarlo.

import java.util.Random;

public class Revolver {


private int posicionActual;
private int posicionBala;

public Revolver() {
posicionActual = 1;
posicionBala = new Random().nextInt(6) + 1;
}

138
public boolean disparar() {
boolean resultado = (posicionActual == posicionBala);
siguienteBala();
return resultado;
}

public void siguienteBala() {


posicionActual = (posicionActual % 6) + 1;
}

public String toString() {


return "Posición actual: " + posicionActual + ", Posición de la
bala: " + posicionBala;
}
}

public class Jugador {


private int id;
private String nombre;
private boolean vivo;

public Jugador(int id) {


this.id = id;
this.nombre = "Jugador " + id;
this.vivo = true;
}

public void disparar(Revolver revolver) {


if (revolver.disparar()) {
vivo = false;
System.out.println(nombre + " se ha disparado y ha muerto.");
} else {
System.out.println(nombre + " se ha disparado y sigue vivo.");
}
}

public boolean estaVivo() {


return vivo;
}

}
import java.util.HashSet;
import java.util.Set;

public class Juego {


private Set<Jugador> jugadores;
private Revolver revolver;

139
public Juego(int numJugadores) {
if (numJugadores < 1 || numJugadores > 6) {
numJugadores = 6;
}
jugadores = new HashSet<>();
revolver = new Revolver();
for (int i = 1; i <= numJugadores; i++) {
jugadores.add(new Jugador(i));
}
}

public boolean finJuego() {


for (Jugador jugador : jugadores) {
if (!jugador.estaVivo()) {
return true;
}
}
return false;
}

public void ronda() {


for (Jugador jugador : jugadores) {
jugador.disparar(revolver);
if (finJuego()) {
break;
}
}
}
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {


public static void main(String[] args) {
BufferedReader teclado= new BufferedReader(new
InputStreamReader(System.in));
try{
System.out.print("Ingrese el número de jugadores (entre 1 y
6): ");
int numJugadores;
numJugadores= Integer.parseInt(teclado.readLine());
Juego juego = new Juego(numJugadores);
while (!juego.finJuego()) {
juego.ronda();
}
}catch (IOException e){ System.out.println(e.getMessage()); }} }

140
Nos piden hacer un almacén, vamos a usar programación orientado a objetos.
En un almacén se guardan un conjunto de bebidas.

Estos productos son bebidas como agua mineral y bebidas azucaradas (coca-cola, fanta, etc). De los
productos nos interesa saber su identificador (cada uno tiene uno distinto), cantidad de litros, precio
y marca.

Si es agua mineral nos interesa saber también el origen (manantial tal sitio o donde sea).

Si es una bebida azucarada queremos saber el porcentaje que tiene de azúcar y si tiene o no alguna
promoción (si la tiene tendrá un descuento del 10% en el precio).

En el almacén iremos almacenado estas bebidas por estanterías (que son las columnas de la matriz).

Las operaciones del almacén son las siguientes:

- Calcular precio de todas las bebidas: calcula el precio total de todos los productos del
almacén.
- Calcular el precio total de una marca de bebida: dada una marca, calcular el precio total de
esas bebidas.
- Calcular el precio total de una estantería: dada una estantería (columna) calcular el precio
total de esas bebidas.
- Agregar producto: agrega un producto en la primera posición libre, si el identificador esta
repetido en alguno de las bebidas, no se agregará esa bebida.
- Eliminar un producto: dado un ID, eliminar el producto del almacén.
- Mostrar información: mostramos para cada bebida toda su información.
Puedes usar un main para probar las funcionalidades (añade productos, calcula precios, muestra
información, etc)

public class Bebida {


private String identificador;
private double litros;
private double precio;
private String marca;

public Bebida(String identificador, double litros, double precio,


String marca) {
this.identificador = identificador;
this.litros = litros;
this.precio = precio;
this.marca = marca;
}

public String getIdentificador() {


return identificador;
}

public double getPrecio() {

141
return precio;
}

public String getMarca() {


return marca;
}

public void mostrarInformacion() {


System.out.println("Identificador: " + identificador);
System.out.println("Litros: " + litros);
System.out.println("Precio: " + precio);
System.out.println("Marca: " + marca);
}

public class BebidaAzucarada extends Bebida {


private double porcentajeAzucar;
private boolean tienePromocion;

public BebidaAzucarada(String identificador, double litros, double


precio, String marca, double porcentajeAzucar, boolean tienePromocion) {
super(identificador, litros, precio, marca);
this.porcentajeAzucar = porcentajeAzucar;
this.tienePromocion = tienePromocion;
}

public double getPorcentajeAzucar() {


return porcentajeAzucar;
}

public boolean tienePromocion() {


return tienePromocion;
}

@Override
public void mostrarInformacion() {
super.mostrarInformacion();
System.out.println("Porcentaje de Azúcar: " + porcentajeAzucar);
System.out.println("Tiene Promoción: " + tienePromocion);
}
}

public class AguaMineral extends Bebida {


private String origen;

public AguaMineral(String identificador, double litros, double precio,


String marca, String origen) {
super(identificador, litros, precio, marca);

142
this.origen = origen;
}

public String getOrigen() {


return origen;
}

@Override
public void mostrarInformacion() {
super.mostrarInformacion();
System.out.println("Origen: " + origen);
}
}

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Almacen {


private List<List<Bebida>> estanterias;

public Almacen(int numEstanterias) {


estanterias = new ArrayList<>();
for (int i = 0; i < numEstanterias; i++) {
estanterias.add(new ArrayList<>());
}
}

public double calcularPrecioTotalBebidas() {


double precioTotal = 0;
for (List<Bebida> estanteria : estanterias) {
for (Bebida bebida : estanteria) {
precioTotal += bebida.getPrecio();
}
}
return precioTotal;
}

public double calcularPrecioTotalMarca(String marca) {


double precioTotal = 0;
for (List<Bebida> estanteria : estanterias) {
for (Bebida bebida : estanteria) {
if (bebida.getMarca().equals(marca)) {
precioTotal += bebida.getPrecio();
}
}
}
return precioTotal;
}

143
public double calcularPrecioTotalEstanteria(int numEstanteria) {
double precioTotal = 0;
if (numEstanteria >= 0 && numEstanteria < estanterias.size()) {
List<Bebida> estanteria = estanterias.get(numEstanteria);
for (Bebida bebida : estanteria) {
precioTotal += bebida.getPrecio();
}
}
return precioTotal;
}

public void agregarProducto(Bebida bebida, int numEstanteria) {


if (numEstanteria >= 0 && numEstanteria < estanterias.size()) {
List<Bebida> estanteria = estanterias.get(numEstanteria);
estanteria.add(bebida);
}
}

public void eliminarProducto(String identificador) {


for (List<Bebida> estanteria : estanterias) {
for (Bebida bebida : estanteria) {
if (bebida.getIdentificador().equals(identificador)) {
estanteria.remove(bebida);
return;
}
}
}
}

public void mostrarInformacion() {


for (int i = 0; i < estanterias.size(); i++) {
System.out.println("Estantería " + i + ":");
List<Bebida> estanteria = estanterias.get(i);
for (Bebida bebida : estanteria) {
bebida.mostrarInformacion();
System.out.println();
}
}
}
public void ordenarPorPrecio() {
for (List<Bebida> estanteria : estanterias) {
Collections.sort(estanteria,
Comparator.comparingDouble(Bebida::getPrecio));
}
}

public void ordenarPorMarca() {


for (List<Bebida> estanteria : estanterias) {
Collections.sort(estanteria,

144
Comparator.comparing(Bebida::getMarca));
}
}

public void ordenarPorIdentificador() {


List<Bebida> todasLasBebidas = new ArrayList<>();
for (List<Bebida> estanteria : estanterias) {
todasLasBebidas.addAll(estanteria);
}
Collections.sort(todasLasBebidas,
Comparator.comparing(Bebida::getIdentificador));
int index = 0;
for (List<Bebida> estanteria : estanterias) {
estanteria.clear();
estanteria.addAll(todasLasBebidas.subList(index, index +
estanteria.size()));
index += estanteria.size();
}
}
}

145

También podría gustarte