مهام 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 تنفيذ التسلسل الهرمي للفصل الذي يصف الأشكال ثلاثية الأبعاد - 24.2 تطبيق التسلسل الهرمي للفصل الذي يصف الأشكال ثلاثية الأبعاد - 34.3 تطبيق التسلسل الهرمي للفصل الذي يصف الأشكال ثلاثية الأبعاد - 4خطوط
5.0 قاموس تردد الحروففئات مجردة والواجهات
6.0. محول درجة الحرارة6.1. Stringbuilder مع دعم التراجع6.2. State Stringbuilder (نمط المراقب)6.4. ملء مجموعة مع وظيفةمجموعات
7.0 قاموس تردد الكلمات7.1. جمع دون التكرارات7.2. ArrayList و LinkedList7.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. المهمة النهائية - فائدة وحدة لتحميل الملفات عبر HTTP10.3. المهمة النهائية - الطقس Telegram-bot10.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];
الحل: 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}; 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]; } }
يمكنك كتابة طريقة لقص ذيل الصفيف بنفسك ، لكن تجدر الإشارة إلى أن الطريقة القياسية ستعمل بشكل أسرع:
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;
2.0. تصميم وإنشاء فئة ناقلات
التحدي:قم بإنشاء فصل يصف المتجه (في مساحة ثلاثية الأبعاد).
يجب أن يكون لديه:
إذا أرجعت الطريقة متجهًا ، فيجب عليها إرجاع كائن جديد ، وليس تغيير الأساس. هذا هو ، تحتاج إلى تنفيذ قالب "
كائن غير قابل للتغيير "
الحل: public class Vector {
يمكنك استخدام هذه الفئة مثل هذا:
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 {
2.1. توليد عنصر عشوائي مع الوزن
التحدي:اكتب فصلًا يأخذ منشئه صفيفين: مجموعة من القيم ومجموعة من أوزان القيم.
يجب أن يحتوي الفصل على طريقة تعيد عنصرًا من الصفيف الأول عشوائيًا ، مع مراعاة وزنه.
مثال:
يتم إعطاء صفيف [1 ، 2 ، 3] ، ومجموعة من الأوزان [1 ، 2 ، 10].
في المتوسط ، يجب أن تُرجع القيمة
"1" مرتين أقل من القيمة
"2" وعشر مرات أقل من القيمة
"3" .
الحل: class RandomFromArray { private int[] values;
ولكن نظرًا
لتصنيف مجموعة
النطاقات ، يمكنك (ويجب) استخدام بحث ثنائي:
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;
هذا الحل له ميزة - الوقت لاستخراج عنصر عشوائي
O (1) ، على النقيض من log (n) في الحل السابق. ومع ذلك ، فإنه يتطلب الكثير من الذاكرة:
O( sumn)
2.2. قائمة مرتبطة
المهمة الأخرى التي أقدمها غالبًا هي تنفيذ قائمة مرتبطة. يمكن تقديمها بأبسط أشكالها (تطبيق
add () و
get () فقط ) ، أو يمكنك طلب تنفيذ
java.util.List .
لن أتناول هذا بالتفصيل ، فهناك العديد من المقالات حول تنفيذ قائمة مرتبطة في Java في Habr ، على سبيل المثال هذه:
هياكل البيانات في الصور. LinkedListالتحدي:اكتب طريقة تتحقق مما إذا كان العنصر المحدد موجودًا في الصفيف أم لا.
استخدم التعداد والبحث الثنائي لحل هذه المشكلة.
مقارنة وقت تشغيل كلا الحلين للصفائف الكبيرة (مثل 100000000 عنصر)
الحل: public static int bruteForce(double[] array, double key) { for (int i = 0; i < array.length; i++) { if (array[i] == key) return i; } return -1; } public static int binarySearchRecursively(double[] sortedArray, double key) { return binarySearchRecursively(sortedArray, key, 0, sortedArray.length); } private static int binarySearchRecursively (double[] sortedArray, double key, int low, int high) { int middle = (low + high) / 2;
3.1. أوجد جذر المعادلة
التحدي:أوجد جذر المعادلة
c o s ( x 5 ) + x 4 - 345.3 ∗ x - 23 = 0
على الجزء [0؛ 10] بدقة
× ليست أسوأ من 0.001. من المعروف أن الجذر فريد في هذا الفاصل الزمني.
استخدم
طريقة النصف (والعودية) لهذا الغرض.
الحل:
ملاحظة: إذا أردنا تحقيق الدقة المطلوبة وليس في
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 {
من أجل عدم العودة إلى هذه المهمة ، يتم وصف العديد من الأشكال المختلفة لهذه المهمة أدناه.
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 {
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 {
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 قاموس تردد الحروف الأبجدية الروسية (أو الإنجليزية).
التحدي:بناء قاموس تردد الحروف الأبجدية الروسية (أو الإنجليزية). لقد أغفلنا مشكلة اختيار نص اللغة وتحليله ، فستكون كافية لإدخال نص قصير المدة).
الحل: 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);
أو هكذا:
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 القياسي ، وقم بتخزين قائمة من جميع العمليات للتراجع عن (في) الفصل الدراسي الخاص بك . سيكون هذا هو تنفيذ قالب الفريق .الحل: class UndoableStringBuilder { private interface Action{ void undo(); } private class DeleteAction implements Action{ private int size; public DeleteAction(int size) { this.size = size; } public void undo(){ stringBuilder.delete( stringBuilder.length() - size, stringBuilder.length()); } } private StringBuilder stringBuilder;
6.2. State Stringbuilder (نمط المراقب)
المهمة:اكتب فئة StringBuilder الخاصة بك ، مع القدرة على إعلام الكائنات الأخرى بأي تغيير في حالته. للقيام بذلك ، قم بتفويض كافة الطرق إلى StringBuilder القياسي ، وقم بتطبيق نمط تصميم Observer في الفصل الدراسي الخاص بك .الحل: interface OnStringBuilderChangeListener { void onChange(OvservableStringBuilder stringBuilder); } class OvservableStringBuilder {
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]; } }
ولكن ، من الممكن مع الوراثة. ثم يمكنك استخدام الوظيفة القياسية : public class Main { public static <T> T[] filter(T[] array, Function<? super T, Boolean> filter) { int offset = 0; for (int i = 0; i < array.length; i++) { if (!filter.apply(array[i])) { offset++; } else { array[i - offset] = array[i]; } }
6.4. ملء الصفيف
مهمة تشبه إلى حد ما المهمة السابقة:اكتب طريقة تعبئة تقبل مجموعة من الكائنات وتطبيق واجهة دالة (أو واجهة خاصة بك).يجب أن تملأ طريقة التعبئة الصفيف ، وتحصل على القيمة الجديدة بواسطة فهرس باستخدام تطبيق واجهة الوظيفة. هذا هو ، تريد استخدامه مثل هذا: public static void main(String[] args) { Integer[] squares = new Integer[100]; fill(squares, integer -> integer * integer);
الحل: 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(); } }
الحل العودية: 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. مكرر أكثر من اثنين التكرار
المهمة:اكتب مكرر يمر عبر اثنين من التكرارات.الحل: 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 {
إضافة الانتظار والمغلق: 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();
بالنسبة إلى TIMED_WAITING ، قم بتغيير نفس الرمز قليلاً: public static void main(String[] strings) throws InterruptedException { Object lock = new Object(); Thread thread = new Thread() { @Override public void run() { try { synchronized (lock) { lock.notifyAll(); lock.wait(3000); } } catch (InterruptedException e) { e.printStackTrace(); } } }; synchronized (lock) { thread.start();
8.1 تزامن الموضوع
المهمة:اكتب برنامجًا يتم فيه إنشاء مؤشرات ترابط تعرض اسمها على وحدة التحكم بدورها.الحل: class StepThread extends Thread {
8.2. الشركة المصنعة للمستهلك
واحدة من المهام multithreading الكلاسيكية. إعطاء اثنين تيارات - المنتج والمستهلك. تقوم الشركة المصنعة بإنشاء بعض البيانات (في المثال ، الأرقام). الشركة المصنعة "يستهلك" لهم.مشاركة دفقين مخزن بيانات شائع ، حجمه محدود. إذا كان المخزن المؤقت فارغًا ، يجب على العميل انتظار ظهور البيانات هناك. إذا كان المخزن المؤقت ممتلئًا ، يجب على الشركة المصنعة الانتظار حتى يأخذ المستهلك البيانات ويصبح المكان مجانيًا.الشركة المصنعة:
المستهلك:
إنشاء وتشغيل: 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) {
المهام النهائية وغيرها
خلال الدورة ، أعطي الطلاب العديد من المهام الصعبة - للدرس بأكمله. مطلوب لكتابة برنامج صغير باستخدام المستفادة سابقا. بالمناسبة ، غالبًا ما ينشأ التعقيد هنا. يعد حل مشكلات كتابة طريقة واحدة شيئًا واحدًا ، ولكن الخروج بخوارزمية وتذكر أن كل ما درسته سابقًا وكذلك كتابة 50 سطرًا في Java على الفور مختلف تمامًا. لكن في الدرس ، يمكنني دفعهم في الاتجاه الصحيح ، والمساعدة في حل المشكلات ، وإلغاء الأخطاء ، والعثور على الفئات والأساليب المناسبة ، وما إلى ذلك. يتم وصف العديد من هذه المهام أدناه. في هذا النموذج ، أعطيهم لطلابي.بالإضافة إلى ذلك ، في نهاية الدورة ، يجب على الجميع إكمال المهمة النهائية. هذا هو ، في المنزل ، لوحدك ، اكتب البرنامج. أكثر تعقيدا قليلا. أنا أعطيك الفرصة لاختيار واحد من عدة خيارات. بالمناسبة ، هناك حقيقة مثيرة للاهتمام وهي أنك تحتاج إلى كتابة برنامج واحد على الأقل ، أو يمكنك كتابة عدة برامج مرة واحدة. يبدو أنني أتذكر شخصًا واحدًا فقط كتب أكثر من شخص.10.0 عدد قيود الطريق
مهمة صغيرة توضح كيفية تطبيق Java لحل المشكلات العملية.إعداد البيانات:من بوابة البيانات المفتوحة في سان بطرسبرغ ، نقوم بتحميل البيانات على تقييد حركة المرور لفترة الإنتاج بتنسيق CSV .المهمة:يجب تحديد عدد القيود المفروضة على الطريق في المدينة في تاريخ معين.يأخذ البرنامج معلمتين كوسيطة:- المسار إلى ملف البيانات
- تاريخ
وهذا هو ، ويبدأ على النحو التالي: java TrafficBlocks "PATH_TO_CSV_FILE" dd.MM.yyyy
من الضروري استنتاج عدد قيود الحركة المطبقة في هذا التاريخ.الخوارزمية المثاليةبالمناسبة ، لاحظ شخص واحد فقط طوال الوقت أن تنسيق التاريخ في البيانات (yyyyMMdd) هو أنه لا يمكن تحليلها ، ولكن مقارنة بالسلاسل. لذلك يمكن تبسيط الحل. أعطي هذه المهمة بعد الحديث عن التاريخ ، التقويم ، DateFormat ، لذلك أنا أتحدث عن هذا التبسيط عندما قاموا بالفعل بكتابة كل شيء.لا أحمل حلاً هنا ، يمكن أن يكون كثيرًا مختلفًا ويجب أن يتم النظر في كل منها على حدة.10.1.ويكيبيديا البحث. في برنامج وحدة التحكم
المهمة:اكتب برنامجًا يقرأ استعلام البحث من وحدة التحكم ويعرض نتيجة البحث في ويكيبيديا. تنقسم المهمة إلى 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/gsonhttps://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 لزيادة الدقة.بضع كلمات أخرى
إذا كانت لديك مهام رائعة يمكنك تقديمها للطلاب (وقت الحل التقريبي ساعة أو ساعتان) ، فقم بمشاركتها في التعليقات.