مهام Java العملية - للدورات والأنشطة الأخرى

مهام Java العملية - للدورات والأنشطة الأخرى


بضع كلمات تمهيدية


خلال السنوات القليلة الماضية ، قمت بتدريس برمجة جافا. بمرور الوقت ، تغيرت - ثم تمت إضافة أجزاء مختلفة ، وأحيانا يتم تجاهلها ، وتسلسل الموضوعات التي تم تغييرها ، وتغيرت طريقة بناء خطة للفصول الدراسية ذاتها ، وهكذا. وهذا هو ، تم تحسين الدورة. واحدة من المشاكل الرئيسية التي واجهتها في إعداد الدورة هي المهام. عنهم وسيتم مناقشتها.

الحقيقة هي أن كل فصل من فصولي يتكون من جزأين. في البداية ، أعمل محاضرًا - أخبركم بأمثلة الكود حول بعض الموضوعات الجديدة (الفصول ، الميراث ، الأدوية العامة ، وما إلى ذلك). الجزء الثاني هو عملي. من الواضح ، أنه لا معنى للتحدث فقط عن البرمجة ، فأنت بحاجة إلى البرنامج. الأولوية في الفصل هي حل المشكلات ، أي برمجة شيء ما بطريقة ما. تختلف البرمجة في الفصل الدراسي عن البرمجة في المنزل ، كما في الفصل الدراسي ، يمكنك طرح سؤال ، وإظهار الرمز ، والحصول على تقييم سريع للكود ، والتعليقات على التحسين ، وتصحيح المكتوبة. كان من السهل جدًا العثور على مهام للدروس الأولى. مهام الحلقات والعبارات الشرطية و OOP (على سبيل المثال ، اكتب الفئة "كلب" أو الفئة "ناقل"). تسمح لك الخدمات مثل كود leetcode بالتحقق من صحة حل هذه المشكلات فورًا ، عبر الإنترنت. ولكن ما هي المهام التي ينبغي إعطاء الطلاب في درس مخصص للمجموعات؟ تيارات؟ ماذا عن الشروح؟ لقد توصلت إلى العديد من هذه المهام أو نقحتها لعدة سنوات ، وهذه المقالة هي في الواقع مجموعة من هذه المشكلات (الحل مرفق ببعض المشاكل).

بالطبع ، ظهرت جميع المهام بالفعل في مكان ما. ومع ذلك ، فإن هذه المقالة موجهة إلى معلمي دورات البرمجة (بالنسبة للغات المشابهة لـ Java ، فإن معظم المهام ستقوم بها) ، أو أولئك الذين يقومون بتدريس البرمجة على انفراد. يمكن استخدام هذه المهام "خارج الصندوق" في الفصول الدراسية. يمكن لمتعلمي Java أيضًا محاولة حلها. لكن مثل هذه القرارات تتطلب التحقق والتقييم من طرف ثالث.

بعض من أبسط المهام التي يستخدمها الجميع منذ عقود ، لقد قمت أيضًا بتضمينها في هذه المقالة. ربما ، حتى لا تبدأ على الفور مع فصول مجردة.

أي أفكار واقتراحات هي موضع ترحيب!

قائمة المهام


الأساسيات


1.0. الحد الأدنى والحد الأدنى والمتوسط
1.1 ترتيب الفرز
1.2 إيجاد الأعداد الأولية
1.3 إزالة من مجموعة

أساسيات OOP


2.0 تصميم وإنشاء فصل يصف المتجه
2.1 توليد عنصر عشوائي مع وزن
2.2 قائمة مرتبطة

العودية


3.0 بحث ثنائي
3.1 أوجد جذر المعادلة
3.2 شجرة البحث الثنائية

الميراث


4.0 قم بتطبيق تسلسل هرمي للفئة يصف الأشكال ثلاثية الأبعاد
4.1 تنفيذ التسلسل الهرمي للفصل الذي يصف الأشكال ثلاثية الأبعاد - 2
4.2 تطبيق التسلسل الهرمي للفصل الذي يصف الأشكال ثلاثية الأبعاد - 3
4.3 تطبيق التسلسل الهرمي للفصل الذي يصف الأشكال ثلاثية الأبعاد - 4

خطوط


5.0 قاموس تردد الحروف

فئات مجردة والواجهات


6.0. محول درجة الحرارة
6.1. Stringbuilder مع دعم التراجع
6.2. State Stringbuilder (نمط المراقب)
6.4. ملء مجموعة مع وظيفة

مجموعات


7.0 قاموس تردد الكلمات
7.1. جمع دون التكرارات
7.2. ArrayList و LinkedList
7.3. اكتب مكرر على صفيف
7.4. اكتب مكررًا على صفيف ثنائي الأبعاد
7.5. التكرار أكثر تعقيدا
7.6. مكرر أكثر من اثنين التكرار
7.7. عد العناصر
7.8. تغيير المفاتيح والقيم في الخريطة

تعدد


8.0. الولايات
8.1 تزامن الموضوع
8.2. الصانع المستهلك

الشروح


9.0. شرح توضيحي مخصص - إنشاء واستخدام مع التفكير

المهام النهائية وغيرها


10.0 عدد قيود الطريق
10.1. ويكيبيديا البحث. في برنامج وحدة التحكم
10.2. المهمة النهائية - فائدة وحدة لتحميل الملفات عبر HTTP
10.3. المهمة النهائية - الطقس Telegram-bot
10.4. المهمة النهائية - التعرف على خط اليد

الأساسيات


1.0. الحد الأدنى والحد الأدنى والمتوسط


التحدي:

املأ المصفوفة بأرقام عشوائية واطبع القيمة القصوى والدنيا والمتوسطة.

لإنشاء رقم عشوائي ، استخدم الطريقة Math.random () ، والتي تُرجع قيمة في الفاصل الزمني [0 ، 1].

الحل:

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. تطبيق خوارزمية فرز فقاعة لفرز صفيف


الحل:

  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. البحث عن الأعداد الأولية


التحدي:

اكتب برنامجًا يطبع الأعداد الأولية على وحدة التحكم بين [2 ، 100].
استخدم عامل التشغيل ٪ (باقي القسمة) والحلقات لحل هذه المشكلة.

الحل:

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

أو باستخدام حلقات التسمية :

  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. حذف من مجموعة


التحدي:

إعطاء مجموعة من الأعداد الصحيحة وعدد صحيح آخر. أزل كل تكرارات هذا الرقم من الصفيف (يجب ألا تكون هناك فجوات).

الحل:

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

يمكنك كتابة طريقة لقص ذيل الصفيف بنفسك ، لكن تجدر الإشارة إلى أن الطريقة القياسية ستعمل بشكل أسرع:

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

ومع ذلك ، إذا ذهبت بهذه الطريقة ، يمكنك أولاً إنشاء مجموعة من الطول المطلوب ، ثم تعبئته:

 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. تصميم وإنشاء فئة ناقلات


التحدي:

قم بإنشاء فصل يصف المتجه (في مساحة ثلاثية الأبعاد).

يجب أن يكون لديه:

  • منشئ مع المعلمات في شكل قائمة الإحداثيات س ، ص ، ض
  • الطريقة التي تحسب طول ناقل. يمكن حساب الجذر باستخدام Math.sqrt ():

     sqrtx2+y2+z2

  • طريقة حساب المنتج العددية:

    x1x2+y1y2+z1z2

  • طريقة حساب منتج ناقل مع ناقل آخر:

    (y1z2z1y2،z1x2x1z2،x1y2y1x2)

    ،،
  • الطريقة التي تحسب الزاوية بين المتجهات (أو جيب تمام الزاوية): جيب تمام الزاوية بين المتجهات يساوي ناتج العددية للمتجهات مقسومًا على ناتج الوحدات (أطوال) المتجهات:

     frac(a،b)|a| cdot|b|

    ،
  • طرق الجمع والاختلاف:

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

    ،،

    (x1x2،y1y2،z1z2)

    ،،


  • طريقة ثابتة تأخذ عددًا صحيحًا N وتُرجع مجموعة من المتجهات العشوائية بحجم N.

إذا أرجعت الطريقة متجهًا ، فيجب عليها إرجاع كائن جديد ، وليس تغيير الأساس. هذا هو ، تحتاج إلى تنفيذ قالب " كائن غير قابل للتغيير "

الحل:

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

يمكنك استخدام هذه الفئة مثل هذا:

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

يمكنك تعميم هذا الحل وكتابة فئة Vector للحصول على بعد تعسفي:

 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. توليد عنصر عشوائي مع الوزن


التحدي:

اكتب فصلًا يأخذ منشئه صفيفين: مجموعة من القيم ومجموعة من أوزان القيم.
يجب أن يحتوي الفصل على طريقة تعيد عنصرًا من الصفيف الأول عشوائيًا ، مع مراعاة وزنه.
مثال:
يتم إعطاء صفيف [1 ، 2 ، 3] ، ومجموعة من الأوزان [1 ، 2 ، 10].
في المتوسط ​​، يجب أن تُرجع القيمة "1" مرتين أقل من القيمة "2" وعشر مرات أقل من القيمة "3" .

الحل:

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

ولكن نظرًا لتصنيف مجموعة النطاقات ، يمكنك (ويجب) استخدام بحث ثنائي:

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

هناك حل آخر لهذه المشكلة. يمكنك إنشاء صفيف بحجمه يساوي مجموع كل الأوزان. ثم يأتي اختيار عنصر عشوائي لتوليد فهرس عشوائي. أي إذا تم إعطاء صفيف من القيم [1 ، 2 ، 3] ، ومجموعة من الأوزان [1 ، 2 ، 10] ، فيمكنك إنشاء صفيف على الفور [1 ، 2 ، 2 ، 3 ، 3 ، 3 ، 3 ، 3 ، 3 ، 3 ، 3 ، 3 ، 3 ، 3 ، 3] واستخلص عنصرًا عشوائيًا منه:

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

هذا الحل له ميزة - الوقت لاستخراج عنصر عشوائي O (1) ، على النقيض من log (n) في الحل السابق. ومع ذلك ، فإنه يتطلب الكثير من الذاكرة:

O( sumn)



2.2. قائمة مرتبطة


المهمة الأخرى التي أقدمها غالبًا هي تنفيذ قائمة مرتبطة. يمكن تقديمها بأبسط أشكالها (تطبيق add () و get () فقط ) ، أو يمكنك طلب تنفيذ java.util.List .
لن أتناول هذا بالتفصيل ، فهناك العديد من المقالات حول تنفيذ قائمة مرتبطة في Java في Habr ، على سبيل المثال هذه:
هياكل البيانات في الصور. LinkedList

3.0 بحث ثنائي


التحدي:

اكتب طريقة تتحقق مما إذا كان العنصر المحدد موجودًا في الصفيف أم لا.
استخدم التعداد والبحث الثنائي لحل هذه المشكلة.
مقارنة وقت تشغيل كلا الحلين للصفائف الكبيرة (مثل 100000000 عنصر)
الحل:

  /*  ,   .    ,  -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. أوجد جذر المعادلة


التحدي:

أوجد جذر المعادلة

c o s ( x 5 ) + x 4 - 345.3 x - 23 = 0

على الجزء [0؛ 10] بدقة × ليست أسوأ من 0.001. من المعروف أن الجذر فريد في هذا الفاصل الزمني.
استخدم طريقة النصف (والعودية) لهذا الغرض.

الحل:

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

ملاحظة: إذا أردنا تحقيق الدقة المطلوبة وليس في x ، في y ، فيجب إعادة كتابة الشرط في الطريقة:

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

خدعة صغيرة: بالنظر إلى أن مجموعة القيم المزدوجة محدودة (هناك قيمتان متجاورتان لا توجد بينهما قيمتان مزدوجتان) ، أعد كتابة الشرط للخروج من العودية كما يلي:

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

وبالتالي ، نحصل على أقصى درجات الدقة التي يمكن الحصول عليها عمومًا باستخدام هذا النهج.

3.2. شجرة البحث الثنائية


يعد تنفيذ شجرة بحث ثنائية مهمة ممتازة. عادة ما أعطيها عندما يتعلق الأمر بالعودة.
لن أكتب الكثير عن هذا ، فهناك العديد من المقالات / التطبيقات من نوع مختلف تمامًا:
هياكل البيانات: الأشجار الثنائية.
شجرة ثنائية ، تنفيذ سريع
تنفيذ جافا لشجرة ثنائية مجزأة

الميراث


4.0. قم بتطبيق تسلسل هرمي للفصل يصف الأشكال ثلاثية الأبعاد


التحدي:

تطبيق التسلسل الهرمي للفئة:


فئة Box عبارة عن حاوية ، ويمكن أن تحتوي على أشكال أخرى. تأخذ طريقة add () الشكل كمدخل. نحتاج إلى إضافة أشكال جديدة حتى نمتلك مساحة لها في الصندوق (سننظر فقط في الحجم ، مع تجاهل الشكل. لنفترض أننا نصب السائل). إذا لم تكن هناك مساحة كافية لإضافة شكل جديد ، فيجب أن تعود الطريقة خطأ .

الحل:

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

من أجل عدم العودة إلى هذه المهمة ، يتم وصف العديد من الأشكال المختلفة لهذه المهمة أدناه.

4.1. قم بتطبيق تسلسل هرمي للفصل يصف الأشكال ثلاثية الأبعاد - 2


التحدي:

قم بتطبيق التسلسل الهرمي للفئة نفسها ، ولكن اجعل بعض الفئات مجردة.

الحل:

 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. قم بتطبيق تسلسل هرمي للفصل يصف الأشكال ثلاثية الأبعاد - 3


التحدي:

تطبيق التسلسل الهرمي للفئة نفسها ، ولكن باستخدام واجهات.
بالإضافة إلى ذلك ، يتم تشجيع الطلاب على تنفيذ واجهة قابلة للمقارنة .

الحل:

 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. قم بتطبيق تسلسل هرمي للفصل يصف الأشكال ثلاثية الأبعاد - 4


التحدي:

أضف شكل دوران إلى التسلسل الهرمي للفئة لوظيفة تعسفية. يمكنك حساب الحجم التقريبي باستخدام تكامل معين. منذ حجم الرقم دوران حول المحور س هو

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


والتكامل هو
الصورة


ثم يمكنك كتابة تطبيق لطريقة المستطيل :

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

بالطبع ، نحن لا نأخذ بعين الاعتبار دقة الحسابات هنا ، ولا نختار عدد الأقسام لتحقيق الدقة اللازمة ، لكن هذه مهمة برمجة ، وليست طرقًا رقمية ، لذلك نحن نحذفها في الفصل الدراسي.

خطوط


يمكنك العثور على الكثير من المهام في كل سطر. عادة ما أعطي هذه:

  • اكتب طريقة للعثور على أطول سلسلة في صفيف.
  • اكتب طريقة تتحقق مما إذا كانت الكلمة عبارة عن طبقة متناظرة .
  • اكتب طريقة تستبدل في النص جميع تكرارات كلمة Bulk "byaka" بـ "[cut
    رقابة]. "
  • هناك خطان. اكتب طريقة تُرجع عدد مرات تواجد صف واحد في آخر.

لن أصف الحلول لمثل هذه المشاكل ، وهناك عدد كبير من المهام للسلاسل أيضًا.

من الأكثر إثارة للاهتمام ، وأنا أحب هذا واحد:

5.0 قاموس تردد الحروف الأبجدية الروسية (أو الإنجليزية).


التحدي:

بناء قاموس تردد الحروف الأبجدية الروسية (أو الإنجليزية). لقد أغفلنا مشكلة اختيار نص اللغة وتحليله ، فستكون كافية لإدخال نص قصير المدة).

الحل:

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

أو هكذا:

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

فئات مجردة والواجهات


6.0. محول درجة الحرارة


التحدي:

اكتب فئة BaseConverter لتحويلها من درجات مئوية إلى
كلفن ، فهرنهايت ، وهلم جرا. يجب أن يكون الأسلوب وسيلة تحويل ، والتي
وهل التحويل.

الحل:

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

بالإضافة إلى ذلك ، يمكنك أن تطلب تنفيذ طريقة المصنع ، شيء مثل هذا:

 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 مع دعم التراجع


المهمة:

اكتب فئة StringBuilder الخاصة بك مع دعم لعملية التراجع . للقيام بذلك ، قم بتفويض كافة الطرق إلى StringBuilder القياسي ، وقم بتخزين قائمة من جميع العمليات للتراجع عن (في) الفصل الدراسي الخاص بك . سيكون هذا هو تنفيذ قالب الفريق .

الحل:

 /** * 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. State Stringbuilder (نمط المراقب)


المهمة:

اكتب فئة StringBuilder الخاصة بك ، مع القدرة على إعلام الكائنات الأخرى بأي تغيير في حالته. للقيام بذلك ، قم بتفويض كافة الطرق إلى StringBuilder القياسي ، وقم بتطبيق نمط تصميم Observer في الفصل الدراسي الخاص بك .

الحل:

 /** .  ,      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. مرشح


المشكلة:

كتابة أسلوب تصفية ، الذي يستقبل مجموعة المدخلات (من أي نوع)، وتنفيذ واجهة تصفية طريقة ج تطبيق (كائن س) ، لإزالة من مجموعة لا لزوم لها.
تحقق كيف يعمل على الأوتار أو الأشياء الأخرى.

الحل:
عادةً ما أقوم بهذه المهمة قبل الأدوية العامة ، بحيث يكتب الطلاب طريقة بدونهم باستخدام كائن:

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

ولكن ، من الممكن مع الوراثة. ثم يمكنك استخدام الوظيفة القياسية :

 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. ملء الصفيف


مهمة تشبه إلى حد ما المهمة السابقة:
اكتب طريقة تعبئة تقبل مجموعة من الكائنات وتطبيق واجهة دالة (أو واجهة خاصة بك).
يجب أن تملأ طريقة التعبئة الصفيف ، وتحصل على القيمة الجديدة بواسطة فهرس باستخدام تطبيق واجهة الوظيفة. هذا هو ، تريد استخدامه مثل هذا:

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

الحل:

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

مجموعات



7.0 قاموس تردد الكلمات


رؤية مشكلة حول قاموس تردد الحروف الأبجدية

7.1. جمع دون التكرارات


المهمة:

اكتب طريقة تتلقى مجموعة من الكائنات كمدخلات ، وتقوم بإرجاع مجموعة دون التكرارات.

الحل:

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

7.2. ArrayList و LinkedList


اكتب طريقة تضيف 1،000،000 عنصر إلى ArrayList و LinkedList. اكتب طريقة أخرى تختار عنصرًا عشوائيًا 100000 مرة من القائمة المملوءة. قياس الوقت الذي يقضيه على ذلك. قارن النتائج واقترح سبب وجودها.

الحل:

  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. اكتب مكرر على صفيف


الحل:

 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. مكرر مجموعة ثنائية الأبعاد


المهمة:

اكتب مكررًا على صفيف ثنائي الأبعاد.

الحل:

 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. التكرار أكثر تعقيدا


أنا أحب هذه المهمة. تصل إلى عدد قليل من الطلاب في المجموعة الذين يسهل عليهم التعامل مع المهام السابقة نسبيًا.

المهمة:

دان التكرار. تُرجع الطريقة التالية () إما سلسلة أو مكررة من نفس البنية (أي ، والتي تُرجع مرة أخرى إما سلسلة أو نفس التكرار). اكتب أعلى هذا التكرار آخر ، بالفعل "مسطح".

الحل على المداخن:

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

الحل العودية:

 /** * @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. مكرر أكثر من اثنين التكرار


المهمة:

اكتب مكرر يمر عبر اثنين من التكرارات.

الحل:

 /** * @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. عد العناصر


اكتب طريقة تتلقى صفيف إدخال لعناصر النوع K (عام) وإرجاع الخريطة <K ، Integer> ، حيث K هي القيمة من الصفيف ، و Integer هو عدد الإدخالات في الصفيف.
أي أن توقيع الطريقة يبدو كالتالي:

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

الحل:

 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. تغيير المفاتيح والقيم في الخريطة


اكتب طريقة تتلقى خريطة <K ، V> وترجع خريطة ، حيث يتم عكس المفاتيح والقيم. نظرًا لأن القيم قد تتزامن ، لن يكون نوع القيمة في الخريطة هو K ، ولكن
 المجموعة <K>: 

 Map<V, Collection<K>> 

الحل:

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

تعدد


8.0. الولايات


المهمة:

اطبع حالة الدفق قبل أن تبدأ ، وبعد أن تبدأ ، وفي وقت التشغيل.

الحل:

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

إضافة الانتظار والمغلق:

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

بالنسبة إلى TIMED_WAITING ، قم بتغيير نفس الرمز قليلاً:

  /** *   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 تزامن الموضوع


المهمة:

اكتب برنامجًا يتم فيه إنشاء مؤشرات ترابط تعرض اسمها على وحدة التحكم بدورها.

الحل:

 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. الشركة المصنعة للمستهلك


واحدة من المهام multithreading الكلاسيكية. إعطاء اثنين تيارات - المنتج والمستهلك. تقوم الشركة المصنعة بإنشاء بعض البيانات (في المثال ، الأرقام). الشركة المصنعة "يستهلك" لهم.
مشاركة دفقين مخزن بيانات شائع ، حجمه محدود. إذا كان المخزن المؤقت فارغًا ، يجب على العميل انتظار ظهور البيانات هناك. إذا كان المخزن المؤقت ممتلئًا ، يجب على الشركة المصنعة الانتظار حتى يأخذ المستهلك البيانات ويصبح المكان مجانيًا.

الشركة المصنعة:

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

المستهلك:

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

إنشاء وتشغيل:

 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. التعليق التوضيحي الخاص - إنشاء واستخدام


عادةً ما أقوم بهذه المهمة عندما يتعلق الأمر بالشروح والتأمل. في الوقت نفسه ، يمكنك التحدث عن Executors و ThreadPoolExecutor وغيرهم.

الهدف:

خلق الشرح الخاص كرر مع معلمة عدد صحيح.
توسيع الطبقة ThreadPoolExecutor وتجاوز أسلوب تنفيذ على النحو التالي: إذا مثيل Runnable والمشروح مع وكرر ، ثم منهجه المدى يتم تنفيذ عدة مرات (العدد المحدد من قبل المعلمة في وكرر ).

وهذا هو ، من خلال كتابة هذا الفصل:

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

واستخدامه:

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

يجب أن نرى:

 Hello! Hello! Hello! 

الحل:

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

المهام النهائية وغيرها


خلال الدورة ، أعطي الطلاب العديد من المهام الصعبة - للدرس بأكمله. مطلوب لكتابة برنامج صغير باستخدام المستفادة سابقا. بالمناسبة ، غالبًا ما ينشأ التعقيد هنا. يعد حل مشكلات كتابة طريقة واحدة شيئًا واحدًا ، ولكن الخروج بخوارزمية وتذكر أن كل ما درسته سابقًا وكذلك كتابة 50 سطرًا في Java على الفور مختلف تمامًا. لكن في الدرس ، يمكنني دفعهم في الاتجاه الصحيح ، والمساعدة في حل المشكلات ، وإلغاء الأخطاء ، والعثور على الفئات والأساليب المناسبة ، وما إلى ذلك. يتم وصف العديد من هذه المهام أدناه. في هذا النموذج ، أعطيهم لطلابي.

بالإضافة إلى ذلك ، في نهاية الدورة ، يجب على الجميع إكمال المهمة النهائية. هذا هو ، في المنزل ، لوحدك ، اكتب البرنامج. أكثر تعقيدا قليلا. أنا أعطيك الفرصة لاختيار واحد من عدة خيارات. بالمناسبة ، هناك حقيقة مثيرة للاهتمام وهي أنك تحتاج إلى كتابة برنامج واحد على الأقل ، أو يمكنك كتابة عدة برامج مرة واحدة. يبدو أنني أتذكر شخصًا واحدًا فقط كتب أكثر من شخص.

10.0 عدد قيود الطريق


مهمة صغيرة توضح كيفية تطبيق Java لحل المشكلات العملية.

إعداد البيانات:
من بوابة البيانات المفتوحة في سان بطرسبرغ ، نقوم بتحميل البيانات على تقييد حركة المرور لفترة الإنتاج بتنسيق CSV .

المهمة:

يجب تحديد عدد القيود المفروضة على الطريق في المدينة في تاريخ معين.

يأخذ البرنامج معلمتين كوسيطة:

  • المسار إلى ملف البيانات
  • تاريخ

وهذا هو ، ويبدأ على النحو التالي:

 java TrafficBlocks "PATH_TO_CSV_FILE" dd.MM.yyyy 

من الضروري استنتاج عدد قيود الحركة المطبقة في هذا التاريخ.

الخوارزمية المثالية


بالمناسبة ، لاحظ شخص واحد فقط طوال الوقت أن تنسيق التاريخ في البيانات (yyyyMMdd) هو أنه لا يمكن تحليلها ، ولكن مقارنة بالسلاسل. لذلك يمكن تبسيط الحل. أعطي هذه المهمة بعد الحديث عن التاريخ ، التقويم ، DateFormat ، لذلك أنا أتحدث عن هذا التبسيط عندما قاموا بالفعل بكتابة كل شيء.

لا أحمل حلاً هنا ، يمكن أن يكون كثيرًا مختلفًا ويجب أن يتم النظر في كل منها على حدة.

10.1.ويكيبيديا البحث. في برنامج وحدة التحكم


المهمة:

اكتب برنامجًا يقرأ استعلام البحث من وحدة التحكم ويعرض نتيجة البحث في ويكيبيديا. تنقسم المهمة إلى 4 مراحل:
  1. قراءة الطلب
  2. تقديم طلب إلى الخادم
  3. تحليل الجواب
  4. نتيجة الطباعة

لا تحتاج النقطتان الأولى والرابعة إلى الكثير من التوضيح ، دعونا نتناول الطلب على الخادم.

يمكن تقسيم هذه المهمة أيضًا إلى عدة مراحل:

  1. طلب الجيل
  2. طلب خادم
  3. التحضير لمعالجة الاستجابة
  4. معالجة الاستجابة

لننظر في هذا بمزيد من التفصيل: يوفر إنشاء

طلب
API القدرة على القيام باستعلامات البحث بدون مفاتيح. بهذه الطريقة ، تقريبًا:

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

يمكنك فتح هذا الرابط في متصفح وإلقاء نظرة على نتيجة الطلب.
ومع ذلك ، لكي ينجح الطلب ، يجب عليك إزالة الأحرف غير الصالحة من الارتباط ، أي القيام بترميز النسبة المئوية ، وهو أيضًا ترميز URL.
للقيام بذلك في جافا، يمكنك استخدام الأسلوب ثابت ترميز الطبقة URLEncoder ، كما يلي:

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

هذا كل شيء ، عنوان URL جاهز! يبقى الآن تقديم طلب إلى الخادم ...

طلب إلى الخادم
بالنسبة لطلبات GET و POST ، يمكنك استخدام فئة HttpURLConnection . هذا هو أبسط. فقط قم بإنشاء وفتح اتصال واحصل على InputStream . ليس علينا حتى قراءتها ، وسيقوم Gson بذلك من أجلنا .
يمكنك أيضا استخدام التحديثية ، أو شيء من هذا القبيل.

التحضير لمعالجة الاستجابة
يعرض الخادم البيانات بتنسيق JSON .
لكننا لسنا بحاجة إلى تحليلها يدويًا ، فهناك مكتبة Gson من Google.
الأمثلة موجودة هنا:
https://github.com/google/gson
https://habrahabr.ru/company/naumen/blog/228279/

إذا كان هناك وقت متبقٍ ، فيمكنك كتابة إيصال المقال المحدد أثناء البحث وما إلى ذلك.

10.2. المهمة النهائية - فائدة وحدة لتحميل الملفات عبر HTTP


أداة مساعدة لوحدة التحكم لتنزيل الملفات عبر HTTP ... هل تبدو مألوفة؟ نعم ، هذه هي - قصة مهمة اختبار واحدة . كل شيء منطقي - المهمة النهائية لدورة Java هي في نفس مستوى مهمة الاختبار لموضع مطور Junior Java.
وهذه مهمة جيدة حقًا - غير معقدة ، ولكنها تغطي مجموعة متنوعة من الموضوعات. ويمكنك أن ترى على الفور كيف يقوم المؤلف بتكوين الكود ، ويستخدم أساليب وأنماط مختلفة ، ويستخدم اللغة نفسها والمكتبة القياسية.

10.3. المهمة النهائية - الطقس Telegram-bot


المهمة:

اكتب روبوتًا لـ Telegram ، والذي سيكون:

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

يمكنك استخدام https://openweathermap.org/api للحصول على التوقعات .

هذه المهمة هي بالأحرى القدرة والقدرة على فهم التكنولوجيا الجديدة (bot-api) والمكتبات المختلفة. وتحتاج إلى إعداد VPN! وعليك أن تكتب الكود ، بالطبع.

بالمناسبة ، هناك حقيقة مثيرة للاهتمام وهي أن معظم الطلاب يتجاهلون عبارة "موقع الإرسال" وإمكانية إرسالها. يكتبون روبوت يتوقع اسم المدينة. لا اعرف السبب هذا غالباً ما يعمل بشكل سيء ، يصبح الكود أكثر تعقيدًا ، لكنهم مستمرون في القيام بذلك.

10.4. المهمة النهائية - التعرف على خط اليد


الهدف:

تنفيذ برنامج لتصنيف الأرقام المكتوبة بخط اليد.
هذه المهمة هي بالفعل أكثر تركيزا على تنفيذ الخوارزمية ، والقدرة على فهم تلك. عادة ما يكون كود الطلاب غير منظم للغاية.

وصف المهمة
عند استخدام مجموعة البيانات المراد دراستها ، سيتم استخدام قاعدة صور الأرقام المكتوبة بخط اليد MNIST . تبلغ دقة الصور في قاعدة البيانات هذه 28 × 28 ويتم تخزينها كمجموعة من قيم درجات الرمادي. تنقسم قاعدة البيانات بأكملها إلى قسمين: التدريب ، ويتألف من 50000 صورة ، والاختبار - 10000 صورة.

لحل هذه المشكلة ، يُقترح تنفيذ طريقة k أقرب جيران- خوارزمية متري للتصنيف التلقائي للأجسام. يتمثل المبدأ الأساسي لطريقة أقرب الجيران في تعيين الكائن للفئة الأكثر شيوعًا بين الجيران لهذا العنصر.
يتم أخذ الجيران على أساس العديد من الكائنات التي تكون فصولها معروفة بالفعل ، وبناءً على القيمة الأساسية لـ k لهذه الطريقة ، يتم حساب الفئة التي تعد الأكثر عددًا بينها. كمسافة بين الكائنات ، يمكنك استخدام مقياس الإقليدية ، أي المسافة المعتادة بين النقاط في الفضاء.

المتطلبات

يجب عليك كتابة برنامج يتعرف على الأرقام المكتوبة بخط اليد. يجب أن يكون من الممكن تهيئة فصل معين ببيانات للتدريب وتوفير طريقة للتعرف على صورة واحدة.

بالإضافة إلى تنفيذ الخوارزمية نفسها ، يجب عليك كتابة التعليمات البرمجية للتحقق من دقتها (حساب معدل الخطأ). للقيام بذلك ، استخدم 10000 صورة اختبار.
بالإضافة إلى حساب الدقة ، يُقترح إجراء تجربة: بدلاً من القياس الإقليدي ، استخدم مسافة كتل المدينة ، الزاوية بين المتجهات أو أي شيء آخر ، وتحقق من جودة التعرّف.

اختياري

إذا كان كل شيء يعمل بشكل جيد ، فيمكنك تعقيد المهمة أكثر من ذلك بقليل. عن طريق إضافة ، على سبيل المثال ، القضاء على الضوضاء (الانبعاثات) أو استخدام طريقة نافذة Parzenovsky لزيادة الدقة.

بضع كلمات أخرى


إذا كانت لديك مهام رائعة يمكنك تقديمها للطلاب (وقت الحل التقريبي ساعة أو ساعتان) ، فقم بمشاركتها في التعليقات.

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


All Articles