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édio1.1 Classificação da matriz1.2 Localizando primos1.3 Removendo de uma matrizNoções básicas de OOP
2.0 Projetando e criando uma classe que descreve um vetor2.1 Geração de um elemento aleatório com peso2.2 Lista vinculadaRecursão
3.0 Pesquisa binária3.1 Encontre a raiz da equação3.2 Árvore de Pesquisa BináriaHerança
4.0 Implementar uma hierarquia de classes que descreva formas tridimensionais4.1 Implementar uma hierarquia de classes que descreva figuras tridimensionais - 24.2 Implementar uma hierarquia de classes que descreva formas tridimensionais - 34.3 Implementar uma hierarquia de classes que descreva formas tridimensionais - 4Linhas
5.0 Dicionário de letras de frequênciaClasses abstratas e interfaces
6.0 Conversor de temperatura6.1 Construtor de cordas com suporte a desfazer6.2 Statebuilding Stringbuilder (padrão do observador)6.4 Preenchendo uma matriz com FunctionColecções
7.0 Dicionário de Palavras de Frequência7.1 Coleção sem duplicatas7.2 ArrayList e LinkedList7.3 Escreva um iterador sobre uma matriz7.4 Escreva um iterador sobre uma matriz bidimensional7.5 Um iterador ainda mais complexo7.6 Iterador sobre dois iteradores7.7 Elementos de contagem7.8 Alterar chaves e valores no mapaMultithreading
8.0 Estados8.1 Sincronização de threads8.2 Fabricante do ConsumidorAnotações
9.0 Anotação personalizada - criação e uso com reflexão10.0 Número de restrições rodoviárias10.1 Pesquisa na Wikipedia. No programa do console10.2 Tarefa final - utilitário do console para baixar arquivos via HTTP10.3 Tarefa final - tempo Telegram-bot10.4 Tarefa final - reconhecimento de manuscritoO 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];
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}; 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]; } }
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;
2.0 Projetando e criando uma classe de vetores
Desafio:Crie uma classe que descreva um vetor (no espaço tridimensional).
Ele deve ter:
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 {
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 {
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: class RandomFromArray { private int[] values;
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;
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. LinkedListDesafio: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: 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); } private static int binarySearchRecursively (double[] sortedArray, double key, int low, int high) { int middle = (low + high) / 2;
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:
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ápidaImplementação Java de uma árvore binária com hashHeranç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 {
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 {
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 {
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 é
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: 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);
Ou então:
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: 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;
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: interface OnStringBuilderChangeListener { void onChange(OvservableStringBuilder stringBuilder); } class OvservableStringBuilder {
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]; } }
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]; } }
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);
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 alfabeto7.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: 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: 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 {
Adicione WAITING e BLOCKED: 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();
Para TIMED_WAITING, altere um pouco o mesmo código: 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();
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 {
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:
Consumidor:
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) {
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 exemplarA 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:- Solicitação de leitura
- Faça uma solicitação ao servidor
- Analisar a resposta
- 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:- Geração de solicitação
- Pedido do servidor
- Preparando para o Processamento de Resposta
- Processamento de resposta
Vamos considerar isso com mais detalhes: ageração de solicitações deAPI 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 servidorPara 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 respostaO 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/gsonhttps://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 tarefaConforme 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.OpcionalSe 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.