Tâches pratiques Java - pour les cours et autres activités

Tâches pratiques Java - pour les cours et autres activités


Quelques mots d'introduction


Depuis quelques années, j'enseigne la programmation Java. Au fil du temps, cela a changé - parfois différentes parties ont été ajoutées ou supprimées, la séquence des sujets a changé, l'approche pour construire un plan des classes elles-mêmes a changé, et ainsi de suite. Autrement dit, le cours a été amélioré. L'un des principaux problèmes rencontrés lors de la préparation du cours concerne les tâches. À leur sujet et seront discutés.

Le fait est que chacune de mes classes se compose de deux parties. Au début, j'agis en tant que conférencier - je vous dis avec des exemples de code sur un nouveau sujet (classes, héritage, génériques, etc.). La deuxième partie est pratique. Évidemment, cela n'a aucun sens de simplement parler de programmation, vous devez programmer. La priorité en classe est la résolution de problèmes, c'est-à-dire la programmation de quelque chose. La programmation en classe est différente de la programmation à la maison, car en classe, vous pouvez poser une question, montrer le code, obtenir une évaluation rapide du code, des commentaires sur l'amélioration, la correction de l'écrit. Il était très facile de trouver des tâches pour les toutes premières leçons. Tâches pour les boucles, les instructions conditionnelles et la POO (par exemple, écrivez la classe "Dog" ou la classe "Vector"). Des services comme leetcode vous permettent même de vérifier l'exactitude de la résolution de tels problèmes immédiatement, en ligne. Mais quelles tâches donner aux élèves dans une leçon consacrée aux collections? Streams? Et les annotations? Pendant plusieurs années, j'ai proposé ou révisé plusieurs de ces tâches, et cet article, en fait, est une collection de ces problèmes (une solution est attachée à certains problèmes).

Bien sûr, toutes les tâches sont déjà apparues quelque part. Cependant, cet article s'adresse aux enseignants de cours de programmation (pour les langages similaires à Java, la plupart des tâches le feront), ou à ceux qui enseignent la programmation en privé. Ces tâches peuvent être utilisées «prêtes à l'emploi» dans vos cours. Les apprenants Java peuvent également essayer de les résoudre. Mais de telles décisions nécessitent une vérification et une évaluation par un tiers.

Certaines des tâches les plus simples que tout le monde utilise depuis des décennies, j'ai également inclus dans cet article. Peut-être, afin de ne pas commencer immédiatement avec des classes abstraites.

Toutes les idées et suggestions sont les bienvenues!

Liste des tâches


Les bases


1.0. Maximum, minimum et moyenne
1.1 Tri par tableau
1.2 Trouver des nombres premiers
1.3 Suppression d'une baie

Bases de la POO


2.0 Concevoir et créer une classe qui décrit un vecteur
2.1 Génération d'un élément aléatoire avec un poids
2.2 Liste liée

Récursivité


3.0 Recherche binaire
3.1 Trouver la racine de l'équation
3.2 Arbre de recherche binaire

Héritage


4.0 Implémenter une hiérarchie de classes qui décrit les formes tridimensionnelles
4.1 Mettre en œuvre une hiérarchie de classes décrivant des figures en trois dimensions - 2
4.2 Mettre en œuvre une hiérarchie de classes qui décrit des formes tridimensionnelles - 3
4.3 Implémenter une hiérarchie de classes décrivant des formes tridimensionnelles - 4

Lignes


5.0 Dictionnaire de fréquence des lettres

Classes abstraites et interfaces


6.0. Convertisseur de température
6.1. Stringbuilder avec support d'annulation
6.2. Statebuilder Stringbuilder (modèle d'observateur)
6.4. Remplir un tableau avec Function

Les collections


7.0 Dictionnaire de fréquence des mots
7.1. Collection sans doublons
7.2. ArrayList et LinkedList
7.3. Écrire un itérateur sur un tableau
7.4. Écrire un itérateur sur un tableau à deux dimensions
7.5. Un itérateur encore plus complexe
7.6. Itérateur sur deux itérateurs
7.7. Compter les éléments
7.8. Modifier les clés et les valeurs dans la carte

Multithreading


8.0. États
8.1. Synchronisation des threads
8.2. Fabricant grand public

Annotations


9.0. Annotation personnalisée - création et utilisation avec réflexion

Tâches finales et autres


10,0 Nombre de restrictions routières
10.1. Recherche Wikipedia. Dans le programme de la console
10.2. Tâche finale - utilitaire de console pour télécharger des fichiers via HTTP
10.3. Dernière tâche - Météo Telegram-Bot
10.4. Tâche finale - reconnaissance de l'écriture manuscrite

Les bases


1.0. Maximum, minimum et moyenne


Défi:

Remplissez le tableau avec des nombres aléatoires et imprimez la valeur maximale, minimale et moyenne.

Pour générer un nombre aléatoire, utilisez la méthode Math.random () , qui renvoie une valeur dans l'intervalle [0, 1].

Solution:

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. Implémenter un algorithme de tri à bulles pour trier un tableau


Solution:

  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. Recherche de nombres premiers


Défi:

Écrivez un programme qui imprime des nombres premiers sur la console entre [2, 100].
Utilisez l'opérateur % (reste de la division) et les boucles pour résoudre ce problème.

Solution:

  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); } } 

Ou en utilisant des boucles d'étiquette :

  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. Supprimer du tableau


Défi:

Étant donné un tableau d'entiers et un autre entier. Supprimez toutes les occurrences de ce nombre du tableau (il ne doit pas y avoir d'espace).

Solution:

  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); } 

Vous pouvez écrire vous-même une méthode pour couper la queue du tableau, mais il convient de noter que la méthode standard fonctionnera plus rapidement:

 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; } 

Cependant, si vous procédez de cette façon, vous pouvez d'abord créer un tableau de la longueur souhaitée, puis le remplir:

 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. Conception et création d'une classe vectorielle


Défi:

Créez une classe qui décrit un vecteur (dans un espace en trois dimensions).

Il doit avoir:

  • constructeur avec des paramètres sous la forme d'une liste de coordonnées x, y, z
  • méthode qui calcule la longueur d'un vecteur. La racine peut être calculée à l'aide de Math.sqrt ():

     sqrtx2+y2+z2

  • méthode de calcul du produit scalaire:

    x1x2+y1y2+z1z2

  • méthode de calcul d'un produit vectoriel avec un autre vecteur:

    (y1z2z1y2,z1x2x1z2,x1y2y1x2)

  • méthode qui calcule l'angle entre les vecteurs (ou le cosinus de l'angle): le cosinus de l'angle entre les vecteurs est égal au produit scalaire des vecteurs divisé par le produit des modules (longueurs) des vecteurs:

     frac(a,b)|a| cdot|b|

  • méthodes de somme et de différence:

    (x1+x2,y1+y2,z1+z2)

    (x1x2,y1y2,z1z2)


  • une méthode statique qui prend un entier N et retourne un tableau de vecteurs aléatoires de taille N.

Si la méthode renvoie un vecteur, elle doit renvoyer un nouvel objet et ne pas changer celui de base. Autrement dit, vous devez implémenter le modèle " Objet immuable "

Solution:

 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 + '}'; } } 

Vous pouvez utiliser cette classe comme ceci:

 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])); } 

Vous pouvez généraliser cette solution et écrire la classe Vector pour une dimension arbitraire:

 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. Générer un élément aléatoire avec un poids


Défi:

Écrivez une classe dont le constructeur prend deux tableaux: un tableau de valeurs et un tableau de poids de valeurs.
La classe doit contenir une méthode qui renverra un élément du premier tableau de façon aléatoire, en tenant compte de son poids.
Un exemple:
Un tableau est donné [1, 2, 3] et un tableau de poids [1, 2, 10].
En moyenne, la valeur «1» devrait renvoyer 2 fois moins que la valeur «2» et dix fois moins souvent que la valeur «3» .

Solution:

 /*     :  ,   —    .   ""     ,       ,        : |-|--|----------| 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]; } } 

Mais, puisque le tableau des plages est trié, vous pouvez (et devez) utiliser une recherche binaire:

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

Il existe une autre solution à ce problème. Vous pouvez créer un tableau dont la taille est égale à la somme de tous les poids. Ensuite, le choix d'un élément aléatoire revient à générer un indice aléatoire. Autrement dit, si un tableau de valeurs est donné [1, 2, 3] et un tableau de poids [1, 2, 10], vous pouvez immédiatement créer un tableau de [1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] et en extraire un élément aléatoire:

 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]; } } 

Cette solution a un avantage - le temps d'extraire un élément aléatoire O (1) , contrairement à log (n) dans la solution précédente. Cependant, cela nécessite beaucoup de mémoire:

O( sumn)



2.2. Liste liée


Une autre tâche que je donne souvent est la mise en place d'une liste chaînée. Il peut être donné sous sa forme la plus simple (implémenter add () et get () uniquement ), ou vous pouvez demander d'implémenter java.util.List .
Je ne m'attarderai pas là-dessus en détail, il existe de nombreux articles sur l'implémentation d'une liste chaînée en Java dans Habr, par exemple celui-ci:
Structures de données en images. LinkedList

3.0 Recherche binaire


Défi:

Écrivez une méthode qui vérifie si l'élément donné est dans le tableau ou non.
Utilisez l'énumération et la recherche binaire pour résoudre ce problème.
Comparez le temps d'exécution des deux solutions pour les grandes baies (par exemple 100000000 éléments).
Solution:

  /*  ,   .    ,  -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. Trouver la racine de l'équation


Défi:

Trouver la racine de l'équation

c o s ( x 5 ) + x 4 - 345 , 3 x - 23 = 0

sur le segment [0; 10] avec une précision en x non inférieure à 0,001. Il est connu que la racine est unique dans cet intervalle.
Utilisez la méthode de réduction de moitié (et la récursivité) pour cela.

Solution:

  //   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)); } 

Remarque: si nous voulons atteindre la précision requise non pas en x , en y , alors la condition dans la méthode doit être réécrite:

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

Une petite astuce: considérant que l'ensemble des valeurs doubles est fini (il y a deux valeurs adjacentes entre lesquelles il n'y a pas de valeurs doubles), réécrivez la condition pour quitter la récursion comme suit:

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

Ainsi, nous obtenons la précision maximale qui peut généralement être obtenue en utilisant cette approche.

3.2. Arbre de recherche binaire


L'implémentation d'un arbre de recherche binaire est une excellente tâche. Je le donne généralement quand il s'agit de récursivité.
Je n'écrirai pas beaucoup à ce sujet, il existe de nombreux articles / implémentations d'un type très différent:
Structures de données: arbres binaires.
Arbre binaire, mise en œuvre rapide
Implémentation Java d'une arborescence binaire hachée

Héritage


4.0. Implémenter une hiérarchie de classes qui décrit des formes tridimensionnelles


Défi:

Implémentez une hiérarchie de classes:


La classe Box est un conteneur; elle peut contenir d'autres formes. La méthode add () prend Shape en entrée. Nous devons ajouter de nouvelles formes jusqu'à ce que nous prenions de la place pour elles dans la boîte (nous ne considérerons que le volume, en ignorant la forme. Supposons que nous versions du liquide). S'il n'y a pas assez d'espace pour ajouter une nouvelle forme, la méthode doit retourner false .

Solution:

 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 } } 

Afin de ne pas revenir à cette tâche, plusieurs autres variantes de cette tâche sont décrites ci-dessous.

4.1. Implémenter une hiérarchie de classes qui décrit des formes tridimensionnelles - 2


Défi:

Implémentez la même hiérarchie de classes, mais rendez certaines classes abstraites.

Solution:

 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. Mettre en œuvre une hiérarchie de classes qui décrit des formes tridimensionnelles - 3


Défi:

Implémentez la même hiérarchie de classes, mais en utilisant des interfaces.
De plus, les étudiants sont encouragés à implémenter l'interface comparable .

Solution:

 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. Mettre en œuvre une hiérarchie de classes qui décrit des formes tridimensionnelles - 4


Défi:

Ajoutez une forme de rotation à la hiérarchie des classes pour une fonction arbitraire. Vous pouvez calculer le volume approximatif en utilisant une certaine intégrale. Étant donné que le volume de la rotation autour de l'axe x est

V x = p i i n t b a f 2 ( x ) d x  


Et l'intégrale est
image


Ensuite, vous pouvez écrire une implémentation de la méthode rectangle :

 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()); } 

Bien sûr, nous ne prenons pas en compte la précision des calculs ici, et nous ne sélectionnons pas le nombre de partitions pour atteindre la précision nécessaire, mais c'est une tâche de programmation, pas des méthodes numériques, donc nous omettons cela en classe.

Lignes


Vous pouvez trouver beaucoup de tâches par ligne. Je donne généralement ces derniers:

  • Écrivez une méthode pour trouver la chaîne la plus longue d'un tableau.
  • Écrivez une méthode qui vérifie si un mot est un palindrome .
  • Écrivez une méthode qui remplace dans le texte toutes les occurrences du mot Bulk «byaka» par «[cut out]
    censuré]. »
  • Il y a deux lignes. Écrivez une méthode qui renvoie le nombre d'occurrences d'une ligne dans une autre.

Je ne décrirai pas les solutions à de tels problèmes, et il y a aussi un grand nombre de tâches pour les chaînes.

Parmi les plus intéressants, j'aime celui-ci:

5.0. Dictionnaire de fréquence des lettres de l'alphabet russe (ou anglais).


Défi:

Créez un dictionnaire de fréquences de lettres de l'alphabet russe (ou anglais). On omet le problème du choix et de l'analyse du corps de la langue, il suffira de prendre le texte de courte durée).

Solution:

  /** *      ,  *      , *    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()); } } 

Ou alors:

  /** *   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]); } } 

Classes abstraites et interfaces


6.0. Convertisseur de température


Défi:

Écrivez une classe BaseConverter pour convertir des degrés Celsius en
Kelvin , Fahrenheit , etc. La méthode doit avoir une méthode de conversion , qui
et fait la conversion.

Solution:

 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)); } } 

De plus, vous pouvez demander d'implémenter une méthode d'usine , quelque chose comme ceci:

 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 avec support d' annulation


Tâche:

écrivez votre classe StringBuilder avec la prise en charge de l'opération d' annulation . Pour ce faire, déléguez toutes les méthodes au StringBuilder standard et, dans votre propre classe, stockez une liste de toutes les opérations à exécuter par undo () . Ce sera la mise en œuvre du modèle d' équipe .

Solution:

 /** * 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. Statebuilder Stringbuilder (modèle d'observateur)


Tâche:

écrivez votre classe StringBuilder, avec la possibilité de notifier à d'autres objets un changement de son état. Pour ce faire, déléguez toutes les méthodes au StringBuilder standard et implémentez le modèle de conception Observer dans votre propre classe .

Solution:

 /** .  ,      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. Filtrer


Problème:

Méthode d' écriture filtre , qui reçoit la matrice d'entrée (de tout type), et la mise en œuvre de l'interface de filtre méthode c appliquer (Object o) , à retirer de la matrice superflu.
Vérifiez comment cela fonctionne sur des chaînes ou d'autres objets.

Solution:
Habituellement, je donne cette tâche avant les génériques, afin que les élèves écrivent une méthode sans eux en utilisant 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; } }); } } 

Mais c'est possible avec les génériques. Ensuite, vous pouvez utiliser la fonction standard :

 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. Remplissage de tableau


Une tâche quelque peu similaire à la précédente:
écrire une méthode fill qui accepte un tableau d'objets et une implémentation de l'interface Function (ou la vôtre).
La méthode fill doit remplir le tableau, obtenant la nouvelle valeur par index en utilisant l'implémentation de l'interface Function. Autrement dit, vous voulez l'utiliser comme ceci:

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

Solution:

 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); } } 

Les collections



7.0 Dictionnaire de fréquence des mots


voir le problème du dictionnaire de fréquence des lettres de l'alphabet

7.1. Collection sans doublons


Tâche:

écrire une méthode qui reçoit une collection d'objets en entrée et renvoie une collection sans doublons.

Solution:

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

7.2. ArrayList et LinkedList


Écrivez une méthode qui ajoute 1 000 000 d'éléments à une liste de tableaux et à une liste de liens. Écrivez une autre méthode qui sélectionne un élément au hasard 100 000 fois dans la liste remplie. Mesurez le temps passé dessus. Comparez les résultats et suggérez pourquoi ils le sont.

Solution:

  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. Écrire un itérateur sur un tableau


Solution:

 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. Itérateur de réseau bidimensionnel


Tâche:

écrire un itérateur sur un tableau à deux dimensions.

Solution:

 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 itérateur encore plus complexe


J'aime cette tâche. Elle ne rejoint que quelques élèves du groupe qui sont relativement faciles à gérer les tâches précédentes.

Tâche:

Dan itérateur. La méthode next () renvoie une chaîne ou un itérateur de la même structure (c'est-à-dire qui renvoie à nouveau soit une chaîne, soit le même itérateur). Écrivez au-dessus de cet itérateur un autre, déjà «plat».

Solution sur les piles:

 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 solution récursive:

 /** * @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. Itérateur sur deux itérateurs


Tâche:

écrire un itérateur qui passe par deux itérateurs.

Solution:

 /** * @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. Compter les éléments


Écrivez une méthode qui reçoit un tableau d'entrée d'éléments de type K (générique) et renvoie Map <K, Integer>, où K est la valeur du tableau et Integer est le nombre d'entrées dans le tableau.
Autrement dit, la signature de la méthode ressemble à ceci:

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

Solution:

 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. Modifier les clés et les valeurs dans la carte


Écrivez une méthode qui reçoit Map <K, V> et renvoie Map, où les clés et les valeurs sont inversées. Étant donné que les valeurs peuvent coïncider, le type de valeur dans Map ne sera plus K , mais
 Collection <K>: 

 Map<V, Collection<K>> 

Solution:

 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. États


Tâche:

affichez l'état du flux avant son démarrage, après son démarrage et lors de l'exécution.

Solution:

  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()); 

Ajouter EN ATTENTE et BLOQUÉ:

  /** *   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 } } 

Pour TIMED_WAITING, modifiez un peu le même code:

  /** *   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. Synchronisation des threads


Tâche:

Écrivez un programme dans lequel deux threads sont créés et affichent tour à tour leur nom sur la console.

Solution:

 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. Fabricant-consommateur


L'une des tâches multithreads classiques. Étant donné deux volets - producteur et consommateur. Le fabricant génère certaines données (dans l'exemple, des nombres). Le fabricant les «consomme».
Deux flux partagent un tampon de données commun, dont la taille est limitée. Si le tampon est vide, le consommateur doit attendre que les données y apparaissent. Si la mémoire tampon est pleine, le fabricant doit attendre que le consommateur prenne les données et que l'endroit devienne libre.

Constructeur:

 // 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; } } } 

Consommateur:

 // 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(); } } } 

Créer et exécuter:

 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. Propre annotation - création et utilisation


Je donne généralement cette tâche en ce qui concerne les annotations et la réflexion. Dans le même temps, vous pouvez parler d' exécuteurs , de ThreadPoolExecutor et d'autres.

Tâche:

créez votre annotation de répétition avec un paramètre entier.
Étendre la classe ThreadPoolExecutor et substituer la méthode exécuter de la manière suivante: si une instance de Runnable est annotée avec la répétition , puis sa méthode run est exécuté plusieurs fois (le nombre spécifié par le paramètre dans la répétition ).

Autrement dit, en écrivant cette classe:

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

et en l'utilisant:

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

Il faut voir:

 Hello! Hello! Hello! 

Solution:

 @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); } } } } 

Tâches finales et autres


Pendant le cours, je donne aux étudiants plusieurs tâches difficiles - pour toute la leçon. Il est nécessaire d'écrire un petit programme en utilisant ce qui a été appris précédemment. Soit dit en passant, la complexité se pose souvent ici. La solution aux problèmes d'écriture d'une méthode est une chose, mais pour trouver un algorithme, rappelez-vous tout ce que vous avez étudié plus tôt et écrivez tout de suite 50 lignes en Java est complètement différent. Mais dans la leçon, je peux les pousser dans la bonne direction, aider à résoudre les problèmes, dégrader, trouver les bonnes classes et méthodes, etc. Plusieurs de ces tâches sont décrites ci-dessous. Sous cette forme, je les donne à mes élèves.

De plus, à la fin du cours, tout le monde devrait terminer le travail final. C'est-à-dire, chez vous, par vous-même, écrire un programme. Un peu plus compliqué. Je vous donne la possibilité de choisir parmi plusieurs options. Soit dit en passant, un fait intéressant est que vous devez écrire au moins un programme, ou vous pouvez en écrire plusieurs à la fois. Il semble que je me souvienne d'une seule personne qui en a écrit plus d'une.

10,0 Nombre de restrictions routières


Une petite tâche qui montre comment Java peut être appliqué pour résoudre des problèmes pratiques.

Préparation des données:
À partir du portail de données ouvertes de Saint-Pétersbourg, nous chargeons les données sur la restriction du trafic pour la période de production au format csv .

Tâche:

Il est nécessaire de déterminer combien de restrictions routières étaient en vigueur dans la ville à une certaine date.

Le programme prend deux paramètres en argument:

  • Chemin d'accès au fichier de données
  • Date

c'est-à-dire qu'il commence comme suit:

 java TrafficBlocks "PATH_TO_CSV_FILE" dd.MM.yyyy 

Il est nécessaire de déduire le nombre de restrictions de circulation applicables à cette date.

Algorithme exemplaire


Soit dit en passant, pendant tout le temps, une seule personne a remarqué que le format de date dans les données (aaaaMMjj) est tel qu'elles ne peuvent pas être analysées, mais comparées en tant que chaînes. La solution peut donc être simplifiée. Je donne cette tâche après avoir parlé de Date, Calendar, DateFormat , donc je parle de cette simplification quand ils ont déjà tout écrit.

Je n’apporte pas de solution ici, elles peuvent être très différentes et chacune doit être examinée individuellement.

10.1.Recherche Wikipedia. Dans le programme de la console


Tâche:

rédigez un programme qui lit la requête de recherche à partir de la console et affiche le résultat de la recherche sur Wikipedia. La tâche est divisée en 4 étapes:
  1. Lire la demande
  2. Faire une demande au serveur
  3. Analyser la réponse
  4. Résultat d'impression

Les premier et quatrième points n'ont pas besoin de beaucoup d'explications, arrêtons-nous sur la demande adressée au serveur.

Cette tâche peut également être divisée en plusieurs étapes:

  1. Génération de demande
  2. Demande du serveur
  3. Préparation du traitement des réponses
  4. Traitement des réponses

Examinons cela plus en détail: la

génération de requêtes
API offre la possibilité de faire des requêtes de recherche sans clés. De cette façon, environ:

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

Vous pouvez ouvrir ce lien dans un navigateur et consulter le résultat de la demande.
Cependant, pour que la demande aboutisse, vous devez supprimer les caractères non valides du lien, c'est-à-dire effectuer un encodage en pourcentage , qui est également un encodage d'URL.
Pour ce faire, en Java, vous pouvez utiliser la méthode d' encodage statique dans la classe URLEncoder , comme ceci:

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

Ça y est, l'URL est prête! Il reste maintenant à faire une requête au serveur ...

Demande au serveur
Pour les requêtes GET et POST, vous pouvez utiliser la classe HttpURLConnection . C'est le plus simple. Créez, ouvrez une connexion et obtenez un InputStream . Nous n'avons même pas besoin de le lire, Gson le fera pour nous .
Vous pouvez également utiliser la modification , ou quelque chose de similaire.

Préparation du traitement de la réponse
Le serveur renvoie des données au format JSON .
Mais nous n'avons pas besoin de l'analyser manuellement, pour cela il y a une bibliothèque Gson de Google.
Des exemples sont ici:
https://github.com/google/gson
https://habrahabr.ru/company/naumen/blog/228279/

S'il reste du temps, vous pouvez écrire le reçu de l'article sélectionné lors de la recherche et ainsi de suite.

10.2. Tâche finale - utilitaire de console pour télécharger des fichiers via HTTP


Utilitaire de console pour télécharger des fichiers via HTTP ... ça vous semble familier? Oui, c'est ça - L'histoire d'une tâche de test . Tout est logique - la tâche finale du cours Java est au même niveau que la tâche de test pour le poste de développeur Java junior.
Et c'est une très bonne tâche - simple, mais couvre une grande variété de sujets. Et vous pouvez immédiatement voir comment l'auteur structure le code, utilise différentes approches et modèles, utilise le langage lui-même et la bibliothèque standard.

10.3. Dernière tâche - Météo Telegram-Bot


Tâche:

Écrivez un bot pour Telegram, qui sera:

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

Vous pouvez utiliser https://openweathermap.org/api pour obtenir les prévisions .

Cette tâche porte plutôt sur la capacité et la capacité de comprendre la nouvelle technologie (bot-api) et les différentes bibliothèques. Et vous devez configurer un VPN! Et vous devez bien sûr écrire le code.

Soit dit en passant, un fait intéressant est que la plupart des élèves ignorent l'expression «emplacement envoyé» et la possibilité de l'envoyer. Ils écrivent un bot qui attend le nom de la ville. Je ne sais pas pourquoi. Cela fonctionne souvent mal , le code devient un peu plus compliqué, mais ils continuent de le faire.

10.4. Tâche finale - reconnaissance de l'écriture manuscrite


Objectif:

implémenter un programme de classification des nombres manuscrits.
Cette tâche est déjà plus axée sur la mise en œuvre de l'algorithme, la capacité de les comprendre. Habituellement, le code pour les étudiants n'est pas très structuré.

Description de la tâche
Comme l'ensemble de données à étudier, la base des images de chiffres manuscrits MNIST sera utilisée . Les images de cette base de données ont une résolution de 28x28 et sont stockées sous la forme d'un ensemble de valeurs de niveaux de gris. La base de données entière est divisée en deux parties: formation, composée de 50 000 images, et test - 10 000 images.

Pour résoudre ce problème, il est proposé d'implémenter la méthode des k plus proches voisins- algorithme métrique pour la classification automatique des objets. Le principe de base de la méthode des voisins les plus proches est que l'objet est affecté à la classe la plus courante parmi les voisins de cet élément.
Les voisins sont pris sur la base de nombreux objets dont les classes sont déjà connues, et, sur la base de la valeur clé de k pour cette méthode, il est calculé quelle classe est la plus nombreuse parmi eux. Comme distance entre les objets, vous pouvez utiliser la métrique euclidienne, c'est-à-dire la distance habituelle entre les points dans l'espace.

Conditions Vous

devez écrire un programme qui reconnaîtra les nombres manuscrits. Il devrait être possible d'initialiser une certaine classe avec des données pour la formation et de fournir une méthode pour reconnaître une seule image.

En plus de l'implémentation de l'algorithme lui-même, vous devez écrire du code pour vérifier sa précision (calculer le taux d'erreur). Pour ce faire, utilisez 10 000 images de test.
En plus de calculer la précision, il est proposé de mener une expérience: au lieu de la métrique euclidienne, utilisez la distance des blocs de ville , l'angle entre les vecteurs ou autre chose, et vérifiez la qualité de la reconnaissance.

En option

Si tout fonctionne bien, vous pouvez compliquer un peu plus la tâche. En ajoutant, par exemple, l'élimination du bruit (émissions) ou l'utilisation de la méthode de la fenêtre Parzenovsky pour augmenter la précision.

Encore quelques mots


Si vous avez des tâches intéressantes à proposer aux élèves (le temps de résolution approximatif est d'une heure ou deux), partagez-les dans les commentaires.

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


All Articles