Programación procedimental

Entrada y salida

Leer e imprimir números

Lee e imprime un número real. Obtener código fuente.

Formatear horas, minutos, segundos

Lee los tres campos de una hora y los imprime con formato. Obtener código fuente.

Expresiones y condicionales

Índice de masa corporal

El siguiente programa fue el resultado de un proceso de análisis (comprensión del problema), diseño (de un algoritmo en pseudocódigo), implementación (traducción del algoritmo a Java) y pruebas (de cada uno de los anteriores). El rastro del diseño se puede ver en los comentarios en el código.

 1000 o masa <= 0 o altura <= 0 o altura > 300cm
            if ( mass > 1000.0 || mass <= 0.0 || heightCm <= 0.0 || heightCm > 300.0 )
            {
                // Informar datos invalidos
                System.out.println("invalid data");
            }
            else
            {
                // Los datos son validos

                // Convertir la altura a metros
                double heightMeters = heightCm / 100.0;

                // Calcular el indice de masa corporal (bmi)
                double bodyMassIndex = mass / (heightMeters * heightMeters);

                // Informar la masa, la altura en centrimetros, y el bmi
                System.out.printf("%5.2f ", bodyMassIndex);

                // Imprimir el estado nutricional de acuerdo a los rangos de la OMC
                if ( bodyMassIndex < 18.5 )
                {
                    // Informar infrapeso
                    System.out.println("underweight");
                }
                if ( bodyMassIndex >= 18.5 && bodyMassIndex < 25.0 )
                {
                    // Informar normal
                    System.out.println("normal");
                }
                if ( bodyMassIndex >= 25.0 && bodyMassIndex < 30.0 )
                {
                    // Informar sobrepeso
                    System.out.println("overweight");
                }
                if ( bodyMassIndex >= 30.0 )
                {
                    // Informar obesidad
                    System.out.println("obese");
                }
            }
        }

        // Close the standard input
        input.close();
    }
}
]]>
Lee masas y alturas. Imprime el índice de masa corporal y la categoría nutricional de acuerdo a la OMS. Obtener código fuente.

Números aleatorios

Produce números aleatorios enteros en un rango dado, por ejemplo, si el rango es 00 a 75 (inclusive), producirá números válidos para un bingo.

Números aleatorios en rango. Obtener código fuente.

Operadores relacionales

Los operadores ariméticos (+, -, *, /, %) funcionan únicamente con tipos de datos primitivos (enteros y flotantes). Sólo existe una única excepción: el operador + puede concatenar textos (String). Los demás operadores de Java sólo trabajan con tipos de datos primitivos: operadores relacionales (==, >, >=, <, <=), operadores lógicos (!, &&, ||). Sin embargo hay que tener algunos cuidados. Por ejemplo, el operador de igualdad == sólo funciona con enteros, y en caso de flotantes sólo funciona cuando se compara contra 0.0.

 1000.0 ? 1000.0 : mass;
/*		
		mass > 1000.0
			? System.out.println("invalid data")
			: System.out.println("valid data"); // bmi = mass / h*h*
*/
		int a = 5;
		int x = ++a - a--;
		System.out.printf("a=%d x=%d\n", a, x);
		
		// Close the standard input
		input.close();
	}
}
]]>
Algunos errores comunes con operadores. Obtener código fuente.

Los operadores de asignación (=) tienen variantes cortas para cada operador aritmético (+=, -=, *=, /=, %=), y operadores de pre/post-incremento (++) y pre/post-decremento (--). Estos últimos deben evitarse combinar en una misma expresión.

Ciclos

Se mostró cómo crear un ejercicio desde cero. Las instrucciones se encuentran en la sección Ejercicios inventados del curso. El ejercicio completo puede obtenerse en:

Se crearon 11 casos de prueba, varios con datos inválidos. El siguiente programa usa excepciones de Java para reaccionar a esas condiciones anormales.


		try
		{
			// Read the width of the rectangle, stop if it is negative
			int width = input.nextInt();
			if ( width < 0 )
				throw new RuntimeException("invalid data");

			// If there is another number
			int height = width;
			if ( input.hasNextInt() )
			{
				// Read the height, stop if it is negative
				height = input.nextInt();
				if ( height < 0 )
					throw new RuntimeException("invalid data");
			}

			// Read the fill character
			char fillChar = input.next().charAt(0);

			// Read the boolean that indicates if the rectangle is filled
			// We read the boolean as 0 or 1, and stop if something else is given
			int filled = input.nextInt();
			if ( filled != 0 && filled != 1 )
				throw new RuntimeException("invalid data");
			boolean isFilled = filled == 1;

			// Print each of the height rows
			for ( int row = 1; row <= height; ++row )
			{
				// Print each of the width columns in the row
				for ( int column = 1; column <= width; ++column )
				{
					// If rectangle is filled, or row is first or last one, or column is first or last one
					if ( isFilled || row == 1 || row == height || column == 1 || column == width )
					{
						// Print the fill character
						System.out.print(fillChar);
					}
					else
					{
						// Print a space
						System.out.print(' ');
					}
				}

				// Print a new line
				System.out.println();
			}
		}
		catch ( java.util.InputMismatchException exception )
		{
			System.out.println("invalid data");
		}
		catch ( java.util.NoSuchElementException exception )
		{
			System.out.println("missing data");
		}
		catch ( RuntimeException exception )
		{
			System.out.println( exception.getMessage() );
		}
// 
		// Close the standard input
		input.close();
	}
}
]]>
Imprime rectángulos rellenos o no de tamaño arbitrario. Obtener código fuente.

Subrutinas

Permutaciones y combinaciones

El siguiente ejemplo muestra el resultado de cómo un problema se dividió en varios subproblemas recursivamente. Un subproblema que requiere su propio proceso de análisis, diseño e implementación, es buen canditato para implementarse como una subrutina. Cada subrutina debe tener su propia documentación. Aquí se usa notación JavaDoc.


	/**
	 * Run the solution. This method is called from main()
	 */
	public void run()
	{
		// Create object to read data from standard input
		input = new Scanner(System.in);
		
		// Read the n, r
		long n = input.nextLong();
		long r = input.nextLong();
		
		// Print the table header
		System.out.printf("%33s%21s%n", "No repetitions", "With repetitions");
		
		// Print permutations
		printPermutations(n, r);

		// Print combinations
		printCombinations(n, r);
		
		// Close the standard input
		input.close();
	}

	/**
	 * Print the table row for the permutations with and without repetition
	 *
	 * @param n The number of elements to choose from. n must be positive
	 * otherwise, it will print invalid data.
	 *
	 * @param r The number of chosen elements from n. r must be positive
	 */
	public void printPermutations(long n, long r)
	{
		System.out.print("Permutations");

		// Calculate and print permutations without repetition
		printPermutationsNoRepetition(n, r);
		
		// Calculate and print permutations with repetition
		printPermutationsWithRepetition(n, r);
	}

	/**
	 * Calculate and print permutations without repetition
	 *
	 * @param n The number of elements to choose from. n must be positive
	 * otherwise, it will print invalid data.
	 *
	 * @param r The number of chosen elements from n. r must be positive
	 */
	public void printPermutationsNoRepetition(long n, long r)
	{
		long permutations = factorialDivision(n, n - r);
		System.out.printf("%21d", permutations);
	}

	/**
	 * Calculate and print permutations with repetition
	 *
	 * @param n The number of elements to choose from. n must be positive
	 * otherwise, it will print invalid data.
	 *
	 * @param r The number of chosen elements from n. r must be positive
	 */
	public void printPermutationsWithRepetition(long n, long r)
	{
		long permutations = calculatePermutationsWithRepetition(n, r);
		System.out.printf("%21d%n", permutations);
	}
	
	/**
	 * Calculate the number of permutations with repetition of r in n as n^r
	 * 
	 * @see printPermutations() for details about the parameters
	 * 
	 * @return The amount of permutations 
	 */
	public long calculatePermutationsWithRepetition(long n, long r)
	{
		long power = 1;
		for ( long counter = 1; counter <= r; ++counter )
			power = power * n;
		
		return power;
	}

	/**
	 * Returns the factorial of an integer number. The factorial of n is
	 * the multiplication of all integers less or equals to n
	 *
	 * @param n The number to get the factorial from. n must be positive,
	 * otherwise an RuntimeException is thrown. If n is greater than 20
	 * it will overflow and an invalid result is produced
	 *
	 * @return The factorial of n
	 */
	public long factorial(long n)
	{
		long factorial = 1;
		for ( long counter = 1; counter <= n; ++counter )
		{
			factorial *= counter;
		}
		
		return factorial;
	}

	/**
	 * Optimizes the calculus of the division of two factorial parts in the form:
	 * (numerator)! / (denominator)!
	 * 
	 * @see factorialDivision(long, long, long)
	 */
	public long factorialDivision(long numerator, long denominator)
	{
/*		long factorial = 1;
		if ( numerator >= denominator && denominator > 0 )
		{
			for ( long counter = numerator; counter > 1 && counter > denominator; --counter )
			{
				factorial *= counter;
			}
		}
		else
		{
			factorial = 0;
		}

		return factorial;
		*/
		return factorialDivision(numerator, denominator, 1);
	}

	/**
	 * Optimizes the calculus of the division with three factorial parts in the form:
	 * (numerator)! / ( (denominator1)! (denominator2)! )
	 * 
	 * This function cancels the common numerators and denominators before multiply
	 * them for getting the resulting factorial. Therefore it avoids overfloating
	 * long values.
	 * 
	 * @param numerator The numerator before calculating its factorial
	 * @param denominator1 The first denominator part before calculating its factorial
	 * @param denominator2 The second denominator part before calculating its factorial
	 * @return The result of the division n! / (d1! d2!)
	 */
	public long factorialDivision(long numerator, long denominator1, long denominator2)
	{
		long factorial = 1;
		long minimum = Math.min(denominator1, denominator2);
		long maximum = Math.max(denominator1, denominator2);
		
		if ( numerator >= maximum && maximum > 0 )
		{
			for ( long counter = numerator; counter > maximum; --counter )
			{
				factorial *= counter;
			}
			
			factorial /= factorial(minimum);
		}
		else
		{
			factorial = 0;
		}

		return factorial;
	}

	/**
	 * Print the table row for the combinations with and without repetition
	 *
	 * @param n The number of elements to choose from. n must be positive
	 * otherwise, it will print invalid data.
	 *
	 * @param r The number of chosen elements from n. r must be positive
	 */
	public void printCombinations(long n, long r)
	{
		System.out.print("Combinations");

		// Calculate and print combinations without repetition
		printCombinationsNoRepetition(n, r);
		
		// Calculate and print combinations with repetition
		printCombinationsWithRepetition(n, r);
	}

	/**
	 * Calculate and print combinations without repetition
	 *
	 * @param n The number of elements to choose from. n must be positive
	 * otherwise, it will print invalid data.
	 *
	 * @param r The number of chosen elements from n. r must be positive
	 */
	void printCombinationsNoRepetition(long n, long r)
	{
		long combinations = factorialDivision(n, r, n - r);
		System.out.printf("%21d", combinations);
	}

	/**
	 * Calculate and print combinations with repetition
	 *
	 * @param n The number of elements to choose from. n must be positive
	 * otherwise, it will print invalid data.
	 *
	 * @param r The number of chosen elements from n. r must be positive
	 */
	void printCombinationsWithRepetition(long n, long r)
	{
		long combinations = factorialDivision(n + r - 1, r, n - 1);
		System.out.printf("%21d", combinations);
	}
// 
}
]]>
Imprime una tabla con permutaciones y combinaciones, ambas con repetición y sin repetición. Obtener código fuente.

Indirección, arreglos y matrices

Mediana estadística

Calcula la mediana de un número conocido de datos reales. Obtener código fuente.

Leer e imprimir una matrix

Crea una matriz de dimensiones arbitrarias, la llena leyendo valores de la entrada estándar, y la imprime en la salida estándar. Obtener código fuente.

Recursión

Factorial



	/**
	 * Run the solution. This method is called from main()
	 */
	public void run()
	{
		// Create object to read data from standard input
		this.input = new Scanner(System.in);

		// This code replicates the input to the standard output
		// Modify this code to solve the problem
		while ( this.input.hasNextLong() )
		{
			long number = this.input.nextLong();
			
			testIterativeFactorial(number);
			testTailRecursiveFactorial(number);
			testRecursiveFactorial(number);
		}

		// Close the standard input
		this.input.close();
	}
	
	public void testIterativeFactorial(long number)
	{
		long start = System.currentTimeMillis();
		BigInteger factorial = iterativeFactorial( number );
		double duration = (System.currentTimeMillis() - start) / 1000.0;
		System.out.printf( "Iterative factorial:%n%,d: %.3fs%n%n", factorial, duration );
	}
	
	public void testTailRecursiveFactorial(long number)
	{
		long start = System.currentTimeMillis();
		BigInteger factorial = tailRecursiveFactorial( number );
		double duration = (System.currentTimeMillis() - start) / 1000.0;
		System.out.printf( "%n%nTail Recursive factorial:%n%,d: %.3fs%n%n", factorial, duration );
	}
	
	public void testRecursiveFactorial(long number)
	{
		long start = System.currentTimeMillis();
		BigInteger factorial = recursiveFactorial( number );
		double duration = (System.currentTimeMillis() - start) / 1000.0;
		System.out.printf( "%n%nRecursive factorial:%n%,d: %.3fs%n%n", factorial, duration );
	}
	
	public static BigInteger iterativeFactorial(long number)
	{
		BigInteger result = BigInteger.valueOf(1);
		
		for ( long current = 1; current <= number; ++current )
			result = result.multiply( BigInteger.valueOf(current) );
		
		return result;
	}

	public static BigInteger recursiveFactorial(long number)
	{
		if ( number <= 0 )
			return BigInteger.valueOf(1);
		else
			return BigInteger.valueOf(number).multiply( recursiveFactorial(number - 1) );
	}
	
	public static BigInteger tailRecursiveFactorial(long number)
	{
		return tailRecursiveFactorial(number, BigInteger.valueOf(1));
	}
	
	public static BigInteger tailRecursiveFactorial(long number, BigInteger accumulator)
	{
		if ( number <= 0 )
			return accumulator;
		else
			return tailRecursiveFactorial( number - 1, accumulator.multiply( BigInteger.valueOf(number) ) );
	}
// 
}
]]>
Factorial iterativo, recursivo puro, y con recursión de cola. Obtener código fuente.

Nota: El compilador de Java no soporta recursión de cola al momento de escribir (nov-2017).

Fibonacci



	/**
	 * Run the solution. This method is called from main()
	 */
	public void run()
	{
		// Create object to read data from standard input
		this.input = new Scanner(System.in);

		// This code replicates the input to the standard output
		// Modify this code to solve the problem
		while ( this.input.hasNextLong() )
		{
			long number = this.input.nextLong();
			System.out.printf("%,d: %,d%n", number, recursiveFibonacci(number));
			System.out.printf("%,d: %,d%n%n", number, tailRecursiveFibonacci(number));
		}

		// Close the standard input
		this.input.close();
	}
	
	public static BigInteger iterativeFactorial(long number)
	{
		BigInteger result = BigInteger.valueOf(1);
		
		for ( long current = 1; current <= number; ++current )
			result = result.multiply( BigInteger.valueOf(current) );
		
		return result;
	}

	public static BigInteger recursiveFibonacci(long number)
	{
		if ( number <= 1 )
			return BigInteger.valueOf(number);
		else
			return recursiveFibonacci(number - 1).add( recursiveFibonacci(number - 2) );
	}
	
	public static BigInteger tailRecursiveFibonacci(long number)
	{
		return tailRecursiveFibonacci(number, BigInteger.valueOf(0), BigInteger.valueOf(1));
	}
	
	public static BigInteger tailRecursiveFibonacci(long number, BigInteger previous, BigInteger result)
	{
		if ( number <= 0 )
			return previous;
		else if ( number == 1 )
			return result;
		else
			return tailRecursiveFibonacci(number - 1, result, previous.add(result) );
	}
// 
}
]]>
Fibonacci iterativo, recursivo puro, y con recursión de cola. Obtener código fuente.

Potencia entera



	/**
	 * Run the solution. This method is called from main()
	 */
	public void run()
	{
		// Create object to read data from standard input
		this.input = new Scanner(System.in);

		// This code replicates the input to the standard output
		// Modify this code to solve the problem
		while ( this.input.hasNextLong() )
		{
			BigInteger base = this.input.nextBigInteger();
			long exponent = this.input.nextLong();
			System.out.printf("%,d^%,d: %,d%n", base, exponent, recursivePower(base, exponent));
			System.out.printf("%,d^%,d: %,d%n", base, exponent, efficientTailRecursivePower(base, exponent));
		}

		// Close the standard input
		this.input.close();
	}
	
	public static BigInteger iterativeFactorial(long number)
	{
		BigInteger result = BigInteger.valueOf(1);
		
		for ( long current = 1; current <= number; ++current )
			result = result.multiply( BigInteger.valueOf(current) );
		
		return result;
	}

	public static BigInteger recursivePower(BigInteger base, long exponent)
	{
		if ( exponent <= 0 )
			return BigInteger.valueOf(1);
		else
			return base.multiply( recursivePower(base, exponent - 1) );
	}
	
	public static BigInteger efficientRecursivePower(BigInteger base, long exponent)
	{
		if ( exponent <= 0 )
			return BigInteger.valueOf(1);
		else if ( exponent == 1 )
			return base;
		else if ( exponent % 2 == 0 )
			return efficientRecursivePower(base.multiply(base), exponent / 2);
		else
			return base.multiply( efficientRecursivePower(base, exponent - 1) );
	}

	public static BigInteger efficientTailRecursivePower(BigInteger base, long exponent)
	{
		return efficientTailRecursivePower(base, exponent, BigInteger.valueOf(1));
	}

	public static BigInteger efficientTailRecursivePower(BigInteger base, long exponent, BigInteger result)
	{
		if ( exponent <= 0 )
			return result;
		else if ( exponent % 2 == 0 )
			return efficientTailRecursivePower(base.multiply(base), exponent / 2, result);
		else
			return efficientTailRecursivePower(base, exponent - 1, base.multiply(result) );
	}
	
// 
}
]]>
Calcula la potencia entera minimizand la cantidad de multiplicaciones. Obtener código fuente.

Programación orientada a objetos

Clases e instancias

Índice de masa corporal (versión OO)

 1000 o masa <= 0 o altura <= 0 o altura > 300cm
			if ( this.person.isValid() )
			{
				// Los datos son validos
				this.person.calculateBodyMassIndex();
				this.person.calculateNutritionalState();

				// Informar la masa, la altura en centrimetros, y el bmi
				System.out.printf("%5.2f %s%n", this.person.getBodyMassIndex(), this.person.getNutritionalState() );
			}
			else
			{
				// Informar datos invalidos
				System.out.println("invalid data");
			}
		}

		// Close the standard input
		input.close();
	}
	
	/**
	 * Read data from standard input and create a Person
	 * 
	 * @return true if a Person was read from standard input, false if there are no
	 * more persons in standard input
	 */
	public boolean readPerson()
	{
		if ( this.input.hasNextDouble() )
		{
			// Obtener la masa en kilogramos
			double mass = input.nextDouble();
	
			// Obtener la altura en centimetros
			double heightCm = input.nextDouble();
			
			// Toma el plano/clase Persona y crea un objeto/instancia
			this.person = new Person(mass, heightCm);
			
			// Done
			return true;
		}
		else
		{
			// No more data to create a person in standard input
			return false;
		}
	}
}
]]>
Clase Solution que hace de vista y controlador. Obtener código fuente.
 0.0 && height > 0.0 && height <= 300.0;
	}
	
	/**
	 * Updates the body mass index data member for this person
	 */
	public void calculateBodyMassIndex()
	{
		// Convertir la altura a metros
		double heightMeters = this.height / 100.0;

		// Calcular el indice de masa corporal (bmi)
		bodyMassIndex = this.mass / (heightMeters * heightMeters);
	}
	
	/**
	 * Updates the nutritional state data member for this person
	 */
	public void calculateNutritionalState()
	{
		// Actualizar el estado nutricional de acuerdo a los rangos de la OMC
		if ( bodyMassIndex < 18.5 )
		{
			// Informar infrapeso
			this.nutritionalState = "underweight";
		}
		if ( bodyMassIndex >= 18.5 && bodyMassIndex < 25.0 )
		{
			// Informar normal
			this.nutritionalState = "normal";
		}
		if ( bodyMassIndex >= 25.0 && bodyMassIndex < 30.0 )
		{
			// Informar sobrepeso
			this.nutritionalState = "overweight";
		}
		if ( bodyMassIndex >= 30.0 )
		{
			// Informar obesidad
			this.nutritionalState =  "obese";
		}
	}
}
]]>
Clase Person que hace de modelo. Obtener código fuente.

Desigualdad triangular

Diseño de la solución:

Documentación en Markdown (design.md). Obtener código fuente.
Diagrama de clases. Ver en LucidChart.
Diagrama de secuencia. Ver en LucidChart.

Estructura del proyecto, sus carpetas y archivos:

Las clases deben estar en control de versiones en la carpeta src/ de su proyecto. Se presentan a continuación la clase controladora y la clase modelo.

 0 )
		{
			// Read a triangle from arguments and run in batch, that is,
			// do not ask user to type the arguments in standard input
			this.runInBatch(args);
		}
		else
		{
			// Read triangles from standard input
			this.runInteractively();
		}
	}
	
	/**
	 * Run interactively, that is, reads triangles from standard
	 * input until the EOF is reached
	 */
	public void runInteractively()
	{
		// Create object to read data from standard input
		input = new Scanner(System.in);

		// Echoes the standard input
		while ( readTriangle() )
		{
			this.triangle.print();
		}

		// Close the standard input
		input.close();
	}
	
	/**
	 * Creates a Triangle object and read it from standard input
	 * @return true if the Triangle was read, false otherwise
	 */
	public boolean readTriangle()
	{
		// True if a triangle was read, false otherwise
		boolean wasRead = false;
		
		// We assume if there is a real, there are also three edges
		if ( input.hasNextDouble() )
		{
			// Create the triangle object and read its edges
			this.triangle = new Triangle();
			wasRead = this.triangle.read(input);
		}
		else
		{
			// There is not more edges in standard input, return false
			wasRead = false;
		}
		
		return wasRead;
	}
	
	/**
	 * Runs the program in batch mode, that is, read a triangle
	 * from command line arguments instead of standard input
	 * 
	 * @param args The command line arguments
	 */
	public void runInBatch(String args[])
	{
		// Analyze the given arguments
		ArgumentAnalyzer argumentAnalyzer = new ArgumentAnalyzer();
		argumentAnalyzer.analyze(args);
		
		// If help was asked, just print it and exit
		if ( argumentAnalyzer.wasHelpAsked() )
		{
			this.printHelp();
		}
		else
		{
			// Otherwise, if a valid triangle was given
			if ( argumentAnalyzer.hasThreeEdges() )
			{
				// Create a triangle with the three edges
				double edge1 = argumentAnalyzer.getEdge1();
				double edge2 = argumentAnalyzer.getEdge2();
				double edge3 = argumentAnalyzer.getEdge3();
				this.triangle = new Triangle( edge1, edge2, edge3 );
				
				// Print the triangle properties
				this.triangle.print();
			}
			else
			{
				// Otherwise. Print an error message
				System.err.println("Error: insufficient arguments");
			}
			
			// If there was other error, report it
			if ( argumentAnalyzer.getErrorMessage().length() > 0 )
			{
				System.err.print( argumentAnalyzer.getErrorMessage() );
			}
		}
	}
	
	/**
	 * Print usage of this program in standard output
	 */
	public void printHelp()
	{
		System.out.println("TriangleInequality v1.1 [2017-Oct-02] Jeisson Hidalgo-Cespedes");
		System.out.println("Usage: java TriangleInequality [a b c]");
		System.out.println();
		System.out.println("Arguments:");
		System.out.println("  a b c   Edges of the triangle");
		System.out.println();
		System.out.println("Si no arguments are provided, program will run in interactive mode");
		System.out.println("reading triangles from standard input until EOF is provided.");
	}
}
]]>
TriangleInequality.java. Obtener código fuente.
= 3;
	}
	
	/**
	 * Get a copy of the first triangle edge found
	 * @return Length of first edge
	 */
	public double getEdge1()
	{
		return this.edge1;
	}
	
	/**
	 * Get a copy of the second triangle edge found
	 * @return Length of second edge
	 */
	public double getEdge2()
	{
		return this.edge2;
	}
	
	/**
	 * Get a copy of the third triangle edge found
	 * @return Length of third edge
	 */
	public double getEdge3()
	{
		return this.edge3;
	}
	
	/**
	 * Get a reference to the collected error messages found
	 * while the analysis process is done
	 * 
	 * @return All error messages concatenated, or empty if no errors
	 * were collected
	 */
	public String getErrorMessage()
	{
		return this.errorMessage;
	}
	
	/**
	 * Start the command line analysis process
	 * @param arguments The arguments received by main() function
	 */
	public void analyze(String arguments[])
	{
		// Traverse all command line arguments
		for ( int index = 0; index < arguments.length; ++index )
		{
			// If help was asked
			if ( arguments[index].equals("--help") || arguments[index].equals("-help") )
			{
				this.helpAsked = true;
			}
			else if ( analyzeEdge( arguments[index] ) )
			{
				// Do nothing, the method analyzeEdge updated what is required
			}
			else
			{
				// The argument is unknown, report it later
				this.errorMessage += "Error: invalid argument: " + arguments[index] + "\n";
			}
		}
	}
	
	/**
	 * Checks if the given argument text is a triangle's edge value
	 * If so, the value will be stored in the next free edge attribute
	 * 
	 * @param argument The text of the argument to be analyzed
	 * @return true If the argument is an edge, false otherwise
	 */
	private boolean analyzeEdge(String argument)
	{
		try
		{
			// Try to convert the string to double
			double edge = Double.parseDouble(argument);
			
			// If reached this line, the string has a valid double value
			++this.edgeCount;

			// Store the double value as the following edge's length
			switch ( this.edgeCount )
			{
				case 1: this.edge1 = edge; break; 
				case 2: this.edge2 = edge; break;
				case 3: this.edge3 = edge; break;
				
				default:
					// We ignore more edges, only first three are parsed
					System.err.println("Warning: ignoring argument " + argument);
			}
			
			return true;
		}
		catch ( NumberFormatException exception )
		{
			// If entered here, the text is not a valid real value, report it
			return false;
		}
	}
}
]]>
ArgumentAnalyzer.java. Obtener código fuente.
 0.0 && this.edge2 > 0.0 && this.edge3 > 0.0 )
		{
			valid = true;
		}
		return valid;
		*/
		
		// The following expression is equivalent to the previous code
		// But it is not debugger-friendly
		return this.edge1 > 0.0 && this.edge2 > 0.0 && this.edge3 > 0.0;
	}

	/**
	 * Indicates if this triangle is actually a triangle if it fulfills the
	 * triangle inequality principle, that is, each edge is less than the
	 * sum the other edges.
	 * 
	 * @return true if this triangle fulfills the triangle inequality principle,
	 * false otherwise.
	 */
	public boolean isTriangle()
	{
		// We assume the triangle does not fulfill the principle
		boolean valid = false;
		
		// If any edge is less than the sum of the others
		if ( this.edge1 < this.edge2 + this.edge3 )
		{
			if ( this.edge1 > Math.abs(this.edge2 - this.edge3) )
			{
				// The triangle fulfills the inequality principle
				valid = true;
			}
		}
		
		return valid;
	}
	
	/**
	 * Prints the perimeter of this triangle, for example:
	 * P=15
	 */
	public void printPerimeter()
	{
		System.out.printf("P=%s", format(this.calculatePerimeter()) );
	}

	/**
	 * Calculates the perimeter of this triangle. The perimeter is the
	 * sum of the three edges of the triangle.
	 * 
	 * @return The perimeter of this triangle as a real value
	 */
	public double calculatePerimeter()
	{
		return this.edge1 + this.edge2 + this.edge3;
	}

	/**
	 * Calculates and prints the area of this triangle. For example:
	 * A=14.28
	 */
	public void printArea()
	{
		System.out.printf("A=%s", format(this.calculateArea()) );
	}

	/**
	 * Calculates the area of this triangle using its edges through the
	 * Heron's formula
	 * 
	 * @return The area of the triangle as a real value
	 */
	public double calculateArea()
	{
		// The Heron's formula uses the semiperimeter `s` of the triangle
		double semiperimeter = this.calculateSemiperimeter();
		
		// Area is calculated as:
		// A = \sqrt{s(s-a)(s-b)(s-c)}
		// where `s` is semiperimeter and `a, b, c` are the triangle's edges
		double subradical = semiperimeter
				* (semiperimeter - this.edge1)
				* (semiperimeter - this.edge2)
				* (semiperimeter - this.edge3);
		
		return Math.sqrt(subradical);
	}
	
	/**
	 * Calculates the semiperimeter of this triangle. The semiperimeter
	 * is half of perimeter
	 * 
	 * @return The semiperimeter of this triangle as a real value
	 */
	public double calculateSemiperimeter()
	{
		return this.calculatePerimeter() / 2.0;
	}

	/**
	 * Prints the classification of this triangle according to its edges
	 * @see getCategoryByEdge()
	 */
	public void printCategoryByEdge()
	{
		System.out.print( this.getCategoryByEdge() );
	}

	/**
	 * Returns a String indicating the classification of this triangle according
	 * to its edges. The returned text is in Spanish. Possible values are:
	 * "equilatero", "isosceles", or "escaleno".
	 */
	public String getCategoryByEdge()
	{
		String category = "";
		
		// Compare the lengths of the sides
		if ( this.edge1 == this.edge2 && this.edge2 == this.edge3 )
		{
			// All edges are equal, we have an equilateral triangle
			category = "equilatero";
		}
		else if ( this.edge1 == this.edge2 || this.edge1 == this.edge3 || this.edge2 == this.edge3 )
		{
			// Only two edges are equal, we have an isosceles triangle
			category = "isosceles";
		}
		else
		{
			// Otherwise, we assume a scalene triangle
			category = "escaleno";
		}
			
		return category;
	}

	/**
	 * Prints the classification of this triangle according to its angles
	 * @see getCategoryByAngle()
	 */
	public void printCategoryByAngle()
	{
		System.out.print( this.getCategoryByAngle() );
	}

	/**
	 * Returns a String indicating the classification of this triangle according
	 * to its angles. The returned text is in Spanish. Possible values are:
	 * "obtusangulo", "rectangulo", or "acutangulo".
	 */
	public String getCategoryByAngle()
	{
		// We need to find the hypotenuse and the two cathetus
		MutableDouble hypotenuse = new MutableDouble(0.0);
		MutableDouble cathetus1 = new MutableDouble(0.0);
		MutableDouble cathetus2 = new MutableDouble(0.0);
		
		// Find the hypotenuse and the two cathetus objects
		// This method will modify the objects referred by parameter
		findHypotenuseAndCathetuses(hypotenuse, cathetus1, cathetus2);
		
		// Text indicating the classification of this triangle according to angles
		String category = "";
		
		// We use the Pythagorean theorem to check the type of triangle
		// We square each edge for convenience
		double squaredHypotenuse = hypotenuse.getValue() * hypotenuse.getValue();
		double squaredCathetus1 = cathetus1.getValue() * cathetus1.getValue();
		double squaredCathetus2 = cathetus2.getValue() * cathetus2.getValue();
		
		// We compare the squares of the hypotenuse and sum of squared cathetus
		if ( squaredHypotenuse == squaredCathetus1 + squaredCathetus2 )
		{
			// Edges fulfill the Pythagorean theorem, it is a rectangled triangle
			category = "rectangulo";
		}
		else if ( squaredHypotenuse > squaredCathetus1 + squaredCathetus2 )
		{
			// Hypotenuse area is larger than cathetus area, is an obtuse triangle
			category = "obtusangulo";
		}
		else
		{
			// Otherwise, we assume it is an acute triangle
			category = "acutangulo";
		}
		
		return category;
	}
	
	/**
	 * Finds the largest edge of the triangle (hypotenuse) and the two smaller edges
	 * and set the given objects to these results.
	 * 
	 * @param hypotenuse This object will be modified to have the hypotenuse's length
	 * @param cathetus1 This object will be modified to have a cathetus's length
	 * @param cathetus2 This object will be modified to have a cathetus's length
	 */
	public void findHypotenuseAndCathetuses(MutableDouble hypotenuse, MutableDouble cathetus1, MutableDouble cathetus2)
	{
		// Test each edge against the remaining two looking for the largest
		if ( this.edge1 > this.edge2 && this.edge1 > this.edge3 )
		{
			// The edge1 is larger than edge2 and edge3, it is the hypotenuse
			hypotenuse.setValue(this.edge1);
			cathetus1.setValue(this.edge2);
			cathetus2.setValue(this.edge3);
		}
		else if ( this.edge2 > this.edge1 && this.edge2 > this.edge3 )
		{
			// The edge2 is larger than edge1 and edge3, it is the hypotenuse
			hypotenuse.setValue(this.edge2);
			cathetus1.setValue(this.edge1);
			cathetus2.setValue(this.edge3);
		}
		else
		{
			// The edge3 must be larger than edge1 and edge2, it is the hypotenuse
			hypotenuse.setValue(this.edge3);
			cathetus1.setValue(this.edge1);
			cathetus2.setValue(this.edge2);
		}
	}
}
]]>
Triangle.java. Obtener código fuente.
MutableDouble.java: una clase que permite crear objetos en memoria dinámica, los cuales almacenan un único valor real. Por estar en memoria dinámica, cualquier método que tenga una referencia a estos objetos, puede modificar el double interno. Obtener código fuente.

Herencia y polimorfismo (tema extra)

Personas de la Universidad

Ejemplo de entrada: varios tipos de personas. Obtener código fuente.
Clase controladora Solution.java. Obtener código fuente.
Clase Persona. Obtener código fuente.
Clase Estudiante. Obtener código fuente.
Clase Funcionario. Obtener código fuente.
Clase Profesor. Obtener código fuente.
Clase Administrativo. Obtener código fuente.

Estructuras de datos y algoritmos

Areglo dinámico

Clase Array

Una clase arreglo dinámico puede crecer. En Java la clase de biblioteca ArrayList realiza esta funcionalidad.

En el siguiente ejemplo, la clase controladora Solution usa el arreglo dinámico para calcular la mediana sin saber la cantidad de datos que vienen en la entrada estándar.

Clase controladora Solution. Obtener código fuente.
 index; --current )
		{
			this.elements[current] = this.elements[current - 1];
		}
		
		this.elements[index] = element;
		++this.count;
	}
	
	public int getCount()
	{
		return this.count;
	}
	
	public int getCapacity()
	{
		return this.elements.length;
	}
	
	public double getAt(int index)
	{
		return this.elements[index];
	}
	
	private void increaseCapacity()
	{
		double[] newElements = new double[ INCREASE_FACTOR * this.getCapacity() ];
		
		for ( int index = 0; index < this.count; ++index )
		{
			newElements[index] = this.elements[index];
		}
		
		this.elements = newElements;
	}
	
	public void sort()
	{
		Arrays.sort( this.elements, 0, this.getCount() );
	}
	
	public void bubbleSort()
	{
		int changes = -1;
		int pass = 0;
		
		//for ( int pass = 0; pass < this.getCount(); ++pass )
		while ( changes != 0 )
		{
			changes = 0;
			for ( int index = 1; index < this.getCount() - pass; ++index )
			{
				if ( this.elements[index - 1] > this.elements[index] )
				{
					this.swap(index - 1, index);
					++changes;
				}
			}
			++pass;
		}
	}
	
	private void swap(int index1, int index2)
	{
		double copy1 = this.elements[index1];
		this.elements[index1] = this.elements[index2];
		this.elements[index2] = copy1;
	}
	
	/**
	 * Sequential search in O(n)
	 * @param element
	 * @return
	 */
	public int searchFirst(double element)
	{
		int position = -1;
		for ( int index = 0; index < this.getCount() && position == -1; ++index )
		{
			if ( this.elements[index] == element )
			{
				position = index;
			}
		}
		
		return position;
	}
	
	public int binarySearch(double element)
	{
		int low = 0;
		int high = this.getCount();
		
		while ( low < high )
		{
			int middle = (low + high) / 2;
			if ( element == this.elements[middle] )
				return middle;
			else if ( element < this.elements[middle] )
				high = middle;
			else
				low = middle + 1;
		}
		
		return -1;
	}
}
]]>
Clase arreglo dinámico de reales. Obtener código fuente.

Clase Matrix

Una clase que administra una matriz, de dimensiones arbitrarias, y realiza operaciones matemáticas comunes de las matrices.

Programa que lee dos matrices e imprime la suma de ellas. Obtener código fuente.
 0 && columns > 0 )
		{
			// Dimensions are valid, create the actual matrix
			this.matrix = new double[rows][columns];
		}
	}
	
	/**
	 * Read the dimensions and values from the given input
	 * @param matrix
	 */
	public void read(Scanner input)
	{
		// Read the dimensions of the matrix
		int rows = input.nextInt();
		int cols = input.nextInt();
		
		// Create the matrix in heap, and keep a reference
		this.matrix = new double[rows][cols];
		
		// Read all values for the matrix
		for ( int row = 0; row < this.matrix.length; ++row )
		{
			// Read all values for current row
			for ( int col = 0; col < this.matrix[row].length; ++col )
			{
				this.matrix[row][col] = input.nextDouble();
			}
		}
	}
	
	/**
	 * Print this matrix in the given output
	 * @param output The file where print this matrix
	 */
	public void print(PrintStream output)
	{
		// Print each row
		for ( int row = 0; row < matrix.length; ++row )
		{
			// Print each column in the current row
			for ( int col = 0; col < matrix[row].length; ++col )
			{
				// Print the cell value
				output.printf("%.2f ", matrix[row][col]);
			}
			
			// Separate this row from next by a new line character
			output.println();
		}
	}
	
	/**
	 * Adds this matrix with another
	 * 
	 * @param other Other matrix to be added with this
	 * @return A reference to the resulting matrix object containing
	 * the sum of this and the other matrix. If matrixes have different
	 * dimensions, a null reference will be retorned 
	 */
	public Matrix add(Matrix other)
	{
		// The resulting matrix of adding this one with other
		Matrix result = null;
		
		// Only matrixes of same size can be added
		if ( this.matchesSize(other) )
		{
			// Create the matrix object to store the resulting sum 
			result = new Matrix( this.getRowCount(), this.getColumnCount() );
			
			// Add the rows of both matrixes. Because the three matrixes
			// (this, other, and result) have the same dimensions, we can use
			// the dimensions of anyone to control the loops
			for ( int row = 0; row < this.matrix.length; ++row )
			{
				// Add all columns in this row
				for ( int col = 0; col < this.matrix[row].length; ++col )
				{
					// The result cell is the sum of respectives cells in
					// this and other matrixes
					result.matrix[row][col] = this.matrix[row][col] + other.matrix[row][col];
				}
			}
		}
		
		// Notice that we return null if matrixes do not match their size
		return result;
	}
	
	/**
	 * Returns true if this matrix and other have the same amount of
	 * rows and columns
	 * 
	 * @param other Other matrix to be checked against this for size
	 * @return true if this matrix and other have the same dimensions
	 */
	public boolean matchesSize(Matrix other)
	{
		return this.getRowCount() == other.getRowCount()
			&& this.getColumnCount() == other.getColumnCount();
	}
	
	/**
	 * Return the number of rows that this matrix has 
	 * @return The number of rows, 0 if this is an invalid matrix
	 */
	public int getRowCount()
	{
		return this.matrix != null ? this.matrix.length : 0;
	}
	
	/**
	 * Return the number of columns that this matrix has
	 * @return The number of rows, 0 if this is an invalid matrix
	 */
	public int getColumnCount()
	{
		// Avoid to check lengths for null matrix references
		// We assume all rows have the same amount of columns,
		// therefore, we use the first row length
		return this.matrix != null && this.matrix.length > 0 ? this.matrix[0].length : 0;
	}
}
]]>
Clase Matrix que reprsenta una matriz matemática de números reales. Obtener código fuente.

Lista enlazada

 0 )
			{
				students.append( name );
			}
		}
		
		students.print( System.out );

		// Close the standard input
		this.input.close();
	}
}
]]>
Lee una lista de elementos de cualquier longitud y la almacena en una lista doblemente enlazada. Obtener código fuente.
Implementa una lista doblemente enlazada de textos. Obtener código fuente.

Árbol binario

Diccionario (mapa)

Lee palabras con definiciones de un archivo diccionario. Obtener código fuente.
 0 )
			{
				// The key to be inserted must go in the right subtree
				// Check if we alread have a right subtree
				if ( this.right == null )
				{
					// We do not have a right subtree, create the node there
					this.right = new Node(key, value);
				}
				else
				{
					// We already have a right subtree, insert the node there
					this.right.insert(key, value);
				}
			}
			// else: notice we insert repeated keys in the tree, so it can
			// have multiple values for a same key. If we forbid this
			// behavior, we will have a set (in mathematical sense).
		}

		/**
		 * Print this tree in in-order, which gives alphabetical order
		 * @param out
		 */
		public void print(PrintStream out)
		{
			// First, print the entire left subtree recursively
			// because all its keys are less than mine
			if ( this.left != null ) { this.left.print(out); }
			
			// Second, print myself because I am the next 
			out.printf("%s\t%s%n", this.key, this.value);
			
			// Third, print the entire right subtree recursively
			// because all its keys are greater than mine
			if ( this.right != null ) { this.right.print(out); }
		}
		
		/**
		 * Finds the value for the given key. This method
		 * will do log_2(n) comparisons if the tree is balanced
		 * 
		 * @param key The key to be searched
		 * @return The corresponding value for the key, null if
		 * the key is not found in the tree
		 */
		public String findValue(String key)
		{
			// Compare the searched key with my key. E.g: if searching
			// for "board" and I have "body", it will produce:
			// "board".compareTo("body") == -3
			int comparison = key.compareTo(this.key);
			
			// If both keys are identical
			if ( comparison == 0 )
			{
				// I have the value for that key, return it
				return this.value;
			}
			else if ( comparison < 0 )
			{
				// The searched key must be in the left subtree
				if ( this.left == null )
				{
					// But I do not have a left subtree, stop here
					return null;
				}
				else
				{
					// I have a left subtree, search there
					return this.left.findValue(key);
				}
			}
			else
			{
				// The searched key must be in the right subtree
				if ( this.right == null )
				{
					// But I do not have a right subtree, stop here
					return null;
				}
				else
				{
					// I have a right subtree, search there
					return this.right.findValue(key);
				}
			}
		} // findValue()
	} // class Node
	
	/**
	 * A tree simply has a reference to the root node.
	 * If this reference is null, the tree is empty
	 */
	private Node root = null;
	
	/**
	 * The number of elements stored in the tree
	 */
	private long count = 0;
	
	/**
	 * Return true if this tree is empty
	 * @return true if the tree is empty, false otherwise
	 */
	public boolean isEmpty()
	{
		return root == null;
	}
	
	/**
	 * Return the count of elements currently stored in the tree
	 * @return The count of elements
	 */
	public long getCount()
	{
		return this.count;
	}

	/**
	 * Insert the given pair (key,value) in the tree
	 * @param key The key to be stored
	 * @param value The value to be stored
	 */
	public void insert(String key, String value)
	{
		// The insertion depends on if the tree is empty
		if ( this.isEmpty() )
		{
			// The tree is empty, make the new pair the root
			this.root = new Node(key, value);
		}
		else
		{
			// The tree has elements, ask the root to insert
			// the pair (key,value) in its place
			this.root.insert(key, value);
		}
		
		// We have one more element stored in the tree
		++this.count;
	}
	
	/**
	 * Prints this tree to the given output in alphabetical order
	 * @param out The file were the tree will be printed
	 */
	public void print(PrintStream out)
	{
		// We can only print a tree if it has values
		if ( ! this.isEmpty() )
		{
			// Print the nodes recursively
			this.root.print(out);
		}
	}
	
	/**
	 * Finds the value for the given key. This method
	 * will do log_2(n) comparisons if the tree is balanced
	 * 
	 * @param key The key to be searched
	 * @return The corresponding value for the key, null if
	 * the key is not found in the tree
	 */
	public String findValue(String key)
	{
		// The search depends on if the tree is empty
		if ( this.isEmpty() )
		{
			// The tree is empty, we do not have a value for the key
			return null;
		}
		else
		{
			// We have elements in the tree, search for the value
			// recursively
			return this.root.findValue(key);
		}
	}
}
]]>
Implementa un árbol binario de búsqueda que guarda parejas (llave, valor). Obtener código fuente.

Programación orientada a eventos

Interfaz gráfica del usuario (GUI)

Train Lane Game

El archivo TrainLaneGame.zip contiene el proyecto completo, que incluye el código fuente, imágenes, archivos de configuración de Eclipse.

Controlador general. Obtener código fuente.
MainWindow: usa componentes gráficos para construirse. Obtener código fuente.
 cellWidth )
						{
							this.timerCarAnimation.stop();
							this.movingCarX = 0;
							this.movingCarRow = this.movingCarColumn = -1;
						}
					}
					else
					{
						g.drawImage(this.obstacle
								, x + paddingHorizontal, y + paddingVertical
								, cellWidth - 2 * paddingHorizontal, cellHeight - 2 * paddingVertical, null);
					}
				}
			}
		}
	}

	/**
	 * Invoked when an action occurs.
	 */
	@Override
	public void actionPerformed(ActionEvent event)
	{
		if ( event.getSource() == this.timerCarAnimation )
		{
			this.repaint();
		}
	}
	
	/**
	 * Invoked when the mouse button has been clicked (pressed
	 * and released) on a component.
	 */
	@Override
	public void mouseClicked(MouseEvent event)
	{
		int cellWidth = this.getWidth() / obstacleMatrix.getColumnCount();
		int cellHeight = this.getHeight() / obstacleMatrix.getRowCount();
		
		int row = event.getY() / cellHeight;
		int column = event.getX() / cellWidth;
		System.out.printf("mouseClicked(%d,%d)%n", event.getX(), event.getY());
		System.out.printf("Obstacle(%d,%d)%n", row + 1, column + 1);
		
		this.movingCarRow = row;
		this.movingCarColumn = column;
		this.movingCarX = -5;
		this.timerCarAnimation.start();
	}

	@Override
	public void mousePressed(MouseEvent event)
	{
		// TODO Auto-generated method stub
		// System.out.printf("mousePressed(%d,%d)%n", event.getX(), event.getY());
	}

	@Override
	public void mouseReleased(MouseEvent event)
	{
		// TODO Auto-generated method stub
		// System.out.printf("mouseReleased(%d,%d)%n", event.getX(), event.getY());
	}

	@Override
	public void mouseEntered(MouseEvent event)
	{
		// TODO Auto-generated method stub
		// System.out.printf("mouseEntered(%d,%d)%n", event.getX(), event.getY());
	}

	@Override
	public void mouseExited(MouseEvent event)
	{
		// TODO Auto-generated method stub
		// System.out.printf("mouseExited(%d,%d)%n", event.getX(), event.getY());
	}
}
]]>
ObstacleBoard: un componente gráfico que usa un contexto gráfico para dibujarse. Obtener código fuente.
ObstacleMatrix: Clase incompleta que representa el modelo del juego. Obtener código fuente.