تحليل Odnoklassniki في Joker 2019



في الفترة من 28 إلى 29 أكتوبر ، عُقد Joker 2019 في سان بطرسبرغ - وهو المؤتمر الأكبر والأكثر المتشددين في اتساع روسيا المخصصة لتطوير جافا. أقيم الحدث للمرة السابعة ، وكما هو الحال دائمًا ، حطم الرقم القياسي للحضور ، استقطب هذا الحدث أكثر من 2000 متخصص.

يشارك زملاء الدراسة تقليديا في جوكر كشركاء في الحدث. هذا العام ، في جناحنا ، يمكن للمرء أن يحاول التعامل مع المهام "غير القابلة للحل" الشهيرة من كبار مهندسي OK.RU. حصل المشاركون في المؤتمر الذين أجابوا على الأسئلة بشكل صحيح على جوائز.

في الإنصاف ، يجب أن أقول أنه من بين 1000 منشور مع المهام التي قمنا بتسليمها ، تم إرجاع أقل من 100 منشور ، وكان الأفضل هو الحل الذي سجل 4.5 نقطة من أصل 5.

ننشر المهام وحلولها حتى تتمكن من اختبار قوتك.

1. التعداد البطولية


كود المصدر للعبة غير معروفة كشفت هذه الشفرة. ما هو التنفيذ السيئ لـ Group.of ، وكيفية إصلاحه؟

 enum Group { Few(1, 4), Several(5, 9), Pack(10, 19), Lots(20, 49), Swarm(50, Integer.MAX_VALUE); Group(int min, int max) { ... } public static Group of(int count) { for (Group group : Group.values()) { if (count >= group.min && count <= group.max) { return group; } } throw new IllegalArgumentException(); } } 

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

 private static final Group[] ALL_GROUPS = Group.values(); public static Group of(int count) { for (Group group : ALL_GROUPS) { .... } 


2. الأحلام


تم إصدار Java 13 بالفعل ، ولا يزال Nikolai يفهم التدفقات فقط. حدد الأخطاء في الطريقة التي تحسب الفرق بين الحد الأقصى والحد الأدنى لعناصر الدفق.

 int getDiameter(Stream<Integer> stream) { int min = stream.min(Integer::compare).get(); int max = stream.max(Integer::compare).get(); return max - min; } 

قرار
تكون التدفقات في Java عادةً لمرة واحدة: سيفشل استدعاء العملية الطرفية الثانية (في هذه الحالة ، max ):

 java.lang.IllegalStateException: stream has already been operated upon or closed 

بالإضافة إلى ذلك ، min و max return Optional ، فإن عملية get() التي ستلقي عليها NoSuchElementException لدفق فارغ. لذلك ، من الأصح التحقق من isPresent() قبل استدعاء get() أو استخدام طرق Optional أخرى: orElse ، orElseThrow ، إلخ.

أخيرًا ، لن يفلت الفرق بين int من int ولن يفلت من المطور الدقيق ، ومن المفيد تغيير نوع القيمة المرجعة إلى القيمة long .

3. عازلة آمنة


ByteBuffer مزامنة جافا البدائية التي يمكن أن تجعل put get عمليات موضوع آمن على ByteBuffer عام؟

 final ByteBuffer buf = ByteBuffer.allocate(SIZE); int get(int offset) { return buf.get(offset); } void put(int offset, int value) { buf.putInt(offset, value); } 

اختر الخيار الأكثر فاعلية إذا كنت تعلم أن هناك العديد من مؤشرات الترابط ، ثم يتم تشغيلها كثيرًا أكثر من وضع.

  • تزامن
  • ReentrantLock
  • ReentrantReadWriteLock
  • StampedLock
  • الملوحة جهاز
  • القراءة والكتابة int في جاوة دائماً الذرية

قرار
يطالب ReentrantReadWriteLock القارئ والكاتب ، وغالبًا ما يكون هذا حلاً فعالًا. لكن لاحظ أنه في هذه الحالة ، تكون عمليات get and put بسيطة للغاية - إن احتمال أن يتعارض مع وضع منافس مع أمر صغير ، علاوة على ذلك ، من غير المرجح أن تحدث شروط وضع مع عمليات put. لذلك ، يمكنك تطبيق آلية القفل المتفائلة التي توفرها StampedLock .

سيكون StampedLock أكثر كفاءة من ReentrantReadWriteLock نظرًا لحقيقة أنه في حالة نجاح المسار السريع المتفائل ، لا يتم تحديث المتغيرات المشتركة على الإطلاق ، في حين تنفذ ReentrantReadWriteLock CAS واحدة على الأقل في أفضل الأحوال.

4. الهدايا


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

 interface Present { long getId(); Date getCreated(); } void add(Present p) { // Implement me } 

قرار
TreeSet أو PriorityQueue مناسبًا بشكل طبيعي كهيكل بيانات من أجل إضافة الهدايا بشكل فعال وحذف الأقدم وليس أسوأ من O (سجل N). كل الحيلة موجودة فقط في المقارنة: لا يكفي مقارنة الهدايا فقط مع getCreated() ، لأن تاريخ الإنشاء لا يجب أن يكون فريدًا. لذلك ، تحتاج إلى مقارنة أولاً بواسطة getCreated() ، ثم عن طريق getId() . سيضمن هذا المقارنة كلاً من تفرد العناصر والطلب حسب التاريخ.

 TreeSet<Present> tree = new TreeSet<>( Comparator.comparing(Present::getCreated) .thenComparing(Present::getId)); 

تظل مسألة صغيرة: عند إضافة هدية ، تحقق من أن الحجم لا يتجاوز N ، وإذا لزم الأمر ، احذف العنصر الأول والأقدم من المجموعة.

 void add(Present p) { if (tree.add(p) && tree.size() > N) { tree.pollFirst(); } } 


5. لن تنتظر


لماذا جوليا لن تنتظر نهاية هذا البرنامج؟

 var executor = Executors.newFixedThreadPool(4); for (File f : File.listRoots()) { executor.submit(() -> f.delete()); } executor.awaitTermination(2, TimeUnit.HOURS); 

قرار
تقترح وثائق awaitTermination أن طلب إيقاف التشغيل يجب أن يسبق التنفيذ. الأمر بسيط: لقد نسيت جوليا الاتصال بمديرها .

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


All Articles