Java Practical Tasks - für Kurse und andere Aktivitäten

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 Durchschnitt
1.1 Array-Sortierung
1.2 Primzahlen finden
1.3 Entfernen aus einem Array

OOP-Grundlagen


2.0 Entwerfen und Erstellen einer Klasse, die einen Vektor beschreibt
2.1 Erzeugung eines zufälligen Elements mit einer Gewichtung
2.2 Verknüpfte Liste

Rekursion


3.0 Binäre Suche
3.1 Finden Sie die Wurzel der Gleichung
3.2 Binärer Suchbaum

Vererbung


4.0 Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt
4.1 Implementieren Sie eine Klassenhierarchie, die dreidimensionale Figuren beschreibt - 2
4.2 Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt - 3
4.3 Implementieren Sie eine Klassenhierarchie, die dreidimensionale Formen beschreibt - 4

Linien


5.0 Frequenzwörterbuch der Buchstaben

Abstrakte Klassen und Schnittstellen


6.0. Temperaturwandler
6.1. Stringbuilder mit Rückgängig-Unterstützung
6.2. Statebuilding Stringbuilder (Beobachtermuster)
6.4. Füllen eines Arrays mit Function

Sammlungen


7.0 Frequenzwörterbuch der Wörter
7.1. Sammlung ohne Duplikate
7.2. ArrayList und LinkedList
7.3. Schreiben Sie einen Iterator über ein Array
7.4. Schreiben Sie einen Iterator über ein zweidimensionales Array
7.5. Ein noch komplexerer Iterator
7.6. Iterator über zwei Iteratoren
7.7. Elemente zählen
7.8. Ändern Sie Schlüssel und Werte in Map

Multithreading


8.0. Staaten
8.1. Thread-Synchronisation
8.2. Verbraucher Hersteller

Anmerkungen


9.0. Benutzerdefinierte Anmerkung - Erstellung und Verwendung mit Reflexion

Letzte und andere Aufgaben


10.0 Anzahl der Straßenbeschränkungen
10.1. Wikipedia-Suche. Im Konsolenprogramm
10.2. Letzte Aufgabe - Konsolendienstprogramm zum Herunterladen von Dateien über HTTP
10.3. Letzte Aufgabe - Wetter Telegramm-Bot
10.4. Letzte Aufgabe - Handschrifterkennung

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

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; //      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. Entwerfen und Erstellen einer Vektorklasse


Herausforderung:

Erstellen Sie eine Klasse, die einen Vektor beschreibt (im dreidimensionalen Raum).

Er muss haben:

  • Konstruktor mit Parametern in Form einer Liste von Koordinaten x, y, z
  • Methode, die die Länge eines Vektors berechnet. Die Wurzel kann mit Math.sqrt () berechnet werden:

     sqrtx2+y2+z2

  • Berechnungsmethode für skalare Produkte:

    x1x2+y1y2+z1z2

  • Methode zur Berechnung eines Vektorprodukts mit einem anderen Vektor:

    (y1z2z1y2,z1x2x1z2,x1y2y1x2)

  • Eine Methode, die den Winkel zwischen Vektoren (oder den Kosinus eines Winkels) berechnet: Der Kosinus des Winkels zwischen Vektoren ist gleich dem Skalarprodukt von Vektoren geteilt durch das Produkt der Module (Längen) von Vektoren:

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

  • Methoden für Summe und Differenz:

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

    (x1x2,y1y2,z1z2)


  • Eine statische Methode, die eine ganze Zahl N verwendet und ein Array von Zufallsvektoren der Größe N zurückgibt.

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

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 { //    ,    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. 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:

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

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

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. LinkedList

3.0 Binäre Suche


Herausforderung:

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:

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

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

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 Implementierung
Java-Implementierung eines Hash-Binärbaums

Vererbung


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

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 { //   @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. 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 { //   @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. 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
Bild


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:

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

Oder so:

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

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:

 /** * 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 (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:

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

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]; } } // 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. 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); // 0, 1, 4, 9, 16 ..... } 

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 Alphabets

7.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:

 /** * @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. Iterator über zwei Iteratoren


Aufgabe:

Schreiben Sie einen Iterator, der zwei Iteratoren durchläuft.

Lösung:

 /** * @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. 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 { //         TERMINATED: thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(thread.getState()); 

In WAITING und VERRIEGELTER

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

Ändern Sie für TIMED_WAITING den gleichen Code ein wenig:

  /** *   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. 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 { //     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. 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:

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

Verbraucher:

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

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

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:

  • Pfad zur Datendatei
  • Datum

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:
  1. Anfrage lesen
  2. Stellen Sie eine Anfrage an den Server
  3. Analysieren Sie die Antwort
  4. 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:

  1. Anforderungsgenerierung
  2. Serveranforderung
  3. Vorbereitung für die Antwortverarbeitung
  4. Antwortverarbeitung

Lassen Sie uns dies genauer betrachten: Die

Generierung von
API- 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 Server
Fü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 Antwort
Der 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/gson
https://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.

Aufgabenbeschreibung
Die 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 Sie

mü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.

Optional

Wenn 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.

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


All Articles