
الوها!
لذا فقد انتهى أحد أكثر المؤتمرات تشددًا في عالم جافا - Joker 2018 ، الذي يُعقد تقليديًا في سان بطرسبرغ في Expoforum. وقد حضر المؤتمر هذا العام عدد قياسي من المشاركين. عرضت Odnoklassniki تقليديا لمساعدة المطورين لدينا على حل المشاكل غير التافهة التي تنشأ عند إنشاء واحد من أكثر مشاريع Java تحميلا.
أولئك الذين أجابوا على الأسئلة حصلوا على جوائز جيدة ، ونحن نقدم لك تحليلًا موجزًا لمشاكلنا. أخفينا الإجابات الصحيحة تحت المفسد ، chur ، لفتحها فقط بعد أن اكتشفنا أنفسنا الحل ؛-)
دعنا نذهب!
Deduplicator
يريد Cyril حفظ الذاكرة عن طريق إلغاء تكرار الكائنات التي تساويها في
equals()
. ساعده في تنفيذ طريقة الاستقطاع الآمنة للخيط عن طريق القياس باستخدام
String.intern
، ولكن ليس فقط للسلاسل.
public static Object dedup(Object obj) { }
الحلبعد خدش الجزء الخلفي من رأسه ، تمكن سيريل من التوصل إلى العديد من الخيارات لحل هذه المشكلة ، لكنهم كانوا جميعًا مخطئين إلى حد ما. ثم ، خدش أنفه
computeIfAbsent
عن
java.util.concurrent
، وتذكر طريقة
computeIfAbsent
الرائعة. ستقوم هذه الطريقة بتنفيذ لامدا التي تم تمريرها إليها في المعلمة فقط في حالة عدم وجود مفتاح في
Map
، وكتابة نتيجتها والعودة. إذا كان هذا المفتاح موجودًا بالفعل ، فلن يتم حساب لامدا ، وسيتم إرجاع القيمة الحالية المرتبطة بالمفتاح. بالإضافة إلى ذلك ، أشار كيريل إلى أنه بالنسبة لـ
ConcurrentHashMap
تعمل هذه الطريقة بشكل تلقائي ، مما يسمح لك بحل المشكلة بأناقة شديدة. كتب Satisfied Cyril هذا الرمز:
private static final ConcurrentHashMap map = new ConcurrentHashMap(); public static Object dedup(Object obj) { return map.computeIfAbsent(obj, o -> o); }
وخدش أنفه بسرور مرة أخرى.
عنوان IP
تقوم ديما بتطوير بروتوكول جديد للشبكة. صحح الخطأ في طريقته لترجمة عنوان IPv4 الذي يمثله صفيف بايت إلى سلسلة.
String ipToString(byte[] ip) { return ip[0] + '.' + ip[1] + '.' + ip[2] + '.' + ip[3]; }
الحلتم عرض الخطأ الأول على الفور من قبل IDE ، مما منع Dima من إضافة الطريقة حتى النهاية. الرمز
'.'
يضاف وجود
char
إلى البايت كنوع صحيح. استبدال
'.'
إلى
"."
، كان ديما سعيدًا جدًا بشأن الشفرة التي تم جمعها بنجاح لدرجة أنه أطلقها على الفور دون اختبار. "Ai-ai-ai ، Dima" ، اعتقدت JVM وأعطت بعض الهراء بدلاً من عنوان IP. على عكس Dima ، عرف JVM على وجه اليقين أنه في Java ، يتم استخدام نوع
byte
لتخزين الأرقام الموقعة ، أي أن جميع العناوين ذات الثماني بتات أكبر من 127 سيتم تمثيلها بأرقام سالبة في Java. وفقًا لقواعد صب هذه الأرقام إلى
int
، فإن العلامة السالبة للرقم هي نفسها الموجودة في البايت الأصلي. آه ، ديمتري ، كان من الضروري اتخاذ تدابير إضافية من أجل تجاهل جزء التوقيع ، على سبيل المثال مثل هذا:
return (ip[0] & 255) + "." + (ip[1] & 255) + "." + (ip[2] & 255) + "." + (ip[3] & 255)
خلاط
تحتاج مارينا إلى مزج عناصر القائمة بترتيب عشوائي. لماذا هذا الخيار غير مناسب ، وكيف ستصلحه؟
Random random = ThreadLocalRandom.current(); list.sort((o1, o2) -> { return random.nextBoolean() ? +1 : -1; });
الحلمن الواضح أن مارينا نسيت أن عقد Comparator
يتطلب الاستقرار: عند مقارنة قيمتين متطابقتين ، يجب أن تكون نتيجة المقارنة هي نفسها. وفي تنفيذ مارينا ، تكون النتيجة لكل زوج عشوائية تمامًا ، والتي يمكن أن تؤدي بسهولة إلى استثناء java.lang.IllegalArgumentException: Comparison method violates its general contract
! إذا قرأت مارينا الوثائق في المساء ، فستعرف أنه في هذه الحالة من الأفضل استخدام طريقة Collections.shuffle()
.
الجواب: انتهك عقد المقارنة. قد يؤدي التصنيف إلى استثناء. من الأفضل استخدام طريقة Collections.shuffle()
.
مشهد المهد الوظيفي
يحب Egor الكتابة بأسلوب وظيفي ، ولا يهتم بفعالية التعليمات البرمجية. تقدير عدد الكائنات التي تنشئها كل استدعاء لهذه الطريقة إذا تم تمرير
ArrayList
من 10 أسطر إليها؟
Predicate<String> equalsAny(List<String> list) { Predicate<String> p = s -> false; for (String s : list) { p = p.or(s::contains); } return p; }
الحلعلى عكس Yegor ، لا تحب ألينا المتحذوفة كتابة كل شيء بأسلوب وظيفي ، لأنها تعرف كيف تحسب التكاليف العامة. سطر واحد
p = p.or(s::contains);
يقوم بإنشاء كائنين في وقت واحد: أحدهما نتيجة استدعاء p.or()
، والثاني لإنشاء s::contains
المسند. هذا الأخير لا يمكن تخزينه مؤقتًا لأنه يلتقط المتغيرات في السياق. بالضرب في عدد التكرارات نحصل على 20 قطعة. ولكن يمكن أيضًا إنشاء Iterator
مخفي إذا لم يقم JIT بتحسينه. اعتقدت ألينا أن "20 أو حتى 21 قطعة ، إذا لم تكن محظوظًا ، آثم".
الجواب: 10 أسناد or
10 إسنادات contains
+ 1 Iterator
حسب تحسينات JIT.
يتحول مكسيم إلى أقصى حد
يحسب Maxim الحد الأقصى في برنامج متعدد الخيوط ، ولكنه يريد الاستغناء عن الأقفال. ساعده في إصلاح الخطأ.
AtomicLong max = new AtomicLong(); void addValue(long v) { if (v > max.get()) { max.set(v); } }
الحليا مكسيم! استخدام
AtomicLong
لا يجعل خيط البرنامج آمنًا. هناك عملية ذرية
AtomicLong.compareAndSwap
. وبدءًا من Java 8 ، ليس من الضروري على الإطلاق كتابة دورة CAS بنفسك ، لأن الطريقة الذرية الرائعة
accumulateAndGet
. وهنا من السهل استخدامه فقط:
void addValue(long v) { max.accumulateAndGet(v, Math::max); }