Java Practical Tasks - für Kurse und andere Aktivitäten
Ein paar einleitende Worte
In den letzten Jahren unterrichte ich Java-Programmierung. Im Laufe der Zeit änderte sich dies - manchmal wurden verschiedene Teile hinzugefügt oder weggeworfen, die Reihenfolge der Themen geändert, der Ansatz zum Erstellen eines Klassenplans selbst geändert und so weiter. Das heißt, der Kurs wurde verbessert. Eines der Hauptprobleme bei der Vorbereitung des Kurses sind Aufgaben. Über sie und wird diskutiert.
Tatsache ist, dass jede meiner Klassen aus zwei Teilen besteht. Zunächst bin ich Dozent - ich erzähle Ihnen Codebeispiele zu einem neuen Thema (Klassen, Vererbung, Generika usw.). Der zweite Teil ist praktisch. Offensichtlich macht es keinen Sinn, nur über das Programmieren zu sprechen, Sie müssen programmieren. Die Priorität im Klassenzimmer ist das Lösen von Problemen, das heißt, etwas irgendwie zu programmieren. Das Programmieren im Klassenzimmer unterscheidet sich vom Programmieren zu Hause, da Sie im Unterricht eine Frage stellen, den Code anzeigen, eine schnelle Bewertung des Codes erhalten, Kommentare zur Verbesserung abgeben und das Geschriebene korrigieren können. Es war sehr einfach, Aufgaben für den ersten Unterricht zu finden. Aufgaben für Schleifen, bedingte Anweisungen und OOP (schreiben Sie beispielsweise die Klasse "Dog" oder die Klasse "Vector"). Mit Diensten wie
Leetcode können Sie sogar online überprüfen, ob die Lösung solcher Probleme sofort
korrekt ist . Aber welche Aufgaben sollten die Schüler in einer Lektion erhalten, die Sammlungen gewidmet war? Streams? Was ist mit Anmerkungen? Ich habe mehrere Jahre lang mehrere solcher Aufgaben entwickelt oder überarbeitet, und dieser Artikel ist in der Tat eine Sammlung dieser Probleme (eine Lösung ist mit einigen Problemen verbunden).
Natürlich sind alle Aufgaben schon irgendwo aufgetaucht. Dieser Artikel richtet sich jedoch an Lehrer von Programmierkursen (für Sprachen ähnlich wie Java werden die meisten Aufgaben ausgeführt) oder an diejenigen, die privat Programmieren unterrichten. Diese Aufgaben können in Ihren Klassen "out of the box" verwendet werden. Java-Lernende können auch versuchen, sie zu lösen. Solche Entscheidungen erfordern jedoch eine Überprüfung und Bewertung durch Dritte.
Einige der einfachsten Aufgaben, die jeder seit Jahrzehnten verwendet, habe ich auch in diesen Artikel aufgenommen. Vielleicht, um nicht sofort mit abstrakten Klassen zu beginnen.
Anregungen und Anregungen sind willkommen!
Aufgabenliste
Die Grundlagen
1.0. Maximum, Minimum und Durchschnitt1.1 Array-Sortierung1.2 Primzahlen finden1.3 Entfernen aus einem ArrayOOP-Grundlagen
2.0 Entwerfen und Erstellen einer Klasse, die einen Vektor beschreibt2.1 Erzeugung eines zufälligen Elements mit einer Gewichtung2.2 Verknüpfte ListeRekursion
3.0 Binäre Suche3.1 Finden Sie die Wurzel der Gleichung3.2 Binärer SuchbaumVererbung
4.0 Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt4.1 Implementieren Sie eine Klassenhierarchie, die dreidimensionale Figuren beschreibt - 24.2 Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt - 34.3 Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt - 4Linien
5.0 Frequenzwörterbuch der BuchstabenAbstrakte Klassen und Schnittstellen
6.0. Temperaturwandler6.1. Stringbuilder mit Rückgängig-Unterstützung6.2. Statebuilding Stringbuilder (Beobachtermuster)6.4. Füllen eines Arrays mit FunctionSammlungen
7.0 Frequenzwörterbuch der Wörter7.1. Sammlung ohne Duplikate7.2. ArrayList und LinkedList7.3. Schreiben Sie einen Iterator über ein Array7.4. Schreiben Sie einen Iterator über ein zweidimensionales Array7.5. Ein noch komplexerer Iterator7.6. Iterator über zwei Iteratoren7.7. Elemente zählen7.8. Ändern Sie Schlüssel und Werte in MapMultithreading
8.0. Staaten8.1. Thread-Synchronisation8.2. Verbraucher HerstellerAnmerkungen
9.0. Benutzerdefinierte Anmerkung - Erstellung und Verwendung mit Reflexion10.0 Anzahl der Straßenbeschränkungen10.1. Wikipedia-Suche. Im Konsolenprogramm10.2. Letzte Aufgabe - Konsolendienstprogramm zum Herunterladen von Dateien über HTTP10.3. Letzte Aufgabe - Wetter Telegramm-Bot10.4. Letzte Aufgabe - HandschrifterkennungDie Grundlagen
1.0. Maximum, Minimum und Durchschnitt
Herausforderung:Füllen Sie das Array mit Zufallszahlen und drucken Sie den Maximal-, Minimal- und Durchschnittswert.
Verwenden
Sie zum Generieren einer Zufallszahl die Methode
Math.random () , die einen Wert im Intervall [0, 1] zurückgibt.
Lösung: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. Implementieren Sie einen Blasensortierungsalgorithmus , um ein Array zu sortieren
Lösung: 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. Suche nach Primzahlen
Herausforderung:Schreiben Sie ein Programm, das Primzahlen zwischen [2, 100] auf die Konsole druckt.
Verwenden Sie den Operator
% (Rest der Division) und Schleifen, um dieses Problem zu lösen.
Lösung: 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); } }
Oder mit
Etikettenschleifen :
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. Aus Array löschen
Herausforderung:Gegeben ein Array von ganzen Zahlen und eine andere ganze Zahl. Entfernen Sie alle Vorkommen dieser Nummer aus dem Array (es sollten keine Lücken vorhanden sein).
Lösung: 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]; } }
Sie können selbst eine Methode zum Schneiden des Endes des Arrays schreiben. Beachten Sie jedoch, dass die Standardmethode schneller funktioniert:
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; }
Wenn Sie diesen Weg gehen, können Sie jedoch zuerst ein Array mit der gewünschten Länge erstellen und es dann füllen:
public static int[] removeElement(int[] nums, int val) { int count = 0;
2.0. Entwerfen und Erstellen einer Vektorklasse
Herausforderung:Erstellen Sie eine Klasse, die einen Vektor beschreibt (im dreidimensionalen Raum).
Er muss haben:
Wenn die Methode einen Vektor zurückgibt, sollte sie ein neues Objekt zurückgeben und das Basisobjekt nicht ändern. Das heißt, Sie müssen die Vorlage "
Unveränderliches Objekt " implementieren
Lösung: public class Vector {
Sie können diese Klasse folgendermaßen verwenden:
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])); }
Sie können diese Lösung verallgemeinern und die Vektorklasse für eine beliebige Dimension schreiben:
public class Vector {
2.1. Generieren eines zufälligen Elements mit einem Gewicht
Herausforderung:Schreiben Sie eine Klasse, deren Konstruktor zwei Arrays akzeptiert: ein Array von Werten und ein Array von Gewichtungen von Werten.
Die Klasse muss eine Methode enthalten, die ein Element aus dem ersten Array unter Berücksichtigung seiner Gewichtung zufällig zurückgibt.
Ein Beispiel:
Ein Array ist gegeben [1, 2, 3] und ein Array von Gewichten [1, 2, 10].
Im Durchschnitt sollte der Wert
„1“ 2-mal weniger als der Wert
„2“ und zehnmal seltener als der Wert
„3“ zurückgeben .
Lösung: class RandomFromArray { private int[] values;
Da das Bereichsarray jedoch sortiert ist, können (und sollten) Sie eine binäre Suche verwenden:
public int getRandom() { int random = (int) (Math.random() * (sum - 1)); int index = Arrays.binarySearch(ranges, random); return values[index >= 0 ? index : -index - 2]; }
Es gibt eine andere Lösung für dieses Problem. Sie können ein Array erstellen, dessen Größe der Summe aller Gewichte entspricht. Dann reduziert sich die Auswahl eines zufälligen Elements auf die Erzeugung eines zufälligen Index. Das heißt, wenn ein Array von Werten [1, 2, 3] und ein Array von Gewichten [1, 2, 10] angegeben ist, können Sie sofort ein Array [1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] und extrahiere ein zufälliges Element daraus:
class RandomFromArray { private int[] extended_values;
Diese Lösung hat einen Vorteil - die Zeit zum Extrahieren eines zufälligen Elements
O (1) im Gegensatz zu log (n) in der vorherigen Lösung. Es erfordert jedoch viel Speicher:
O( sumn)
2.2. Verknüpfte Liste
Eine andere Aufgabe, die ich oft gebe, ist die Implementierung einer verknüpften Liste. Es kann in seiner einfachsten Form angegeben werden (nur
add () und
get () implementieren), oder Sie können die Implementierung von
java.util.List anfordern .
Ich werde nicht im Detail darauf eingehen, es gibt viele Artikel über die Implementierung einer verknüpften Liste in Java in Habr, zum Beispiel diesen:
Datenstrukturen in Bildern. LinkedListHerausforderung:Schreiben Sie eine Methode, die prüft, ob sich das angegebene Element im Array befindet oder nicht.
Verwenden Sie Aufzählung und binäre Suche, um dieses Problem zu lösen.
Vergleichen Sie die Laufzeit beider Lösungen für große Arrays (z. B. 100000000 Elemente).
Lösung: 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. Finden Sie die Wurzel der Gleichung
Herausforderung:Finden Sie die Wurzel der Gleichung
c o s ( x 5 ) + x 4 - 345 , 3 ∗ x - 23 = 0
auf dem Segment [0; 10] mit einer Genauigkeit in
x nicht schlechter als 0,001. Es ist bekannt, dass die Wurzel in diesem Intervall eindeutig ist.
Verwenden Sie dazu die
Halbierungsmethode (und Rekursion).
Lösung:
Hinweis: Wenn wir die erforderliche Genauigkeit nicht in
x , in
y erreichen möchten, sollte die Bedingung in der Methode neu geschrieben werden:
if(Math.abs(func(start)- func(end)) <= 0.001){ return start; }
Ein kleiner Trick: Wenn Sie bedenken, dass die Menge der Doppelwerte endlich ist (es gibt zwei benachbarte Werte, zwischen denen es keine Doppelwerte gibt), schreiben Sie die Bedingung für das Verlassen der Rekursion wie folgt um:
double x = start + (end - start) / 2; if(x == end || x == start){ return x; }
Somit erhalten wir die maximale Genauigkeit, die im Allgemeinen mit diesem Ansatz erzielt werden kann.
3.2. Binärer Suchbaum
Das Implementieren eines
binären Suchbaums ist eine hervorragende Aufgabe. Ich gebe es normalerweise, wenn es um Rekursion geht.
Ich werde nicht viel darüber schreiben, es gibt viele Artikel / Implementierungen ganz anderer Art:
Datenstrukturen: Binärbäume.Binärbaum, schnelle ImplementierungJava-Implementierung eines Hash-BinärbaumsVererbung
4.0. Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt
Herausforderung:Implementieren Sie eine Klassenhierarchie:

Die
Box- Klasse ist ein Container und kann andere Formen enthalten. Die
add () -Methode verwendet Shape als Eingabe. Wir müssen neue Formen hinzufügen, bis wir Platz für sie in der Box haben (wir werden nur das Volumen berücksichtigen und die Form ignorieren. Angenommen, wir gießen Flüssigkeit ein). Wenn nicht genügend Platz zum Hinzufügen einer neuen Form vorhanden ist, sollte die Methode
false zurückgeben .
Lösung: 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 {
Um nicht zu dieser Aufgabe zurückzukehren, werden nachfolgend einige weitere Variationen dieser Aufgabe beschrieben.
4.1. Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt - 2
Herausforderung:Implementieren Sie dieselbe Klassenhierarchie, aber machen Sie einige Klassen abstrakt.
Lösung: 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. Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt - 3
Herausforderung:Implementieren Sie dieselbe Klassenhierarchie, verwenden Sie jedoch Schnittstellen.
Darüber hinaus werden die Schüler aufgefordert, die Schnittstelle Comparable zu implementieren.
Lösung: 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. Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt - 4
Herausforderung:Fügen Sie der Klassenhierarchie eine Rotationsform für eine beliebige Funktion hinzu. Sie können das ungefähre Volumen mit einem bestimmten Integral berechnen. Da das Volumen der Rotationsfigur um die
x- Achse ist
V x = p i i n t b a f 2 ( x ) d x
Und das Integral ist
Dann können Sie eine Implementierung
der Rechteckmethode schreiben:
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()); }
Natürlich berücksichtigen wir hier nicht die Genauigkeit der Berechnungen und wählen nicht die Anzahl der Partitionen aus, um die erforderliche Genauigkeit zu erreichen. Dies ist jedoch eine Programmieraufgabe und keine numerische Methode. Daher lassen wir dies in den Lektionen weg.
Linien
Sie können viele Aufgaben pro Zeile finden. Normalerweise gebe ich diese:
- Schreiben Sie eine Methode, um die längste Zeichenfolge in einem Array zu finden.
- Schreiben Sie eine Methode, die prüft, ob ein Wort ein Palindrom ist .
- Schreiben Sie eine Methode, die im Text alle Vorkommen des Wortes
Bulk "byaka" durch "[ausgeschnitten" ersetzt
zensiert]. " - Es gibt zwei Zeilen. Schreiben Sie eine Methode, die die Anzahl der Vorkommen einer Zeile in einer anderen zurückgibt.
Ich werde die Lösungen für solche Probleme nicht beschreiben, und es gibt auch eine große Anzahl von Aufgaben für Zeichenfolgen.
Von den interessanteren mag ich diesen:
5.0. Frequenzwörterbuch der Buchstaben des russischen (oder englischen) Alphabets.
Herausforderung:Erstellen Sie ein Frequenzwörterbuch mit Buchstaben des russischen (oder englischen) Alphabets. Wir lassen das Problem der Auswahl und Analyse des Sprachkörpers aus, es wird ausreichen, den Text von kurzer Länge zu nehmen.
Lösung: 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);
Oder so:
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]); } }
Abstrakte Klassen und Schnittstellen
6.0. Temperaturwandler
Herausforderung:Schreiben Sie eine BaseConverter-Klasse, die von Grad
Celsius in konvertiert werden soll
Kelvin ,
Fahrenheit und so weiter. Die Methode muss eine
Konvertierungsmethode haben , die
und macht die Umwandlung.
Lösung: 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)); } }
Darüber hinaus können Sie nach einer Factory-Methode fragen : interface Converter { double getConvertedValue(double baseValue); public static Converter getInstance(){ Locale locale = Locale.getDefault(); String[] fahrenheitCountries = {"BS", "US", "BZ", "KY", "PW"}; boolean isFahrenheitCountry = Arrays.asList(fahrenheitCountries).contains(locale.getCountry()); if(isFahrenheitCountry){ return new FahrenheitConverter(); } else { return new CelsiusConverter(); } } }
6.1.Stringbuilder mit Rückgängig- Unterstützung
Aufgabe:Schreiben Sie Ihre StringBuilder-Klasse mit Unterstützung für den Rückgängig- Vorgang . Delegieren Sie dazu alle Methoden an den Standard- StringBuilder und speichern Sie in Ihrer eigenen Klasse eine Liste aller Operationen, die undo () ausführen soll . Dies wird die Umsetzung Vorlage seines „Team“ .Lösung: 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 (Beobachtermuster)
Aufgabe:Schreiben Sie Ihre StringBuilder-Klasse mit der Möglichkeit, andere Objekte über eine Änderung ihres Status zu benachrichtigen. Delegieren Sie dazu alle Methoden an den Standard- StringBuilder und implementieren Sie das Observer- Entwurfsmuster in Ihrer eigenen Klasse .Lösung: interface OnStringBuilderChangeListener { void onChange(OvservableStringBuilder stringBuilder); } class OvservableStringBuilder {
6.3. Filter
Problem:Write Method Filter , der das Eingangsarray empfängt (jeglicher Art), und die Implementierung der Schnittstelle Filter c Methode anwenden (Object o) , von der Anordnung zu entfernen , überflüssig.Überprüfen Sie, wie es bei Zeichenfolgen oder anderen Objekten funktioniert.Lösung:Normalerweise gebe ich diese Aufgabe vor Generics, sodass die Schüler eine Methode schreiben, ohne dass sie Object verwenden: 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]; } }
Mit Generika ist dies jedoch möglich. Dann können Sie den Standard verwenden die Funktion : 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. Array-Füllung
Eine Aufgabe, die der vorherigen etwas ähnlich ist:Schreiben Sie eine Füllmethode , die ein Array von Objekten und eine Implementierung der Funktionsschnittstelle (oder Ihrer eigenen) akzeptiert.Die Füllmethode sollte das Array füllen und den neuen Wert anhand des Index mithilfe der Implementierung der Funktionsschnittstelle abrufen. Das heißt, Sie möchten es so verwenden: public static void main(String[] args) { Integer[] squares = new Integer[100]; fill(squares, integer -> integer * integer);
Lösung: 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); } }
Sammlungen
7.0 Frequenzwörterbuch der Wörter
siehe das Problem über das Frequenzwörterbuch der Buchstaben des Alphabets7.1. Sammlung ohne Duplikate
Aufgabe:Schreiben Sie eine Methode, die eine Sammlung von Objekten als Eingabe empfängt und eine Sammlung ohne Duplikate zurückgibt.Lösung: public static <T> Collection<T> removeDuplicates(Collection<T> collection) { return new HashSet<>(collection);
7.2. ArrayList und LinkedList
Schreiben Sie eine Methode, die einer ArrayList und einer LinkedList 1.000.000 Elemente hinzufügt. Schreiben Sie eine andere Methode, die ein Element 100.000 Mal zufällig aus der gefüllten Liste auswählt. Messen Sie die dafür aufgewendete Zeit. Vergleichen Sie die Ergebnisse und schlagen Sie vor, warum sie sind.Lösung: 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. Schreiben Sie einen Iterator über ein Array
Lösung: 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. Zweidimensionaler Array-Iterator
Aufgabe:Schreiben Sie einen Iterator über ein zweidimensionales Array.Lösung: 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. Ein noch komplexerer Iterator
Ich mag diese Aufgabe. Sie erreicht nur wenige Schüler in der Gruppe, die relativ einfach mit früheren Aufgaben fertig werden.Aufgabe:Dan Iterator. Die next () -Methode gibt entweder einen String oder einen Iterator derselben Struktur zurück (dh, der wiederum entweder einen String oder denselben Iterator zurückgibt). Schreiben Sie auf diesen Iterator einen weiteren, bereits „flachen“.Lösung auf den Stapeln: 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(); } }
Die rekursive Lösung: 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. Iterator über zwei Iteratoren
Aufgabe:Schreiben Sie einen Iterator, der zwei Iteratoren durchläuft.Lösung: 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. Elemente zählen
Schreiben Sie eine Methode, die ein Eingabearray von Elementen vom Typ K (generisch) empfängt und Map <K, Integer> zurückgibt, wobei K der Wert aus dem Array und Integer die Anzahl der Einträge im Array ist.Das heißt, die Methodensignatur sieht folgendermaßen aus: <K> Map<K, Integer> arrayToMap(K[] ks);
Lösung: 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. Ändern Sie Schlüssel und Werte in Map
Schreiben Sie eine Methode, die Map <K, V> empfängt und Map zurückgibt, wobei die Schlüssel und Werte umgekehrt werden. Da die Werte möglicherweise übereinstimmen, ist der Wertetyp in Map nicht mehr K , sondern Sammlung <K>:
Map<V, Collection<K>>
Lösung: 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. Staaten
Aufgabe:Drucken Sie den Status des Streams vor dem Start, nach dem Start und zur Laufzeit.Lösung: Thread thread = new Thread() { @Override public void run() { System.out.println(getState()); } }; System.out.println(thread.getState()); thread.start(); try {
In WAITING und VERRIEGELTER 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();
Ändern Sie für TIMED_WAITING den gleichen Code ein wenig: 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. Thread-Synchronisation
Aufgabe:Schreiben Sie ein Programm, in dem zwei Threads erstellt werden, die nacheinander ihren Namen auf der Konsole anzeigen.Lösung: class StepThread extends Thread {
8.2. Hersteller-Verbraucher
Eine der klassischen Multithreading-Aufgaben. Gegeben zwei Ströme - Produzent und Konsument. Der Hersteller generiert einige Daten (im Beispiel Zahlen). Der Hersteller "konsumiert" sie.Zwei Streams teilen sich einen gemeinsamen Datenpuffer, dessen Größe begrenzt ist. Wenn der Puffer leer ist, muss der Verbraucher warten, bis die Daten dort angezeigt werden. Wenn der Puffer voll ist, muss der Hersteller warten, bis der Verbraucher die Daten nimmt und der Platz frei wird.Hersteller:
Verbraucher:
Erstellen und ausführen: 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. Eigene Anmerkung - Erstellung und Verwendung
Normalerweise gebe ich diese Aufgabe, wenn es um Anmerkungen und Reflexionen geht. Gleichzeitig können Sie über Executors , ThreadPoolExecutor und andere sprechen .Aufgabe:Erstellen Sie Ihre Wiederholungsanmerkung mit einem ganzzahligen Parameter.Extend Klasse ThreadPoolExecutor und überschreiben das Verfahren auszuführen , wie folgt: Wenn eine Instanz von Runnable mit Anmerkungen versehen wird , das Wiederholen , dann seine Methode Lauf wird mehrmals ausgeführt (die Anzahl der Parameter in vorgegebenen Repeat ).Das heißt, indem Sie diese Klasse schreiben: @Repeat(3) class MyRunnable implements Runnable{ @Override public void run() { System.out.println("Hello!"); } }
und mit ihm: public static void main(String[] strings) { CustomThreadPoolExecutor customThreadPoolExecutor = new CustomThreadPoolExecutor(10); customThreadPoolExecutor.execute(new MyRunnable()); }
Wir sollten sehen: Hello! Hello! Hello!
Lösung: @Retention(RetentionPolicy.RUNTIME) @interface Repeat { int value(); } class CustomThreadPoolExecutor extends ThreadPoolExecutor { public CustomThreadPoolExecutor(int corePoolSize) {
Letzte und andere Aufgaben
Während des Kurses gebe ich den Schülern einige schwierige Aufgaben - für die gesamte Lektion. Es ist erforderlich, ein kleines Programm mit dem zuvor erlernten zu schreiben. Komplexität entsteht hier übrigens oft. Die Lösung für die Probleme beim Schreiben einer Methode ist eine Sache, aber um einen Algorithmus zu entwickeln, denken Sie an alles, was Sie zuvor studiert haben, und schreiben Sie sofort 50 Zeilen in Java. Aber in der Lektion kann ich sie in die richtige Richtung lenken, helfen, Probleme zu lösen, zu entwerten, die richtigen Klassen und Methoden zu finden und so weiter. Einige dieser Aufgaben werden nachfolgend beschrieben. In dieser Form gebe ich sie meinen Schülern.Darüber hinaus sollte am Ende des Kurses jeder die endgültige Aufgabe abschließen. Das heißt, zu Hause, alleine, schreiben Sie ein Programm. Etwas komplizierter. Ich gebe Ihnen die Möglichkeit, eine von mehreren Optionen zu wählen. Interessant ist übrigens, dass Sie mindestens ein Programm schreiben müssen oder mehrere gleichzeitig schreiben können. Es scheint, dass ich mich nur an eine Person erinnere, die mehr als eine geschrieben hat.10.0 Anzahl der Straßenbeschränkungen
Eine kleine Aufgabe, die zeigt, wie Java zur Lösung praktischer Probleme eingesetzt werden kann.Datenaufbereitung:Mit dem Portal offenen Daten Sankt Petersburg , die geladen werden Daten über die Begrenzung der Bewegung des Verkehrs für die Dauer der Arbeiten im Format der CSV .Aufgabe:Es muss ermittelt werden, wie viele Straßenbeschränkungen zu einem bestimmten Zeitpunkt in der Stadt gelten.Das Programm verwendet zwei Parameter als Argument:Das heißt, es beginnt wie folgt: java TrafficBlocks "PATH_TO_CSV_FILE" dd.MM.yyyy
An diesem Datum muss die Anzahl der geltenden Verkehrsbeschränkungen abgeleitet werden.Beispielhafter AlgorithmusÜbrigens bemerkte immer nur eine Person, dass das Datumsformat in den Daten (JJJJMMTT) so ist, dass sie nicht analysiert, sondern als Zeichenfolgen verglichen werden können. So kann die Lösung vereinfacht werden. Ich gebe diese Aufgabe, nachdem ich über Datum, Kalender, DateFormat gesprochen habe , also spreche ich über diese Vereinfachung, als sie bereits alles geschrieben haben.Ich bringe hier keine Lösung, sie können sehr unterschiedlich sein und jeder muss einzeln betrachtet werden.10.1.Wikipedia-Suche. Im Konsolenprogramm
Aufgabe:Schreiben Sie ein Programm, das die Suchabfrage von der Konsole liest und das Suchergebnis auf Wikipedia anzeigt. Die Aufgabe ist in 4 Phasen unterteilt:- Anfrage lesen
- Stellen Sie eine Anfrage an den Server
- Analysieren Sie die Antwort
- Ergebnis drucken
Der erste und vierte Punkt brauchen nicht viel Erklärung, lassen Sie uns auf die Anfrage an den Server eingehen.Diese Aufgabe kann auch in mehrere Phasen unterteilt werden:- Anforderungsgenerierung
- Serveranforderung
- Vorbereitung für die Antwortverarbeitung
- Antwortverarbeitung
Lassen Sie uns dies genauer betrachten: DieGenerierung vonAPI- Anforderungen bietet die Möglichkeit, Suchabfragen ohne Schlüssel durchzuführen. Auf diese Weise ungefähr: https://ru.wikipedia.org/w/api.php?action=query&list=search&utf8=&format=json&srsearch="Java"
Sie können diesen Link in einem Browser öffnen und das Ergebnis der Anfrage anzeigen.Damit die Anforderung jedoch erfolgreich ist, sollten Sie ungültige Zeichen aus dem Link entfernen, dh die Prozentcodierung durchführen , bei der es sich auch um die URL-Codierung handelt.Zu diesem Zweck können Sie in Java die statische Codierungsmethode in der URLEncoder- Klasse wie folgt verwenden : street = URLEncoder.encode(street, "UTF-8");
Das war's, die URL ist fertig! Es bleibt nun eine Anfrage an den Server ...Anfrage an den ServerFür GET- und POST-Anfragen können Sie die HttpURLConnection- Klasse verwenden . Das ist das einfachste. Einfach erstellen, eine Verbindung öffnen und einen InputStream erhalten . Wir müssen es nicht einmal lesen, Gson wird es für uns tun .Sie können auch Nachrüstungen oder ähnliches verwenden.Vorbereiten der Verarbeitung der AntwortDer Server gibt Daten im JSON-Format zurück .Wir müssen es jedoch nicht manuell analysieren, dafür gibt es eine Gson- Bibliothek von Google.Beispiele finden Sie hier:https://github.com/google/gsonhttps://habrahabr.ru/company/naumen/blog/228279/Wenn noch Zeit übrig ist, können Sie den Empfang des Artikels schreiben, der während der Suche ausgewählt wurde, und so weiter.10.2. Letzte Aufgabe - Konsolendienstprogramm zum Herunterladen von Dateien über HTTP
Konsolendienstprogramm zum Herunterladen von Dateien über HTTP ... kommt Ihnen das bekannt vor? Ja, das ist es - Die Geschichte einer Testaufgabe . Alles ist logisch - die letzte Aufgabe des Java-Kurses liegt auf dem gleichen Niveau wie die Testaufgabe für die Position des Junior Java-Entwicklers.Und das ist eine wirklich gute Aufgabe - unkompliziert, deckt aber eine Vielzahl von Themen ab. Und Sie können sofort sehen, wie der Autor den Code strukturiert, verschiedene Ansätze und Muster verwendet, die Sprache selbst und die Standardbibliothek verwendet.10.3. Letzte Aufgabe - Wetter Telegramm-Bot
Aufgabe:Schreiben Sie einen Bot für Telegramm, der sein wird:- .
- , . /subscribe. (/unsubscribe).
Sie können https://openweathermap.org/api verwenden , um die Prognose abzurufen .Diese Aufgabe beruht eher auf der Fähigkeit und Fähigkeit, die neue Technologie (bot-api) und verschiedene Bibliotheken zu verstehen. Und Sie müssen ein VPN einrichten! Und den Code muss man natürlich schreiben.Interessant ist übrigens, dass die meisten Schüler den Ausdruck „gesendeter Ort“ und die Möglichkeit des Sendens ignorieren. Sie schreiben einen Bot, der den Namen der Stadt erwartet. Ich weiß nicht warum. Dies funktioniert oft schlecht , der Code wird etwas komplizierter, aber sie tun dies weiterhin.10.4. Letzte Aufgabe - Handschrifterkennung
Ziel:Implementierung eines Programms zur Klassifizierung handschriftlicher Zahlen.Diese Aufgabe konzentriert sich bereits mehr auf die Implementierung des Algorithmus, die Fähigkeit, diese zu verstehen. Normalerweise ist der Code für Schüler nicht sehr strukturiert.AufgabenbeschreibungDie handgezeichnete Zifferndatenbank MNIST wird als untersuchter Datensatz verwendet . Bilder in dieser Datenbank haben eine Auflösung von 28 x 28 und werden als Satz von Graustufenwerten gespeichert. Die gesamte Datenbank ist in zwei Teile unterteilt: Schulung, bestehend aus 50.000 Bildern, und Test - 10.000 Bilder.Um dieses Problem zu lösen, wird vorgeschlagen, die Methode von k nächsten Nachbarn zu implementieren - einen metrischen Algorithmus zur automatischen Klassifizierung von Objekten. Das Grundprinzip der Methode der nächsten Nachbarn besteht darin, dass das Objekt der Klasse zugeordnet wird, die unter den Nachbarn dieses Elements am häufigsten vorkommt.Nachbarn werden auf der Grundlage vieler Objekte genommen, deren Klassen bereits bekannt sind, und basierend auf dem Schlüsselwert von k für diese Methode wird berechnet, welche Klasse die zahlreichste unter ihnen ist. Als Abstand zwischen Objekten können Sie die euklidische Metrik verwenden, dh den üblichen Abstand zwischen Punkten im Raum.Anforderungen Siemüssen ein Programm schreiben, das handschriftliche Zahlen erkennt. Es sollte möglich sein, eine bestimmte Klasse mit Trainingsdaten zu initialisieren und eine Methode zum Erkennen eines einzelnen Bildes bereitzustellen.Zusätzlich zur Implementierung des Algorithmus selbst sollten Sie Code schreiben, um dessen Genauigkeit zu überprüfen (Berechnen Sie die Fehlerrate). Verwenden Sie dazu 10.000 Testbilder.Zusätzlich zur Berechnung der Genauigkeit wird vorgeschlagen, ein Experiment durchzuführen: anstelle vonEuklidische Metriken verwenden den Abstand von Stadtblöcken , den Winkel zwischen Vektoren oder etwas anderem und überprüfen die Erkennungsqualität.OptionalWenn alles gut funktioniert, können Sie die Aufgabe etwas komplizierter machen. Durch Hinzufügen beispielsweise der Beseitigung von Rauschen (Emissionen) oder der Verwendung der Parzenovsky-Fenstermethode zur Erhöhung der Genauigkeit.Noch ein paar Worte
Wenn Sie coole Aufgaben haben, die Sie den Schülern anbieten können (eine ungefähre Lösungszeit beträgt ein oder zwei Stunden), teilen Sie sie in den Kommentaren mit.