Tareas prácticas de Java: para cursos y otras actividades

Tareas prácticas de Java: para cursos y otras actividades


Algunas palabras introductorias


Durante los últimos años, he estado enseñando programación Java. Con el tiempo, cambió: a veces se agregaron o desecharon diferentes partes, cambió la secuencia de temas, cambió el enfoque para construir un plan de las clases, y así sucesivamente. Es decir, el curso ha sido mejorado. Uno de los principales problemas encontrados al preparar el curso son las tareas. Sobre ellos y será discutido.

El hecho es que cada una de mis clases consta de dos partes. Al principio, actúo como profesor: te digo con ejemplos de código sobre algún tema nuevo (clases, herencia, genéricos, etc.). La segunda parte es práctica. Obviamente, no tiene sentido hablar de programación, es necesario programar. La prioridad en el aula es la resolución de problemas, es decir, programar algo de alguna manera. La programación en el aula es diferente de la programación en el hogar, ya que en la clase puede hacer una pregunta, mostrar el código, obtener una evaluación rápida del código, comentarios sobre mejoras, corrección de lo escrito. Fue muy fácil encontrar tareas para las primeras lecciones. Tareas para bucles, sentencias condicionales y OOP (por ejemplo, escriba la clase "Perro" o la clase "Vector"). Servicios como leetcode incluso le permiten verificar la exactitud de la resolución de tales problemas de forma inmediata, en línea. Pero, ¿qué tareas se les debe dar a los estudiantes en una lección dedicada a las colecciones? Corrientes? ¿Qué pasa con las anotaciones? Durante varios años, se me ocurrió o revisé varias de esas tareas, y este artículo, de hecho, es una colección de estos problemas (se adjunta una solución a algunos problemas).

Por supuesto, todas las tareas ya han aparecido en alguna parte. Sin embargo, este artículo está dirigido a profesores de cursos de programación (para lenguajes similares a Java, la mayoría de las tareas lo harán), o aquellos que enseñan programación en privado. Estas tareas se pueden usar "fuera de la caja" en sus clases. Los estudiantes de Java también pueden intentar resolverlos. Pero tales decisiones requieren verificación y evaluación de terceros.

Algunas de las tareas más simples que todos han estado usando durante décadas, también las incluí en este artículo. Quizás, para no comenzar inmediatamente con clases abstractas.

¡Cualquier idea y sugerencia es bienvenida!

Lista de tareas


Los fundamentos


1.0. Máximo, mínimo y promedio
1.1 Clasificación de matriz
1.2 Encontrar primos
1.3 Eliminar de una matriz

Conceptos básicos de OOP


2.0 Diseñando y creando una clase que describe un vector
2.1 Generación de un elemento aleatorio con un peso
2.2 Lista vinculada

Recursividad


3.0 Búsqueda binaria
3.1 Encuentra la raíz de la ecuación
3.2 Árbol de búsqueda binaria

Herencia


4.0 Implemente una jerarquía de clases que describa formas tridimensionales
4.1 Implementar una jerarquía de clases que describa figuras tridimensionales - 2
4.2 Implemente una jerarquía de clases que describa formas tridimensionales - 3
4.3 Implemente una jerarquía de clases que describa formas tridimensionales - 4

Líneas


5.0 Diccionario de frecuencia de letras

Clases abstractas e interfaces


6.0. Convertidor de temperatura
6.1. Stringbuilder con soporte para deshacer
6.2. Statebuilding Stringbuilder (patrón de observador)
6.4. Llenar una matriz con función

Colecciones


7.0 Diccionario de frecuencia de palabras
7.1. Colección sin duplicados
7.2. ArrayList y LinkedList
7.3. Escribir un iterador sobre una matriz
7.4. Escribir un iterador sobre una matriz bidimensional
7.5. Un iterador aún más complejo
7.6. Iterador sobre dos iteradores
7.7. Elementos de conteo
7.8. Cambiar claves y valores en Mapa

Multithreading


8.0. Estados
8.1. Hilo de sincronización
8.2. Fabricante de consumo

Anotaciones


9.0. Anotación personalizada: creación y uso con reflexión

Tareas finales y otras


10,0 Número de restricciones viales
10.1 Búsqueda de Wikipedia. En el programa de consola
10.2 Tarea final: utilidad de consola para descargar archivos a través de HTTP
10.3 Tarea final - weather Telegram-bot
10.4 Tarea final: reconocimiento de escritura a mano

Los fundamentos


1.0. Máximo, mínimo y promedio


Desafío:

Rellene la matriz con números aleatorios e imprima el valor máximo, mínimo y promedio.

Para generar un número aleatorio, use el método Math.random () , que devuelve un valor en el intervalo [0, 1].

Solución:

public static void main(String[] args) { int n = 100; double[] array = new double[n]; for (int i = 0; i < array.length; i++) { array[i] = Math.random(); } double max = array[0]; //      double min = array[0]; double avg = 0; for (int i = 0; i < array.length; i++) { if(max < array[i]) max = array[i]; if(min > array[i]) min = array[i]; avg += array[i]/array.length; } System.out.println("max = " + max); System.out.println("min = " + min); System.out.println("avg = " + avg); } 

1.1. Implemente un algoritmo de clasificación de burbujas para ordenar una matriz


Solución:

  for (int i = 0; i < array.length; i++) { for (int j = 0; j < array.length - i - 1; j++) { if (array[j] > array[j + 1]) { double temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; } } } for (int i = 0; i < array.length; i++) { System.out.println(array[i]); } 

1.2. Buscar números primos


Desafío:

Escriba un programa que imprima números primos en la consola entre [2, 100].
Use el operador % (resto de la división) y bucles para resolver este problema.

Solución:

  for(int i = 2; i <= 100; i ++){ boolean isPrime = true; for(int j = 2; j < i; j++){ if(i % j == 0){ isPrime = false; break; } } if(isPrime){ System.out.println(i); } } 

O usando lazos de etiquetas :

  out_for: for (int i = 2; i <= 100; i++) { for (int j = 2; j < i; j++) { if (i % j == 0) { continue out_for; } } System.out.println(i); } 

1.3. Eliminar de la matriz


Desafío:

Dado un conjunto de enteros y otro entero. Elimine todas las apariciones de este número de la matriz (no debe haber huecos).

Solución:

  public static void main(String[] args) { int test_array[] = {0,1,2,2,3,0,4,2}; /* Arrays.toString: . https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html */ System.out.println(Arrays.toString(removeElement(test_array, 3))); } public static int[] removeElement(int[] nums, int val) { int offset = 0; for(int i = 0; i< nums.length; i++){ if(nums[i] == val){ offset++; } else{ nums[i - offset] = nums[i]; } } // Arrays.copyOf     nums    //   nums.length - offset return Arrays.copyOf(nums, nums.length - offset); } 

Puede escribir un método para cortar la cola de la matriz usted mismo, pero vale la pena señalar que el método estándar funcionará más rápido:

 public static int[] removeElement(int[] nums, int val) { int offset = 0; for(int i = 0; i< nums.length; i++){ if(nums[i] == val){ offset++; } else{ nums[i - offset] = nums[i]; } } int[] newArray = new int[nums.length - offset]; for(int i = 0; i < newArray.length; i++){ newArray[i] = nums[i]; } return newArray; } 

Sin embargo, si sigue este camino, primero puede crear una matriz de la longitud deseada y luego llenarla:

 public static int[] removeElement(int[] nums, int val) { int count = 0; //      for (int i = 0; i < nums.length; i++) { if (nums[i] != val) { count++; } } int[] newArray = new int[count]; int offset = 0; //      , //       for(int i = 0; i< nums.length; i++){ if(nums[i] == val){ offset++; } else{ newArray[i - offset] = nums[i]; } } return newArray; } 


2.0. Diseñando y creando una clase vectorial


Desafío:

Cree una clase que describa un vector (en un espacio tridimensional).

El debe tener:

  • constructor con parámetros en forma de una lista de coordenadas x, y, z
  • Método que calcula la longitud de un vector. La raíz se puede calcular usando Math.sqrt ():

  • método de cálculo de producto escalar:

  • Método para calcular un producto vectorial con otro vector:

  • Método que calcula el ángulo entre los vectores (o el coseno del ángulo): el coseno del ángulo entre los vectores es igual al producto escalar de los vectores dividido por el producto de los módulos (longitudes) de los vectores:

  • métodos para suma y diferencia:


  • Un método estático que toma un número entero N y devuelve una matriz de vectores aleatorios de tamaño N.

Si el método devuelve un vector, debería devolver un nuevo objeto y no cambiar el de base. Es decir, debe implementar la plantilla " Objeto inmutable "

Solución:

 public class Vector { //    private double x; private double y; private double z; //    public Vector(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } //  .     public double length() { return Math.sqrt(x * x + y * y + z * z); } // ,    public double scalarProduct(Vector vector) { return x * vector.x + y * vector.y + z * vector.z; } // ,    public Vector crossProduct(Vector vector) { return new Vector( y * vector.z - z * vector.y, z * vector.x - x * vector.z, x * vector.y - y * vector.x); } //     public double cos(Vector vector) { //       // multiply  length return scalarProduct(vector) / (length() * vector.length()); } public Vector add(Vector vector) { return new Vector( x + vector.x, y + vector.y, z + vector.z ); } public Vector subtract(Vector vector) { return new Vector( x - vector.x, y - vector.y, z - vector.z ); } public static Vector[] generate(int n){ Vector[] vectors = new Vector[n]; for(int i =0; i < n; i++){ vectors[i] = new Vector(Math.random(), Math.random(), Math.random()); } return vectors; } @Override public String toString() { return "Vector{" + "x=" + x + ", y=" + y + ", z=" + z + '}'; } } 

Puedes usar esta clase así:

 public static void main(String[] args) { Vector[] vectors = Vector.generate(10); System.out.println(vectors[0]); System.out.println(vectors[1]); System.out.println(vectors[0].length()); System.out.println(vectors[0].scalarProduct(vectors[1])); System.out.println(vectors[0].crossProduct(vectors[1])); System.out.println(vectors[0].cos(vectors[1])); System.out.println(vectors[0].add(vectors[1])); System.out.println(vectors[0].subtract(vectors[1])); } 

Puede generalizar esta solución y escribir la clase Vector para una dimensión arbitraria:

 public class Vector { //    ,    private double values[]; public Vector(double[] values) { this.values = values; } //  .     public double length() { double sum = 0; for (int i = 0; i < values.length; i++) { sum += values[i] * values[i]; } return Math.sqrt(sum); } // ,    public double scalarProduct(Vector vector) { double res = 0; for (int i = 0; i < values.length; i++) { res += values[i] * vector.values[i]; } return res; } //     // public double crossProduct(Vector vector) { // // } //     public double cos(Vector vector) { return scalarProduct(vector) / (length() * vector.length()); } public Vector add(Vector vector) { double[] another = new double[values.length]; for (int i = 0; i < values.length; i++) { another[i] = values[i] + vector.values[i]; } return new Vector(another); } public Vector subtract(Vector vector) { double[] another = new double[values.length]; for (int i = 0; i < values.length; i++) { another[i] = values[i] - vector.values[i]; } return new Vector(another); } //   private static double[] generateRandomArray(int length) { double[] array = new double[length]; for (int i = 0; i < array.length; i++) { array[i] = Math.random(); } return array; } public static Vector[] generate(int n, int dimension) { Vector[] vectors = new Vector[n]; for (int i = 0; i < n; i++) { vectors[i] = new Vector(generateRandomArray(dimension)); } return vectors; } } 


2.1. Generando un artículo aleatorio con un peso


Desafío:

Escriba una clase cuyo constructor tome dos matrices: una matriz de valores y una matriz de pesos de valores.
La clase debe contener un método que devolverá un elemento de la primera matriz al azar, teniendo en cuenta su peso.
Un ejemplo:
Dado un conjunto [1, 2, 3] y un conjunto de pesos [1, 2, 10].
En promedio, el valor "1" debería devolver 2 veces menos que el valor "2" y diez veces menos frecuente que el valor "3" .

Solución:

 /*     :  ,   —    .   ""     ,       ,        : |-|--|----------| 0-1--3----------13 ^ */ class RandomFromArray { private int[] values; //  private int[] weights; //  private int[] ranges; //    private int sum; //     public RandomFromArray(int[] values, int[] weights) { this.values = values; this.weights = weights; ranges = new int[values.length]; //     sum = 0; for (int weight : weights) { sum += weight; } //  ranges,   int lastSum = 0; for (int i = 0; i < ranges.length; i++) { ranges[i] = lastSum; lastSum += weights[i]; } } /*  ranges  ,        [0;sum],   ,   : */ public int getRandom() { int random = (int) (Math.random() * (sum - 1)); int ourRangeIndex = 0; for (int i = 0; i < ranges.length; i++) { if (ranges[i] > random) { break; } ourRangeIndex = i; } return values[ourRangeIndex]; } } 

Pero, dado que la matriz de rangos está ordenada, puede (y debería) usar una búsqueda binaria:

 public int getRandom() { int random = (int) (Math.random() * (sum - 1)); int index = Arrays.binarySearch(ranges, random); return values[index >= 0 ? index : -index - 2]; } 

Hay otra solución a este problema. Puede crear una matriz cuyo tamaño sea igual a la suma de todos los pesos. Luego, la elección de un elemento aleatorio se reduce a generar un índice aleatorio. Es decir, si se proporciona una matriz de valores [1, 2, 3] y una matriz de pesos [1, 2, 10], puede crear inmediatamente una matriz [1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] y extraer un elemento aleatorio de él:

 class RandomFromArray { private int[] extended_values; //  public RandomFromArray(int[] values, int[] weights) { //     int sum = 0; for (int weight : weights) { sum += weight; } extended_values = new int[sum]; int cursor = 0; for(int i = 0; i < weights.length; i++){ for(int j = 0; j < weights[i]; j++){ extended_values[cursor++] = values[i]; } } } /*  extended_values  ,        [0; extended_values.length) */ public int getRandom() { int random = (int) (Math.random() * ( extended_values.length - 1)); return extended_values[random]; } } 

Esta solución tiene una ventaja: el tiempo para extraer un elemento aleatorio O (1) , en contraste con log (n) en la solución anterior. Sin embargo, requiere mucha memoria:



2.2. Lista vinculada


Otra tarea que a menudo doy es la implementación de una lista vinculada. Se puede dar en su forma más simple (implemente add () y get () solamente ), o puede solicitar implementar java.util.List .
No me detendré en esto en detalle, hay muchos artículos sobre la implementación de una lista vinculada en Java en Habr, por ejemplo este:
Estructuras de datos en imágenes. LinkedList

3.0 Búsqueda binaria


Desafío:

Escriba un método que verifique si el elemento dado está en la matriz o no.
Use la enumeración y la búsqueda binaria para resolver este problema.
Compare el tiempo de ejecución de ambas soluciones para matrices grandes (por ejemplo, elementos 100000000).
Solución:

  /*  ,   .    ,  -1 */ public static int bruteForce(double[] array, double key) { for (int i = 0; i < array.length; i++) { if (array[i] == key) return i; } return -1; } /*   */ public static int binarySearchRecursively(double[] sortedArray, double key) { return binarySearchRecursively(sortedArray, key, 0, sortedArray.length); } /** *    {@link #binarySearchRecursively(double[], double)} * *    ,   ,   " ", *      .    low  high * * @param sortedArray   * @param key   * @param low     * @param high     * @return   */ private static int binarySearchRecursively (double[] sortedArray, double key, int low, int high) { int middle = (low + high) / 2; //  if (high < low) { //    return -1; } if (key == sortedArray[middle]) { //   return middle; } else if (key < sortedArray[middle]) { //     return binarySearchRecursively( sortedArray, key, low, middle - 1); } else { return binarySearchRecursively( //     sortedArray, key, middle + 1, high); } } //     private static double[] generateRandomArray(int length) { double[] array = new double[length]; for (int i = 0; i < array.length; i++) { array[i] = Math.random(); } return array; } public static void main(String[] args) { double[] array = generateRandomArray(100000000); Arrays.sort(array); //    /*  ,       ,   benchmarks . https://habr.com/ru/post/349914/     */ long time = System.currentTimeMillis(); //  , unix-time bruteForce(array, 0.5); System.out.println(System.currentTimeMillis() - time); time = System.currentTimeMillis(); binarySearchRecursively(array, 0.5); System.out.println(System.currentTimeMillis() - time); } 

3.1. Encuentra la raíz de la ecuación


Desafío:

Encuentra la raíz de la ecuación

en el segmento [0; 10] con una precisión en x no peor que 0.001. Se sabe que la raíz es única en este intervalo.
Use el método de reducción a la mitad (y la recursividad) para esto.

Solución:

  //   public static double func(double x){ return Math.cos(Math.pow(x, 5)) + Math.pow(x, 4) - 345.3 * x - 23; } //   public static double solve(double start, double end){ if(end - start <= 0.001){ return start; } double x = start + (end - start) / 2; if(func(start) * func(x) > 0){ return solve(x, end); } else { return solve(start, x); } } public static void main(String[] args) { System.out.println(solve(0, 10)); } 

Nota: si queremos lograr la precisión requerida no en x , en y , entonces la condición en el método debe reescribirse:

  if(Math.abs(func(start)- func(end)) <= 0.001){ return start; } 

Un pequeño truco: considerando que el conjunto de valores dobles es finito (hay dos valores adyacentes entre los cuales no hay valores dobles), reescribe la condición para salir de la recursividad de la siguiente manera:

  double x = start + (end - start) / 2; if(x == end || x == start){ return x; } 

Por lo tanto, obtenemos la máxima precisión que generalmente se puede obtener con este enfoque.

3.2. Árbol de búsqueda binaria


Implementar un árbol de búsqueda binario es una tarea excelente. Generalmente lo doy cuando se trata de recursividad.
No escribiré mucho sobre esto, hay muchos artículos / implementaciones de un tipo muy diferente:
Estructuras de datos: árboles binarios.
Árbol binario, implementación rápida
Implementación de Java de un árbol binario hash

Herencia


4.0. Implemente una jerarquía de clases que describa formas tridimensionales.


Desafío:

Implemente una jerarquía de clases:


La clase Box es un contenedor; puede contener otras formas. El método add () toma forma como entrada. Necesitamos agregar nuevas formas hasta que obtengamos espacio para ellas en la Caja (solo consideraremos el volumen, ignorando la forma. Supongamos que vertimos líquido). Si no hay suficiente espacio para agregar una nueva forma, entonces el método debería devolver falso .

Solución:

 class Shape { private double volume; public Shape(double volume) { this.volume = volume; } public double getVolume() { return volume; } } class SolidOfRevolution extends Shape { private double radius; public SolidOfRevolution(double volume, double radius) { super(volume); this.radius = radius; } public double getRadius() { return radius; } } class Ball extends SolidOfRevolution { //   public Ball(double radius) { super(Math.PI * Math.pow(radius, 3) * 4 / 3, radius); } } class Cylinder extends SolidOfRevolution { //   private double height; public Cylinder(double radius, double height) { super(Math.PI * radius * radius * height, radius); this.height = height; } } class Pyramid extends Shape{ private double height; private double s; //   public Pyramid(double height, double s) { super(height * s * 4 / 3); this.height = height; this.s = s; } } class Box extends Shape { private ArrayList<Shape> shapes = new ArrayList<>(); private double available; public Box(double available) { super(available); this.available = available; } public boolean add(Shape shape) { if (available >= shape.getVolume()) { shapes.add(shape); available -= shape.getVolume(); return true; } else { return false; } } } public class Main { public static void main(String[] args) { Ball ball = new Ball(4.5); Cylinder cylyinder = new Cylinder(2, 2); Pyramid pyramid = new Pyramid(100, 100); Box box = new Box(1000); System.out.println(box.add(ball)); // ok System.out.println(box.add(cylyinder)); // ok System.out.println(box.add(pyramid)); // failed } } 

Para no volver a esta tarea, a continuación se describen varias variaciones más de esta tarea.

4.1. Implemente una jerarquía de clases que describa formas tridimensionales - 2


Desafío:

Implemente la misma jerarquía de clases, pero haga algunas clases abstractas.

Solución:

 abstract class Shape { public abstract double getVolume(); } abstract class SolidOfRevolution extends Shape { protected double radius; public SolidOfRevolution(double radius) { this.radius = radius; } public double getRadius() { return radius; } } class Ball extends SolidOfRevolution { //   @Override public double getVolume() { return Math.PI * Math.pow(radius, 3) * 4 / 3; } public Ball(double radius) { super(radius); } } class Cylinder extends SolidOfRevolution { //   private double height; public Cylinder(double radius, double height) { super(radius); this.height = height; } @Override public double getVolume() { return Math.PI * radius * radius * height; } } class Pyramid extends Shape { private double height; private double s; //   public Pyramid(double height, double s) { this.height = height; this.s = s; } @Override public double getVolume() { return height * s * 4 / 3; } } class Box extends Shape { private ArrayList<Shape> shapes = new ArrayList<>(); private double available; private double volume; public Box(double available) { this.available = available; this.volume = available; } public boolean add(Shape shape) { if (available >= shape.getVolume()) { shapes.add(shape); available -= shape.getVolume(); return true; } else { return false; } } @Override public double getVolume() { return volume; } } public class Main { public static void main(String[] args) { Ball ball = new Ball(4.5); Cylinder cylyinder = new Cylinder(2, 2); Pyramid pyramid = new Pyramid(100, 100); Box box = new Box(1000); System.out.println(box.add(ball)); // ok System.out.println(box.add(cylyinder)); // ok System.out.println(box.add(pyramid)); // failed } } 

4.2. Implemente una jerarquía de clases que describa formas tridimensionales: 3


Desafío:

Implemente la misma jerarquía de clases, pero utilizando interfaces.
Además, se alienta a los estudiantes a implementar la interfaz Comparable .

Solución:

 interface Shape extends Comparable<Shape>{ double getVolume(); @Override default int compareTo(Shape other) { return Double.compare(getVolume(), other.getVolume()); } } abstract class SolidOfRevolution implements Shape { protected double radius; public SolidOfRevolution(double radius) { this.radius = radius; } public double getRadius() { return radius; } } class Ball extends SolidOfRevolution { //   @Override public double getVolume() { return Math.PI * Math.pow(radius, 3) * 4 / 3; } public Ball(double radius) { super(radius); } } class Cylinder extends SolidOfRevolution { //   private double height; public Cylinder(double radius, double height) { super(radius); this.height = height; } @Override public double getVolume() { return Math.PI * radius * radius * height; } } class Pyramid implements Shape { private double height; private double s; //   public Pyramid(double height, double s) { this.height = height; this.s = s; } @Override public double getVolume() { return height * s * 4 / 3; } } class Box implements Shape { private ArrayList<Shape> shapes = new ArrayList<>(); private double available; private double volume; public Box(double available) { this.available = available; this.volume = available; } public boolean add(Shape shape) { if (available >= shape.getVolume()) { shapes.add(shape); available -= shape.getVolume(); return true; } else { return false; } } @Override public double getVolume() { return volume; } public ArrayList<Shape> getShapes() { return shapes; } } public class Main { public static void main(String[] args) { Ball ball = new Ball(4.5); Cylinder cylyinder = new Cylinder(2, 2); Pyramid pyramid = new Pyramid(100, 100); Box box = new Box(1000); System.out.println(box.add(ball)); // ok System.out.println(box.add(cylyinder)); // ok System.out.println(box.add(pyramid)); // failed // Sorting: ArrayList<Shape> shapes = box.getShapes(); Collections.sort(shapes); // sorted by Volume! } } 

4.3. Implemente una jerarquía de clases que describa formas tridimensionales - 4


Desafío:

Agregue una forma de rotación a la jerarquía de clases para una función arbitraria. Puede calcular el volumen aproximado utilizando una determinada integral. Dado que el volumen de la figura de rotación alrededor del eje x es


Y la integral es
imagen


Luego puede escribir una implementación del método de rectángulo :

 class SolidRevolutionForFunction extends SolidOfRevolution { private Function<Double, Double> function; private double a; private double b; public SolidRevolutionForFunction( Function<Double, Double> function, double a, double b) { super(b - a); this.function = function; this.a = a; this.b = b; } @Override public double getVolume() { double sum = 0; int iterations = 10000; double delta = (b - a)/iterations; for(int i = 0; i < iterations; i++){ double x = a + ((b - a) * i/iterations); sum += Math.pow(function.apply(x), 2) * delta; } return Math.PI * sum; } } 

 public static void main(String[] args) { Shape shape = new SolidRevolutionForFunction(new Function<Double, Double>() { @Override public Double apply(Double x) { return Math.cos(x); } }, 0, 10); System.out.println(shape.getVolume()); } 

Por supuesto, no tenemos en cuenta la precisión de los cálculos aquí, y no seleccionamos el número de particiones para lograr la precisión necesaria, pero esta es una tarea de programación, no métodos numéricos, por lo que omitimos esto en el aula.

Líneas


Puede encontrar muchas tareas por línea. Generalmente doy estos:

  • Escriba un método para encontrar la cadena más larga en una matriz.
  • Escriba un método que verifique si una palabra es un palíndromo .
  • Escriba un método que reemplace en el texto todas las apariciones de la palabra Bulk "byaka" con "[recortar
    censurado] ".
  • Hay dos lineas. Escriba un método que devuelva el número de ocurrencias de una fila en otra.

No describiré las soluciones a tales problemas, y también hay una gran cantidad de tareas para las cadenas.

De los más interesantes, me gusta este:

5.0. Diccionario de frecuencias de letras del alfabeto ruso (o inglés).


Desafío:

Construya un diccionario de frecuencia de letras del alfabeto ruso (o inglés). Omitimos el problema de elegir y analizar el cuerpo del lenguaje, será suficiente para tomar el texto de corta duración).

Solución:

  /** *      ,  *      , *    Map. * *    Map.Entry<Character, Integer>, *     (Character) * * @param text -  */ void buildDictionaryWithMap(String text){ text = text.toLowerCase(); Map<Character, Integer> map = new HashMap<>(); for(int i = 0; i < text.length(); i++){ char ch = text.charAt(i); //     - if((ch >= '' && ch <= '') || ch == ''){ map.compute(ch, (character, integer) -> integer == null ? 1 : integer + 1); } } ArrayList<Map.Entry<Character, Integer>> entries = new ArrayList<>(map.entrySet()); entries.sort((o1, o2) -> Character.compare(o1.getKey(), o2.getKey())); for(Map.Entry<Character, Integer> entry : entries){ System.out.println(entry.getKey() + " " + entry.getValue()); } } 

Más o menos:

  /** *   Map. *    ,    *      *  * @param text */ void buildDictionary(String text){ text = text.toLowerCase(); int[] result = new int['' - '' + 1]; for(int i = 0; i < text.length(); i++){ char ch = text.charAt(i); if(ch >= '' && ch <= ''){ result[ch - '']++; } } for(int i = 0; i < result.length; i++){ System.out.println((char) (i + '') + " = " + result[i]); } } 

Clases abstractas e interfaces


6.0. Convertidor de temperatura


Desafío:

Escriba una clase de BaseConverter para convertir de grados Celsius a
Kelvin , Fahrenheit , etc. El método debe tener un método de conversión , que
y hace la conversión.

Solución:

 interface Converter { double getConvertedValue(double baseValue); } class CelsiusConverter implements Converter { @Override public double getConvertedValue(double baseValue) { return baseValue; } } class KelvinConverter implements Converter { @Override public double getConvertedValue(double baseValue) { return baseValue + 273.15; } } class FahrenheitConverter implements Converter { @Override public double getConvertedValue(double baseValue) { return 1.8 * baseValue + 32; } } public class Main { public static void main(String[] args) { double temperature = 23.5; System.out.println("t = " + new CelsiusConverter().getConvertedValue(temperature)); System.out.println("t = " + new KelvinConverter().getConvertedValue(temperature)); System.out.println("t = " + new FahrenheitConverter().getConvertedValue(temperature)); } } 

Además, puede solicitar implementar un método de fábrica , algo como esto:

 interface Converter { double getConvertedValue(double baseValue); public static Converter getInstance(){ Locale locale = Locale.getDefault(); String[] fahrenheitCountries = {"BS", "US", "BZ", "KY", "PW"}; boolean isFahrenheitCountry = Arrays.asList(fahrenheitCountries).contains(locale.getCountry()); if(isFahrenheitCountry){ return new FahrenheitConverter(); } else { return new CelsiusConverter(); } } } 

6.1. Stringbuilder con soporte para deshacer


Tarea:

escriba su clase StringBuilder con soporte para la operación de deshacer . Para hacer esto, delegue todos los métodos al StringBuilder estándar , y en su propia clase almacene una lista de todas las operaciones para que se ejecute undo () . Esta será la implementación de la plantilla del equipo .

Solución:

 /** * StringBuilder    undo * java.lang.StringBuilder —    <b>final</b>, *   ,  . */ class UndoableStringBuilder { private interface Action{ void undo(); } private class DeleteAction implements Action{ private int size; public DeleteAction(int size) { this.size = size; } public void undo(){ stringBuilder.delete( stringBuilder.length() - size, stringBuilder.length()); } } private StringBuilder stringBuilder; //  /** * ,   . *     append,    *  "delete".   undo()  *  . */ private Stack<Action> actions = new Stack<>(); //  public UndoableStringBuilder() { stringBuilder = new StringBuilder(); } /** see {@link java.lang.AbstractStringBuilder#reverse()}  ,   reverse(),      —  reverse().    . */ public UndoableStringBuilder reverse() { stringBuilder.reverse(); Action action = new Action(){ public void undo() { stringBuilder.reverse(); } }; actions.add(action); return this; } public UndoableStringBuilder append(String str) { stringBuilder.append(str); Action action = new Action(){ public void undo() { stringBuilder.delete( stringBuilder.length() - str.length() -1, stringBuilder.length()); } }; actions.add(action); return this; } // .....    append   (. )...... public UndoableStringBuilder appendCodePoint(int codePoint) { int lenghtBefore = stringBuilder.length(); stringBuilder.appendCodePoint(codePoint); actions.add(new DeleteAction(stringBuilder.length() - lenghtBefore)); return this; } public UndoableStringBuilder delete(int start, int end) { String deleted = stringBuilder.substring(start, end); stringBuilder.delete(start, end); actions.add(() -> stringBuilder.insert(start, deleted)); return this; } public UndoableStringBuilder deleteCharAt(int index) { char deleted = stringBuilder.charAt(index); stringBuilder.deleteCharAt(index); actions.add(() -> stringBuilder.insert(index, deleted)); return this; } public UndoableStringBuilder replace(int start, int end, String str) { String deleted = stringBuilder.substring(start, end); stringBuilder.replace(start, end, str); actions.add(() -> stringBuilder.replace(start, end, deleted)); return this; } public UndoableStringBuilder insert(int index, char[] str, int offset, int len) { stringBuilder.insert(index, str, offset, len); actions.add(() -> stringBuilder.delete(index, len)); return this; } public UndoableStringBuilder insert(int offset, String str) { stringBuilder.insert(offset, str); actions.add(() -> stringBuilder.delete(offset, str.length())); return this; } // .....    insert   (. )...... public void undo(){ if(!actions.isEmpty()){ actions.pop().undo(); } } public String toString() { return stringBuilder.toString(); } } 

6.2. Statebuilding Stringbuilder (patrón de observador)


Tarea:

escriba su clase StringBuilder, con la capacidad de notificar a otros objetos de un cambio en su estado. Para hacer esto, delegue todos los métodos al StringBuilder estándar e implemente el patrón de diseño de Observer en su propia clase .

Solución:

 /** .  ,      UndoableStringBuilder,   onChange(). */ interface OnStringBuilderChangeListener { void onChange(OvservableStringBuilder stringBuilder); } class OvservableStringBuilder { // ,     private OnStringBuilderChangeListener onChangeListener; //  private StringBuilder stringBuilder; //   onChangeListener public void setOnChangeListener(OnStringBuilderChangeListener onChangeListener) { this.onChangeListener = onChangeListener; } public OvservableStringBuilder() { stringBuilder = new StringBuilder(); } private void notifyOnStringBuilderChangeListener(){ if(onChangeListener != null){ onChangeListener.onChange(this); } } public OvservableStringBuilder append(Object obj) { stringBuilder.append(obj); notifyOnStringBuilderChangeListener(); return this; } public OvservableStringBuilder replace(int start, int end, String str) { stringBuilder.replace(start, end, str); notifyOnStringBuilderChangeListener(); return this; } public OvservableStringBuilder insert(int index, char[] str, int offset, int len) { stringBuilder.insert(index, str, offset, len); notifyOnStringBuilderChangeListener(); return this; } // .......    .......... public String toString() { return stringBuilder.toString(); } } /**   OnStringBuilderChangeListener */ class MyListener implements OnStringBuilderChangeListener { /*   onChange    stringBuilder,  "" */ public void onChange(OvservableStringBuilder stringBuilder) { System.out.println("CHANGED: " + stringBuilder.toString()); } } public class Main { public static void main(String[] strings) { OvservableStringBuilder UndoableStringBuilder = new OvservableStringBuilder(); UndoableStringBuilder.setOnChangeListener(new MyListener()); UndoableStringBuilder.append("Hello"); UndoableStringBuilder.append(", "); UndoableStringBuilder.append("World!"); } } 

6.3. Filtro


Problema:

Método de escritura de filtro , que recibe la matriz de entrada (de cualquier tipo), y la aplicación de la interfaz del filtro método c aplicar (Object o) , para eliminar de la matriz superfluo
Verifique cómo funciona en cadenas u otros objetos.

Solución: por lo
general, doy esta tarea antes que los genéricos, para que los estudiantes escriban un método sin ellos usando Object:

 interface Filter { boolean apply(Object o); } public class Main { public static Object[] filter(Object[] array, Filter filter) { int offset = 0; for(int i = 0; i< array.length; i++){ if(!filter.apply(array[i])){ offset++; } else{ array[i - offset] = array[i]; } } // Arrays.copyOf     array    //   array.length - offset return Arrays.copyOf(array, array.length - offset); } public static void main(String[] args) { String array[] = new String[]{"1rewf ", "feefewf", "a", null, "1"}; String[] newArray = (String[]) filter(array, new Filter() { @Override public boolean apply(Object o) { return o != null; } }); } } 

Pero, es posible con genéricos. Entonces puede usar la función estándar :

 public class Main { public static <T> T[] filter(T[] array, Function<? super T, Boolean> filter) { int offset = 0; for (int i = 0; i < array.length; i++) { if (!filter.apply(array[i])) { offset++; } else { array[i - offset] = array[i]; } } // Arrays.copyOf     array    //   array.length - offset return Arrays.copyOf(array, array.length - offset); } public static void main(String[] args) { String array[] = new String[]{"1rewf ", "feefewf", "a", null, "1"}; String[] newArray = filter(array, s -> s != null); } } 

6.4. Llenado de matriz


Una tarea algo similar a la anterior:
escriba un método de relleno que acepte una matriz de objetos y una implementación de la interfaz Function (o la suya propia).
El método de relleno debe llenar la matriz, obteniendo el nuevo valor por índice utilizando la implementación de la interfaz de función. Es decir, quieres usarlo así:

 public static void main(String[] args) { Integer[] squares = new Integer[100]; fill(squares, integer -> integer * integer); // 0, 1, 4, 9, 16 ..... } 

Solución:

 public static <T> void fill(T[] objects, Function<Integer, ? extends T> function) { for(int i = 0; i < objects.length; i++){ objects[i] = function.apply(i); } } 

Colecciones



7.0 Diccionario de frecuencia de palabras


vea el problema sobre el diccionario de frecuencias de letras del alfabeto

7.1. Colección sin duplicados


Tarea:

escriba un método que reciba una colección de objetos como entrada y devuelva una colección sin duplicados.

Solución:

  public static <T> Collection<T> removeDuplicates(Collection<T> collection) { return new HashSet<>(collection); //   ! } 

7.2. ArrayList y LinkedList


Escriba un método que agregue 1,000,000 de elementos a una ArrayList y LinkedList. Escriba otro método que seleccione un elemento al azar 100,000 veces de la lista completa. Mida el tiempo dedicado a ello. Compare los resultados y sugiera por qué lo son.

Solución:

  public static void compare2Lists() { ArrayList<Double> arrayList = new ArrayList<>(); LinkedList<Double> linkedList = new LinkedList<>(); final int N = 1000000; final int M = 1000; for (int i = 0; i < N; i++) { arrayList.add(Math.random()); linkedList.add(Math.random()); } long startTime = System.currentTimeMillis(); for (int i = 0; i < M; i++) { arrayList.get((int) (Math.random() * (N - 1))); } System.out.println(System.currentTimeMillis() - startTime); startTime = System.currentTimeMillis(); for (int i = 0; i < M; i++) { linkedList.get((int) (Math.random() * (N - 1))); } System.out.println(System.currentTimeMillis() - startTime); } 

7.3. Escribir un iterador sobre una matriz


Solución:

 class ArrayIterator<T> implements Iterator<T>{ private T[] array; private int index = 0; public ArrayIterator(T[] array) { this.array = array; } @Override public boolean hasNext() { return index < array.length; } @Override public T next() { if(!hasNext()) throw new NoSuchElementException(); return array[index++]; } } 

7.4. Iterador de matriz bidimensional


Tarea:

escriba un iterador sobre una matriz bidimensional.

Solución:

 class Array2d<T> implements Iterable<T>{ private T[][] array; public Array2d(T[][] array) { this.array = array; } @Override public Iterator<T> iterator() { return new Iterator<T>() { private int i, j; @Override public boolean hasNext() { for(int i = this.i; i< array.length; i++){ for(int j = this.j; j< array[i].length; j++){ return true; } } return false; } @Override public T next() { if(!hasNext()) throw new NoSuchElementException(); T t = array[i][j]; j++; for(int i = this.i; i< array.length; i++){ for(int j = (i == this.i ? this.j : 0); j< array[i].length; j++){ this.i = i; this.j = j; return t; } } return t; } }; } } 


7.5. Un iterador aún más complejo


Me gusta esta tarea Solo llega a unos pocos estudiantes en el grupo que son relativamente fáciles de hacer frente a tareas anteriores.

Tarea:

Dan iterador. El método next () devuelve un String o un iterador de la misma estructura (es decir, que nuevamente devuelve String o el mismo iterador). Escriba en la parte superior de este iterador otro, ya "plano".

Solución en las pilas:

 public class DeepIterator implements Iterator<String> { private Stack<Iterator> iterators; private String next; private boolean hasNext; public DeepIterator(Iterator<?> iterator) { this.iterators = new Stack<Iterator>(); iterators.push(iterator); updateNext(); } @Override public boolean hasNext() { return hasNext; } private void updateNext(){ if(iterators.empty()){ next = null; hasNext = false; return; } Iterator current = iterators.peek(); if (current.hasNext()) { Object o = current.next(); if (o instanceof String) { next = (String) o; hasNext = true; } else if (o instanceof Iterator) { Iterator iterator = (Iterator) o; iterators.push(iterator); updateNext(); } else { throw new IllegalArgumentException(); } } else { iterators.pop(); updateNext(); } } @Override public String next() throws NoSuchElementException { if(!hasNext){ throw new NoSuchElementException(); } String nextToReturn = next; updateNext(); return nextToReturn; } @Override public void remove() { throw new UnsupportedOperationException(); } } 

La solución recursiva:

 /** * @author Irina Poroshina */ class DeepIterator implements Iterator<String> { private Iterator subIter; private DeepIterator newIter; public DeepIterator(Iterator iniIter) { this.subIter = iniIter; } @Override public boolean hasNext() { if (subIter.hasNext()) return true; if (newIter != null) return newIter.hasNext(); return false; } @Override public String next() { if(!hasNext()) throw new NoSuchElementException(); Object obj = null; if (newIter != null && newIter.hasNext()) obj = newIter.next(); if (subIter.hasNext() && obj == null) { obj = subIter.next(); if (obj instanceof Iterator && ((Iterator) obj).hasNext()) { newIter = new DeepIterator((Iterator) obj); } } if(obj instanceof Iterator){ obj = next(); } return (String) obj; } } 

7.6. Iterador sobre dos iteradores


Tarea:

escriba un iterador que pase por dos iteradores.

Solución:

 /** * @author Irina Poroshina */ class ConcatIterator<T> implements Iterator<T> { private Iterator<T> innerIterator1; private Iterator<T> innerIterator2; public ConcatIterator (Iterator<T> innerIterator1, Iterator<T> innerIterator2) { this.innerIterator1 = innerIterator1; this.innerIterator2 = innerIterator2; } @Override public boolean hasNext() { while (innerIterator1.hasNext()) return true; while (innerIterator2.hasNext()) return true; return false; } @Override public T next() { if(!hasNext()) throw new NoSuchElementException(); while (innerIterator1.hasNext()) return innerIterator1.next(); while (innerIterator2.hasNext()) return innerIterator2.next(); return null; } } 

7.7. Elementos de conteo


Escriba un método que reciba una matriz de elementos de tipo K (genérico) como entrada y devuelva Map <K, Integer>, donde K es el valor de la matriz e Integer es el número de entradas en la matriz.
Es decir, la firma del método se ve así:

 <K> Map<K, Integer> arrayToMap(K[] ks); 

Solución:

 public static <K> Map<K, Integer> countValues(K[] ks) { Map<K, Integer> map = new HashMap<>(); for (K k : ks) { map.compute(k, new BiFunction<K, Integer, Integer>() { @Override public Integer apply(K k, Integer count) { return count == null ? 1 : count + 1; } }); } return map; } 

7.8. Cambiar claves y valores en Mapa


Escriba un método que reciba Map <K, V> y devuelva Map, donde se invierten las claves y los valores. Como los valores pueden coincidir, el tipo de valor en Map ya no será K , pero
 Colección <K>: 

 Map<V, Collection<K>> 

Solución:

 public static <K, V> Map<V, Collection<K>> inverse(Map<? extends K, ? extends V> map){ Map<V, Collection<K>> resultMap = new HashMap<>(); Set<K> keys = map.keySet(); for(K key : keys){ V value = map.get(key); resultMap.compute(value, (v, ks) -> { if(ks == null){ ks = new HashSet<>(); } ks.add(key); return ks; }); } return resultMap; } 

Multithreading


8.0. Estados


Tarea:

Imprima el estado de la secuencia antes de que comience, después de que comience y en tiempo de ejecución.

Solución:

  Thread thread = new Thread() { @Override public void run() { System.out.println(getState()); } }; System.out.println(thread.getState()); thread.start(); try { //         TERMINATED: thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(thread.getState()); 

Añadir ESPERA y BLOQUEADO:

  /** *   WAITING * * @param strings * @throws InterruptedException */ public static void main(String[] strings) throws InterruptedException { Object lock = new Object(); Thread thread = new Thread() { @Override public void run() { try { synchronized (lock) { lock.notifyAll(); lock.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } }; synchronized (lock){ thread.start(); //   lock.wait(); //  ,     System.out.println(thread.getState()); // WAITING lock.notifyAll(); System.out.println(thread.getState()); // BLOCKED } } 

Para TIMED_WAITING, cambie el mismo código un poco:

  /** *   WAITING * * @param strings * @throws InterruptedException */ public static void main(String[] strings) throws InterruptedException { Object lock = new Object(); Thread thread = new Thread() { @Override public void run() { try { synchronized (lock) { lock.notifyAll(); lock.wait(3000); } } catch (InterruptedException e) { e.printStackTrace(); } } }; synchronized (lock) { thread.start(); //   lock.wait(); //  ,     System.out.println(thread.getState()); // WAITING } } 

8.1. Hilo de sincronización


Tarea:

escriba un programa en el que se creen dos hilos que muestren su nombre en la consola.

Solución:

 class StepThread extends Thread { //     lock private Object lock; public StepThread(Object object) { this.lock = object; } /** *  :   ,   , *     ,  ,    . * *     lock.notify()   *   ,  , *  lock   .    ,   *  lock.wait(),      .    . */ @Override public void run() { while (true) { synchronized (lock) { try { System.out.println(getName()); lock.notify(); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public class Main { public static void main(String[] strings) { Object lock = new Object(); new StepThread(lock).start(); new StepThread(lock).start(); } } 

8.2. Fabricante-consumidor


Una de las tareas clásicas de subprocesos múltiples. Dadas dos corrientes: productor y consumidor. El fabricante genera algunos datos (en el ejemplo, números). El fabricante los "consume".
Dos secuencias comparten un búfer de datos común, cuyo tamaño es limitado. Si el búfer está vacío, el consumidor debe esperar a que los datos aparezcan allí. Si el búfer está lleno, el fabricante debe esperar hasta que el consumidor tome los datos y el lugar quede libre.

Fabricante:

 // implements Runnable      class Producer implements Runnable { //   private final Queue<Double> sharedQueue; //   private final int SIZE; //  public Producer(Queue<Double> sharedQueue, int size) { this.sharedQueue = sharedQueue; this.SIZE = size; } @Override public void run() { //   while (true) { try { //     produce System.out.println("Produced: " + produce()); } catch (InterruptedException e) { e.printStackTrace(); } } } private double produce() throws InterruptedException { synchronized (sharedQueue) { //  synchronized if (sharedQueue.size() == SIZE) { //   ,   sharedQueue.wait(); } //    . double newValue = Math.random(); sharedQueue.add(newValue); //     ,    sharedQueue.notifyAll(); return newValue; } } } 

Consumidor:

 // implements Runnable      class Consumer implements Runnable { //   private final Queue<Double> sharedQueue; public Consumer(Queue<Double> sharedQueue) { this.sharedQueue = sharedQueue; } @Override public void run() { while (true) { try { System.out.println("Consumed: " + consume()); } catch (InterruptedException ex) { ex.printStackTrace(); } } } // ,      private Double consume() throws InterruptedException { synchronized (sharedQueue) { if (sharedQueue.isEmpty()) { //  ,   sharedQueue.wait(); } sharedQueue.notifyAll(); return sharedQueue.poll(); } } } 

Crea y ejecuta:

 public static void main(String[] strings) { LinkedList<Double> sharedQueue = new LinkedList<>(); int size = 4; Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer"); Thread consThread = new Thread(new Consumer(sharedQueue), "Consumer"); prodThread.start(); consThread.start(); } 

9.0. Anotación propia: creación y uso


Normalmente doy esta tarea cuando se trata de anotaciones y reflexiones. Al mismo tiempo, puede hablar sobre Ejecutores , ThreadPoolExecutor y otros.

Tarea:

Cree su anotación de repetición con un parámetro entero.
Extender clase ThreadPoolExecutor y reemplazar el método ejecuta como sigue: si una instancia de Ejecutable se anota con la repetición , entonces su método de ejecución se ejecuta varias veces (el número especificado por el parámetro en la repetición ).

Es decir, escribiendo esta clase:

 @Repeat(3) class MyRunnable implements Runnable{ @Override public void run() { System.out.println("Hello!"); } } 

y usándolo:

 public static void main(String[] strings) { CustomThreadPoolExecutor customThreadPoolExecutor = new CustomThreadPoolExecutor(10); customThreadPoolExecutor.execute(new MyRunnable()); } 

Deberíamos ver:

 Hello! Hello! Hello! 

Solución:

 @Retention(RetentionPolicy.RUNTIME) @interface Repeat { int value(); } class CustomThreadPoolExecutor extends ThreadPoolExecutor { public CustomThreadPoolExecutor(int corePoolSize) { // why Integer.MAX_VALUE, 0m and TimeUnit.MILLISECONDS? // see Executors.newFixedThreadPool(int nThreads) super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); } @Override public void execute(Runnable command) { if (command != null) { Class<? extends Runnable> runnableClass = command.getClass(); Repeat repeat = runnableClass.getAnnotation(Repeat.class); for (int i = 0; i < (repeat != null ? repeat.value() : 1); i++) { super.execute(command); } } } } 

Tareas finales y otras


Durante el curso, les doy a los estudiantes varias tareas difíciles, para toda la lección. Se requiere escribir un pequeño programa usando lo aprendido previamente. Por cierto, la complejidad a menudo surge aquí. La solución a los problemas de escribir un método es una cosa, y para encontrar un algoritmo, recuerde todo lo que estudió anteriormente y también escriba 50 líneas en Java de inmediato es completamente diferente. Pero en la lección, puedo empujarlos en la dirección correcta, ayudar a resolver problemas, degradar, encontrar las clases y métodos correctos, etc. Varias de estas tareas se describen a continuación. De esta forma, se los doy a mis alumnos.

Además, al final del curso, todos deben completar la tarea final. Es decir, en casa, por su cuenta, escriba un programa. Un poco mas complicado. Te doy la oportunidad de elegir una de varias opciones. Por cierto, un hecho interesante es que necesita escribir al menos un programa, o puede escribir varios a la vez. Parece que solo recuerdo una persona que escribió más de una.

10,0 Número de restricciones viales


Una pequeña tarea que demuestra cómo se puede aplicar Java para resolver problemas prácticos.

Preparación de datos:
desde el portal de datos abierto de San Petersburgo, cargamos datos sobre la restricción del tráfico para el período de producción en formato csv .

Tarea:

Se requiere determinar cuántas restricciones de carreteras estaban vigentes en la ciudad en una fecha determinada.

El programa toma dos parámetros como argumento:

  • Ruta al archivo de datos
  • Fecha

es decir, comienza de la siguiente manera:

 java TrafficBlocks "PATH_TO_CSV_FILE" dd.MM.yyyy 

Es necesario deducir el número de restricciones de tráfico aplicables en esta fecha.

Algoritmo ejemplar


Por cierto, durante todo el tiempo solo una persona notó que el formato de fecha en los datos (aaaaMMdd) es tal que no se pueden analizar, sino que se comparan como cadenas. Entonces la solución puede simplificarse. Doy esta tarea después de hablar sobre Date, Calendar, DateFormat , así que estoy hablando de esta simplificación cuando ya escribieron todo.

No traigo una solución aquí, pueden ser muy diferentes y cada una debe considerarse de forma individual.

10.1Búsqueda de Wikipedia. En el programa de consola


Tarea:

escriba un programa que lea la consulta de búsqueda desde la consola y muestre el resultado de la búsqueda en Wikipedia. La tarea se divide en 4 etapas:
  1. Leer solicitud
  2. Hacer una solicitud al servidor
  3. Analiza la respuesta
  4. Imprimir resultado

Los puntos primero y cuarto no necesitan mucha explicación, detengámonos en la solicitud al servidor.

Esta tarea también se puede dividir en varias etapas:

  1. Solicitud de generación
  2. Solicitud del servidor
  3. Preparación para el procesamiento de respuestas
  4. Procesamiento de respuesta

Consideremos esto con más detalle: la

generación de solicitudes de
API proporciona la capacidad de realizar consultas de búsqueda sin claves. De esta manera, aproximadamente:

 https://ru.wikipedia.org/w/api.php?action=query&list=search&utf8=&format=json&srsearch="Java" 

Puede abrir este enlace en un navegador y ver el resultado de la solicitud.
Sin embargo, para que la solicitud tenga éxito, debe eliminar los caracteres no válidos del enlace, es decir, realizar la codificación porcentual , que también es codificación de URL.
Para hacer esto, en Java, puede usar el método de codificación estática en la clase URLEncoder , así:

 street = URLEncoder.encode(street, "UTF-8"); 

Eso es todo, la URL está lista! Queda ahora hacer una solicitud al servidor ...

Solicitud al servidor
Para las solicitudes GET y POST, puede usar la clase HttpURLConnection . Este es el más simple. Simplemente cree, abra una conexión y obtenga un InputStream . Ni siquiera tenemos que leerlo, Gson lo hará por nosotros .
También puede usar la modificación , o algo similar.

Preparación para procesar la respuesta
El servidor devuelve datos en formato JSON .
Pero no necesitamos analizarlo manualmente, para esto hay una biblioteca Gson de Google.
Los ejemplos están aquí:
https://github.com/google/gson
https://habrahabr.ru/company/naumen/blog/228279/

Si queda tiempo, puede escribir el recibo del artículo seleccionado durante la búsqueda, etc.

10.2 Tarea final: utilidad de consola para descargar archivos a través de HTTP


Utilidad de consola para descargar archivos a través de HTTP ... ¿te suena familiar? Sí, esta es la historia de una tarea de prueba . Todo es lógico: la tarea final del curso de Java está al mismo nivel que la tarea de prueba para el puesto de desarrollador Junior Java.
Y esta es una tarea realmente buena, sin complicaciones, pero cubre una amplia variedad de temas. Y puede ver de inmediato cómo el autor estructura el código, usa diferentes enfoques y patrones, usa el lenguaje en sí y la biblioteca estándar.

10.3 Tarea final - weather Telegram-bot


Tarea:

escriba un bot para Telegram, que será:

  • .
  • , . /subscribe. (/unsubscribe).

Puede usar https://openweathermap.org/api para obtener el pronóstico .

Esta tarea se basa más bien en la capacidad y la capacidad de comprender la nueva tecnología (bot-api) y diferentes bibliotecas. ¡Y necesitas configurar una VPN! Y tienes que escribir el código, por supuesto.

Por cierto, un hecho interesante es que la mayoría de los estudiantes ignoran la frase "ubicación enviada" y la posibilidad de enviarla. Escriben un bot que espera el nombre de la ciudad. No se porque. Esto a menudo funciona mal , el código se vuelve un poco más complicado, pero continúan haciéndolo.

10.4 Tarea final: reconocimiento de escritura a mano


Objetivo:

Implementar un programa para clasificar números escritos a mano.
Esta tarea ya está más enfocada en la implementación del algoritmo, la capacidad de comprenderlos. Por lo general, el código para los estudiantes no está muy estructurado.

Descripción de la tarea
Como conjunto de datos a estudiar, se utilizará la base de las imágenes de dígitos manuscritos MNIST . Las imágenes en esta base de datos tienen una resolución de 28x28 y se almacenan como un conjunto de valores de escala de grises. La base de datos completa se divide en dos partes: capacitación, que consta de 50,000 imágenes, y prueba - 10,000 imágenes.

Para resolver este problema, se propone implementar el método de k vecinos más cercanos- Algoritmo métrico para la clasificación automática de objetos. El principio básico del método de los vecinos más cercanos es que el objeto se asigna a la clase que es más común entre los vecinos de este elemento.
Los vecinos se toman en función de muchos objetos cuyas clases ya se conocen y, en función del valor clave de k para este método, se calcula qué clase es la más numerosa entre ellas. Como la distancia entre objetos, puede usar la métrica euclidiana, es decir, la distancia habitual entre puntos en el espacio.

Requisitos

Debe escribir un programa que reconozca los números escritos a mano. Debería ser posible inicializar una determinada clase con datos para el entrenamiento y proporcionar un método para reconocer una sola imagen.

Además de la implementación del algoritmo en sí, debe escribir código para verificar su precisión (calcular la tasa de error). Para hacer esto, use 10,000 imágenes de prueba.
Además de calcular la precisión, se propone realizar un experimento: en lugar de la métrica euclidiana, use la distancia de las manzanas de la ciudad , el ángulo entre vectores u otra cosa, y verifique la calidad del reconocimiento.

Opcional

Si todo funciona bien, entonces puede complicar la tarea un poco más. Al agregar, por ejemplo, la eliminación del ruido (emisiones) o el uso del método de ventana Parzenovsky para aumentar la precisión.

Unas pocas palabras mas


Si tiene tareas interesantes que puede ofrecer a los estudiantes (el tiempo aproximado de solución es una o dos horas), compártalas en los comentarios.

Source: https://habr.com/ru/post/440436/


All Articles