Tarefas práticas Java - para cursos e outras atividades

Tarefas práticas Java - para cursos e outras atividades


Algumas palavras introdutórias


Nos últimos anos, tenho ensinado programação Java. Com o tempo, mudou - algumas vezes, partes diferentes foram adicionadas ou jogadas fora, a sequência de tópicos mudou, a abordagem para a criação de um plano das próprias classes mudou, e assim por diante. Ou seja, o curso foi aprimorado. Um dos principais problemas encontrados na preparação do curso são as tarefas. Sobre eles e será discutido.

O fato é que cada uma das minhas aulas consiste em duas partes. No início, eu atuo como palestrante - digo com exemplos de código sobre algum novo tópico (classes, herança, genéricos e assim por diante). A segunda parte é prática. Obviamente, não faz sentido apenas falar sobre programação, você precisa programar. A prioridade na sala de aula é resolver problemas, ou seja, programar algo de alguma forma. A programação na sala de aula é diferente da programação em casa, pois na sala de aula você pode fazer uma pergunta, mostrar o código, obter uma avaliação rápida do código, comentários sobre melhorias, correção da escrita. Foi muito fácil encontrar tarefas para as primeiras lições. Tarefas para loops, instruções condicionais e OOP (por exemplo, escreva a classe "Dog" ou a classe "Vector"). Serviços como o leetcode até permitem verificar a correção de problemas imediatamente, on-line. Mas que tarefas os alunos deveriam realizar em uma lição dedicada às coleções? Streams? E as anotações? Por vários anos, propus ou revisei várias dessas tarefas, e este artigo, de fato, é uma coleção desses problemas (uma solução está anexada a alguns problemas).

Obviamente, todas as tarefas já apareceram em algum lugar. No entanto, este artigo é destinado a professores de cursos de programação (para linguagens semelhantes a Java, a maioria das tarefas será útil) ou àqueles que ensinam programação em particular. Essas tarefas podem ser usadas "prontas para uso" em suas aulas. Os alunos de Java também podem tentar resolvê-los. Mas essas decisões exigem verificação e avaliação de terceiros.

Algumas das tarefas mais simples que todo mundo usa há décadas, também incluí neste artigo. Talvez, para não começar imediatamente com as classes abstratas.

Todas as idéias e sugestões são bem-vindas!

Lista de tarefas


O básico


1.0 Máximo, Mínimo e Médio
1.1 Classificação da matriz
1.2 Localizando primos
1.3 Removendo de uma matriz

Noções básicas de OOP


2.0 Projetando e criando uma classe que descreve um vetor
2.1 Geração de um elemento aleatório com peso
2.2 Lista vinculada

Recursão


3.0 Pesquisa binária
3.1 Encontre a raiz da equação
3.2 Árvore de Pesquisa Binária

Herança


4.0 Implementar uma hierarquia de classes que descreva formas tridimensionais
4.1 Implementar uma hierarquia de classes que descreva figuras tridimensionais - 2
4.2 Implementar uma hierarquia de classes que descreva formas tridimensionais - 3
4.3 Implementar uma hierarquia de classes que descreva formas tridimensionais - 4

Linhas


5.0 Dicionário de letras de frequência

Classes abstratas e interfaces


6.0 Conversor de temperatura
6.1 Construtor de cordas com suporte a desfazer
6.2 Statebuilding Stringbuilder (padrão do observador)
6.4 Preenchendo uma matriz com Function

Colecções


7.0 Dicionário de Palavras de Frequência
7.1 Coleção sem duplicatas
7.2 ArrayList e LinkedList
7.3 Escreva um iterador sobre uma matriz
7.4 Escreva um iterador sobre uma matriz bidimensional
7.5 Um iterador ainda mais complexo
7.6 Iterador sobre dois iteradores
7.7 Elementos de contagem
7.8 Alterar chaves e valores no mapa

Multithreading


8.0 Estados
8.1 Sincronização de threads
8.2 Fabricante do Consumidor

Anotações


9.0 Anotação personalizada - criação e uso com reflexão

Tarefas finais e outras


10.0 Número de restrições rodoviárias
10.1 Pesquisa na Wikipedia. No programa do console
10.2 Tarefa final - utilitário do console para baixar arquivos via HTTP
10.3 Tarefa final - tempo Telegram-bot
10.4 Tarefa final - reconhecimento de manuscrito

O básico


1.0 Máximo, Mínimo e Médio


Desafio:

Preencha a matriz com números aleatórios e imprima o valor máximo, mínimo e médio.

Para gerar um número aleatório, use o método Math.random () , que retorna um valor no intervalo [0, 1].

Solução:

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 um algoritmo de classificação de bolhas para classificar uma matriz


Solução:

  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 Procure por números primos


Desafio:

Escreva um programa que imprima números primos no console entre [2, 100].
Use o operador % (restante da divisão) e loops para resolver esse problema.

Solução:

  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 usando loops de etiqueta :

  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 Excluir da matriz


Desafio:

Dada uma matriz de números inteiros e outro número inteiro. Remova todas as ocorrências desse número da matriz (não deve haver lacunas).

Solução:

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

Você pode escrever um método para cortar a cauda da matriz, mas vale a pena notar que o método padrão funcionará mais rapidamente:

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

No entanto, se você seguir esse caminho, primeiro poderá criar uma matriz do comprimento desejado e preenchê-la:

 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 Projetando e criando uma classe de vetores


Desafio:

Crie uma classe que descreva um vetor (no espaço tridimensional).

Ele deve ter:

  • construtor com parâmetros na forma de uma lista de coordenadas x, y, z
  • método que calcula o comprimento de um vetor. A raiz pode ser calculada usando Math.sqrt ():

     sqrtx2+y2+z2

  • método de cálculo escalar do produto:

    x1x2+y1y2+z1z2

  • método de cálculo de um produto vetorial com outro vetor:

    (y1z2z1y2,z1x2x1z2,x1y2y1x2)

  • método que calcula o ângulo entre os vetores (ou o cosseno do ângulo): o cosseno do ângulo entre os vetores é igual ao produto escalar dos vetores dividido pelo produto dos módulos (comprimentos) dos vetores:

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

  • métodos para soma e diferença:

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

    (x1x2,y1y2,z1z2)


  • um método estático que pega um número inteiro N e retorna uma matriz de vetores aleatórios de tamanho N.

Se o método retornar um vetor, ele deve retornar um novo objeto e não alterar o base. Ou seja, você precisa implementar o modelo " Objeto imutável "

Solução:

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

Você pode usar esta classe assim:

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

Você pode generalizar esta solução e escrever a classe Vector para uma dimensão arbitrária:

 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 Gerando um item aleatório com um peso


Desafio:

Escreva uma classe cujo construtor use duas matrizes: uma matriz de valores e uma matriz de pesos de valores.
A classe deve conter um método que retorne um elemento da primeira matriz aleatoriamente, levando em consideração seu peso.
Um exemplo:
Uma matriz é dada [1, 2, 3] e uma matriz de pesos [1, 2, 10].
Em média, o valor "1" deve retornar 2 vezes menos que o valor "2" e dez vezes menos que o valor "3" .

Solução:

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

Mas, como a matriz de intervalos é classificada, você pode (e deve) usar uma pesquisa binária:

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

Há outra solução para esse problema. Você pode criar uma matriz cujo tamanho seja igual à soma de todos os pesos. Em seguida, a escolha de um elemento aleatório se resume a gerar um índice aleatório. Ou seja, se uma matriz de valores for fornecida [1, 2, 3] e uma matriz de pesos [1, 2, 10], você poderá criar imediatamente uma matriz [1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] e extraia dele um elemento aleatório:

 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 solução tem uma vantagem - o tempo para extrair um elemento aleatório O (1) , em contraste com o log (n) na solução anterior. No entanto, isso requer muita memória:

O( sumn)



2.2 Lista vinculada


Outra tarefa que geralmente dou é a implementação de uma lista vinculada. Ele pode ser fornecido em sua forma mais simples (implementar apenas add () e get () ), ou você pode solicitar a implementação do java.util.List .
Não vou me debruçar sobre isso em detalhes, existem muitos artigos sobre a implementação de uma lista vinculada em Java no Habr, por exemplo, este:
Estruturas de dados em imagens. LinkedList

3.0 Pesquisa binária


Desafio:

Escreva um método que verifique se o elemento fornecido está na matriz ou não.
Use enumeração e pesquisa binária para resolver esse problema.
Compare o tempo de execução de ambas as soluções para matrizes grandes (por exemplo, 100000000 elementos).
Solução:

  /*  ,   .    ,  -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 Encontre a raiz da equação


Desafio:

Encontre a raiz da equação

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

no segmento [0; 10] com uma precisão em x não pior que 0,001. Sabe-se que a raiz é única nesse intervalo.
Use o método da metade (e recursão) para isso.

Solução:

  //   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: se quisermos obter a precisão necessária, não em x , em y , a condição no método deve ser reescrita:

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

Um pequeno truque: considerando que o conjunto de valores duplos é finito (há dois valores adjacentes entre os quais não há valores duplos), reescreva a condição para sair da recursão da seguinte maneira:

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

Assim, obtemos a precisão máxima que geralmente pode ser obtida usando essa abordagem.

3.2 Árvore de pesquisa binária


A implementação de uma árvore de pesquisa binária é uma excelente tarefa. Eu geralmente dou quando se trata de recursão.
Não vou escrever muito sobre isso, existem muitos artigos / implementações de um tipo muito diferente:
Estruturas de dados: árvores binárias.
Árvore binária, implementação rápida
Implementação Java de uma árvore binária com hash

Herança


4.0 Implementar uma hierarquia de classes que descreva formas tridimensionais


Desafio:

Implemente uma hierarquia de classes:


A classe Box é um contêiner; pode conter outras formas. O método add () usa Shape como entrada. Precisamos adicionar novas formas até conseguirmos espaço para elas na Caixa (consideraremos apenas o volume, ignorando a forma. Suponha que derramemos líquidos). Se não houver espaço suficiente para adicionar uma nova forma, o método retornará false .

Solução:

 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 não retornar a esta tarefa, várias outras variações desta tarefa são descritas abaixo.

4.1 Implemente uma hierarquia de classes que descreva formas tridimensionais - 2


Desafio:

Implemente a mesma hierarquia de classes, mas torne algumas classes abstratas.

Solução:

 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 uma hierarquia de classes que descreva formas tridimensionais - 3


Desafio:

Implemente a mesma hierarquia de classes, mas usando interfaces.
Além disso, os alunos são incentivados a implementar a interface Comparável .

Solução:

 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 uma hierarquia de classes que descreva formas tridimensionais - 4


Desafio:

Adicione uma forma de rotação à hierarquia de classes para uma função arbitrária. Você pode calcular o volume aproximado usando uma certa integral. Como o volume da rotação em torno do eixo x é

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


E a integral é
imagem


Então você pode escrever uma implementação do método retâ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()); } 

Obviamente, não levamos em conta a precisão dos cálculos aqui e não selecionamos o número de partições para obter a precisão necessária, mas essa é uma tarefa de programação, não métodos numéricos, por isso omitimos isso na sala de aula.

Linhas


Você pode encontrar muitas tarefas por linha. Eu costumo dar estes:

  • Escreva um método para encontrar a string mais longa em uma matriz.
  • Escreva um método que verifique se uma palavra é um palíndromo .
  • Escreva um método que substitua no texto todas as ocorrências da palavra Bulk “byaka” por “[recortar
    censurado]. ”
  • Existem duas linhas. Escreva um método que retorne o número de ocorrências de uma linha em outra.

Não descreverei as soluções para esses problemas e também há um grande número de tarefas para seqüências de caracteres.

Dos mais interessantes, eu gosto deste:

5.0 Dicionário de frequência de letras do alfabeto russo (ou inglês).


Desafio:

Crie um dicionário de frequência de letras do alfabeto russo (ou inglês). Omitimos o problema de escolher e analisar o corpo da linguagem; basta levar o texto de curta duração).

Solução:

  /** *      ,  *      , *    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 então:

  /** *   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 abstratas e interfaces


6.0 Conversor de temperatura


Desafio:

Escreva uma classe BaseConverter para converter de graus Celsius em
Kelvin , Fahrenheit e assim por diante. O método deve ter um método de conversão , que
e faz a conversão.

Solução:

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

Além disso, você pode pedir para implementar um método de fábrica , algo como isto:

 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 Construtor de cordas com suporte a desfazer


Tarefa:

Escreva sua classe StringBuilder com suporte para a operação desfazer . Para fazer isso, delegue todos os métodos ao StringBuilder padrão e, em sua própria classe, armazene uma lista de todas as operações para a execução de desfazer () . Essa será a implementação do modelo de equipe .

Solução:

 /** * 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 (padrão do observador)


Tarefa:

Escreva sua classe StringBuilder, com a capacidade de notificar outros objetos sobre uma alteração em seu estado. Para fazer isso, delegue todos os métodos ao StringBuilder padrão e implemente o padrão de design do Observer em sua própria classe .

Solução:

 /** .  ,      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 gravação filtro , que recebe o feixe de entrada (de qualquer tipo), e a implementação da interface de filtro método c aplicar (Object o) , para remover da matriz supérfluo.
Verifique como ele funciona em strings ou outros objetos.

Solução:
Normalmente, eu dou essa tarefa antes do Generics, para que os alunos escrevam um método sem eles usando o 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; } }); } } 

Mas, é possível com os genéricos. Então você pode usar a função padrão :

 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 Preenchimento de matriz


Uma tarefa semelhante à anterior:
escreva um método de preenchimento que aceite uma matriz de objetos e uma implementação da interface Function (ou a sua).
O método fill deve preencher a matriz, obtendo o novo valor pelo índice, usando a implementação da interface Function. Ou seja, você deseja usá-lo assim:

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

Solução:

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

Colecções



7.0 Dicionário de Palavras de Frequência


veja o problema sobre o dicionário de frequências de letras do alfabeto

7.1 Coleção sem duplicatas


Tarefa:

Escreva um método que receba uma coleção de objetos como entrada e retorne uma coleção sem duplicatas.

Solução:

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

7.2 ArrayList e LinkedList


Escreva um método que adicione 1.000.000 de elementos a um ArrayList e LinkedList. Escreva outro método que selecione um elemento aleatoriamente 100.000 vezes na lista preenchida. Meça o tempo gasto com isso. Compare os resultados e sugira por que eles são.

Solução:

  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 Escreva um iterador sobre uma matriz


Solução:

 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


Tarefa:

Escreva um iterador em uma matriz bidimensional.

Solução:

 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 Um iterador ainda mais complexo


Eu gosto desta tarefa. Ela alcança apenas alguns alunos do grupo que são relativamente fáceis de lidar com tarefas anteriores.

Tarefa:

Dan iterator. O método next () retorna uma String ou um iterador da mesma estrutura (ou seja, que novamente retorna String ou o mesmo iterador). Escreva em cima deste iterador, outro já "plano".

Solução nas pilhas:

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

A solução 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 dois iteradores


Tarefa:

Escreva um iterador que passe por dois iteradores.

Solução:

 /** * @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 contagem


Escreva um método que receba uma matriz de entrada de elementos do tipo K (Genérico) e retorne Mapa <K, Inteiro>, onde K é o valor da matriz e Inteiro é o número de entradas na matriz.
Ou seja, a assinatura do método fica assim:

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

Solução:

 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 Alterar chaves e valores no mapa


Escreva um método que receba Map <K, V> e retorne Map, onde as chaves e os valores são invertidos. Como os valores podem coincidir, o tipo de valor no Mapa não será mais K , mas
 Coleção <K>: 

 Map<V, Collection<K>> 

Solução:

 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


Tarefa:

imprima o estado do fluxo antes de iniciar, depois de iniciar e em tempo de execução.

Solução:

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

Adicione WAITING e BLOCKED:

  /** *   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, altere um pouco o mesmo código:

  /** *   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 Sincronização de threads


Tarefa:

Escreva um programa no qual são criados dois encadeamentos que exibem seus nomes no console por vez.

Solução:

 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


Uma das tarefas clássicas de multithreading. Dado dois fluxos - produtor e consumidor. O fabricante gera alguns dados (no exemplo, números). O fabricante "consome" eles.
Dois fluxos compartilham um buffer de dados comum, cujo tamanho é limitado. Se o buffer estiver vazio, o consumidor deverá aguardar os dados aparecerem lá. Se o buffer estiver cheio, o fabricante deve esperar até que o consumidor pegue os dados e o local fique livre.

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

Crie e execute:

 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 Anotação própria - criação e uso


Eu geralmente dou essa tarefa quando se trata de anotações e reflexão. Ao mesmo tempo, você pode falar sobre Executors , ThreadPoolExecutor e outros.

Tarefa:

Crie sua anotação de repetição com um parâmetro inteiro.
Estender classe ThreadPoolExecutor e substituir o método executar como se segue: se um exemplo de Execut�el é anotada com a repetição , em seguida, o seu método de execução é executado várias vezes (o número especificado pelo parâmetro em Repeat ).

Ou seja, escrevendo esta classe:

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

e usá-lo:

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

Deveríamos ver:

 Hello! Hello! Hello! 

Solução:

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

Tarefas finais e outras


Durante o curso, dou aos alunos várias tarefas difíceis - durante toda a lição. É necessário escrever um pequeno programa usando o aprendido anteriormente. A propósito, a complexidade geralmente surge aqui. A solução para os problemas de escrever um método é uma coisa, mas para criar um algoritmo, lembre-se de tudo o que você estudou anteriormente e também escreva 50 linhas em Java imediatamente é completamente diferente. Mas na lição, eu posso empurrá-los na direção certa, ajudar a resolver problemas, degradar, encontrar as classes e métodos certos e assim por diante. Várias dessas tarefas são descritas abaixo. Nesta forma, eu os dou aos meus alunos.

Além disso, no final do curso, todos devem concluir a tarefa final. Ou seja, em casa, por conta própria, escreva um programa. Um pouco mais complicado. Dou-lhe a oportunidade de escolher uma das várias opções. A propósito, um fato interessante é que você precisa escrever pelo menos um programa ou escrever vários ao mesmo tempo. Parece que me lembro de apenas uma pessoa que escreveu mais de uma.

10.0 Número de restrições rodoviárias


Uma pequena tarefa que demonstra como o Java pode ser aplicado para resolver problemas práticos.

Dados Preparação:
Com os dados aberto portal St Petersburg carregar os dados sobre a limitação do movimento de transporte para o período de obras no formato CSV .

Tarefa:

É necessário determinar quantas restrições de estrada estavam em vigor na cidade em uma determinada data.

O programa aceita dois parâmetros como argumento:

  • Caminho para o arquivo de dados
  • Data

isto é, começa da seguinte maneira:

 java TrafficBlocks "PATH_TO_CSV_FILE" dd.MM.yyyy 

É necessário deduzir o número de restrições de tráfego aplicáveis ​​nesta data.

Algoritmo exemplar


A propósito, durante todo o tempo, apenas uma pessoa notou que o formato da data nos dados (aaaamMdd) é tal que eles não podem ser analisados, mas comparados como seqüências de caracteres. Portanto, a solução pode ser simplificada. Eu dou essa tarefa depois de falar sobre Date, Calendar, DateFormat , então estou falando dessa simplificação quando eles já escreveram tudo.

Não trago uma solução aqui, elas podem ser muito diferentes e cada uma deve ser analisada individualmente.

10.1Pesquisa na Wikipedia. No programa do console


Tarefa:

escreva um programa que leia a consulta de pesquisa no console e exiba o resultado da pesquisa na Wikipedia. A tarefa é dividida em 4 etapas:
  1. Solicitação de leitura
  2. Faça uma solicitação ao servidor
  3. Analisar a resposta
  4. Imprimir resultado

O primeiro e o quarto pontos não precisam de muita explicação, vamos nos concentrar na solicitação ao servidor.

Essa tarefa também pode ser dividida em várias etapas:

  1. Geração de solicitação
  2. Pedido do servidor
  3. Preparando para o Processamento de Resposta
  4. Processamento de resposta

Vamos considerar isso com mais detalhes: a

geração de solicitações de
API fornece a capacidade de fazer consultas de pesquisa sem chaves. Dessa maneira, aproximadamente:

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

Você pode abrir esse link em um navegador e ver o resultado da solicitação.
No entanto, para que a solicitação seja bem-sucedida, remova caracteres inválidos do link, ou seja, faça a codificação Porcentagem , que também é a codificação de URL.
Para fazer isso, em Java, você pode usar o método de codificação estática na classe URLEncoder , desta forma:

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

É isso aí, o URL está pronto! Agora resta fazer uma solicitação ao servidor ...

Solicitação ao servidor
Para solicitações GET e POST, você pode usar a classe HttpURLConnection . Este é o mais simples. Basta criar, abrir uma conexão e obter um InputStream . Nem precisamos ler, o Gson fará isso por nós .
Você também pode usar o retrofit ou algo semelhante.

Preparando para processar a resposta
O servidor retorna dados no formato JSON .
Mas não precisamos analisá-lo manualmente, pois existe uma biblioteca Gson do Google.
Exemplos estão aqui:
https://github.com/google/gson
https://habrahabr.ru/company/naumen/blog/228279/

Se ainda houver tempo, você pode escrever o recibo do artigo selecionado durante a pesquisa e assim por diante.

10.2 Tarefa final - utilitário do console para baixar arquivos via HTTP


Utilitário de console para baixar arquivos via HTTP ... soa familiar? Sim, é isso - A história de uma tarefa de teste . Tudo é lógico - a tarefa final do curso Java está no mesmo nível da tarefa de teste para a posição de desenvolvedor Java Júnior.
E essa é uma tarefa muito boa - sem complicações, mas abrange uma ampla variedade de tópicos. E você pode ver imediatamente como o autor estrutura o código, usa diferentes abordagens e padrões, usa a própria linguagem e a biblioteca padrão.

10.3 Tarefa final - tempo Telegram-bot


Tarefa:

Escreva um bot para o Telegram, que será:

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

Você pode usar https://openweathermap.org/api para obter a previsão .

Essa tarefa é mais a capacidade e a capacidade de entender a nova tecnologia (bot-api) e as diferentes bibliotecas. E você precisa configurar uma VPN! E você tem que escrever o código, é claro.

A propósito, um fato interessante é que a maioria dos estudantes ignora a frase "local enviado" e a possibilidade de enviá-lo. Eles escrevem um bot que espera o nome da cidade. Não sei porque. Isso geralmente funciona mal , o código fica um pouco mais complicado, mas eles continuam a fazê-lo.

10.4 Tarefa final - reconhecimento de manuscrito


Objetivo:

implementar um programa para classificar números manuscritos.
Essa tarefa já está mais focada na implementação do algoritmo, na capacidade de entendê-los. Normalmente, o código para os alunos não é muito estruturado.

Descrição da tarefa
Conforme o conjunto de dados a ser estudado, será usada a base das imagens dos dígitos manuscritos MNIST . As imagens neste banco de dados têm uma resolução de 28x28 e são armazenadas como um conjunto de valores em escala de cinza. O banco de dados inteiro é dividido em duas partes: treinamento, composto por 50.000 imagens e teste - 10.000 imagens.

Para resolver este problema, propõe-se implementar o método de k vizinhos mais próximos- algoritmo métrico para classificação automática de objetos. O princípio básico do método dos vizinhos mais próximos é que o objeto seja atribuído à classe que é mais comum entre os vizinhos desse elemento.
Os vizinhos são obtidos com base em muitos objetos cujas classes já são conhecidas e, com base no valor-chave de k para esse método, calcula-se qual classe é a mais numerosa dentre elas. Como distância entre objetos, você pode usar a métrica euclidiana, ou seja, a distância usual entre pontos no espaço.

Requisitos Você

deve escrever um programa que reconheça números manuscritos. Deve ser possível inicializar uma determinada classe com dados para treinamento e fornecer um método para reconhecer uma única imagem.

Além da implementação do próprio algoritmo, você deve escrever um código para verificar sua precisão (calcular a taxa de erro). Para fazer isso, use 10.000 imagens de teste.
Além de calcular a precisão, propõe-se realizar um experimento: em vez da métrica euclidiana, use a distância dos quarteirões da cidade , o ângulo entre os vetores ou qualquer outra coisa e verifique a qualidade do reconhecimento.

Opcional

Se tudo funcionar bem, você poderá complicar um pouco mais a tarefa. Adicionando, por exemplo, a eliminação do ruído (emissões) ou o uso do método da janela Parzenovsky para aumentar a precisão.

Mais algumas palavras


Se você tiver tarefas legais que possa oferecer aos alunos (um tempo aproximado de solução é de uma ou duas horas), compartilhe-as nos comentários.

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


All Articles