Teka-teki Joker 2018



Aloha!

Jadi salah satu konferensi paling keras di dunia Jawa - Joker 2018, yang secara tradisional berlangsung di St. Petersburg di Expoforum, telah berakhir. Tahun ini konferensi dihadiri oleh sejumlah peserta. Odnoklassniki secara tradisional ditawarkan untuk membantu pengembang kami memecahkan masalah non-sepele yang muncul saat membuat salah satu proyek Java yang paling banyak dimuat.

Mereka yang menjawab pertanyaan menerima hadiah dengan baik, dan kami menawarkan Anda analisis singkat tentang masalah kami. Kami menyembunyikan jawaban yang benar di bawah spoiler, chur, untuk membuka hanya setelah kami sendiri yang menemukan solusinya ;-)

Ayo pergi!

Deduplicator


Cyril ingin menghemat memori dengan menduplikasi objek sama equals() . Bantu dia menerapkan metode dedup thread-aman dengan analogi dengan String.intern , tetapi tidak hanya untuk string.

 public static Object dedup(Object obj) { } 

Solusi
Setelah menggaruk bagian belakang kepalanya, Cyril dapat menemukan beberapa opsi untuk menyelesaikan masalah ini, tetapi mereka semua entah bagaimana salah. Kemudian, menggaruk hidung dan berlabuh tentang java.util.concurrent , dia ingat metode computeIfAbsent luar computeIfAbsent . Metode ini akan mengeksekusi lambda yang diteruskan ke parameter hanya jika tidak ada kunci dalam Map , tulis hasilnya dan kembali. Jika kunci semacam itu sudah ada, lambda tidak akan dihitung, dan nilai saat ini yang terkait dengan kunci akan dikembalikan. Selain itu, kenang Kirill, untuk ConcurrentHashMap metode ini bekerja secara atom, yang memungkinkan Anda untuk menyelesaikan masalah dengan sangat elegan. Satisfied Cyril menulis kode ini:

 private static final ConcurrentHashMap map = new ConcurrentHashMap(); public static Object dedup(Object obj) { return map.computeIfAbsent(obj, o -> o); } 

dan dengan senang hati menggaruk hidungnya lagi.

Alamat IP


Dima sedang mengembangkan protokol jaringan baru. Perbaiki kesalahan dalam metodenya untuk menerjemahkan alamat IPv4 yang direpresentasikan sebagai array byte ke dalam string.

 String ipToString(byte[] ip) { return ip[0] + '.' + ip[1] + '.' + ip[2] + '.' + ip[3]; } 

Solusi
Kesalahan pertama segera ditunjukkan oleh IDE, mencegah Dima bahkan menambahkan metode sampai akhir. Simbol '.' memiliki tipe char ditambahkan ke byte sebagai tipe integer. Mengganti '.' ke "." , Dima sangat senang dengan kode yang berhasil dikompilasi sehingga ia segera meluncurkannya tanpa pengujian. "Ay-ah-ah, Dima", pikir JVM dan memberikan beberapa omong kosong bukannya alamat IP. Tidak seperti Dima, JVM tahu pasti bahwa di Jawa, tipe byte digunakan untuk menyimpan angka yang ditandatangani, yaitu, semua alamat dengan oktet lebih besar dari 127 akan diwakili oleh angka negatif di Jawa. Dengan aturan memasukkan angka-angka ini ke int , tanda negatif dari nomor tersebut sama dengan di byte asli. Ah, Dmitry, perlu mengambil tindakan tambahan untuk membuang bagian tanda, misalnya seperti ini:

 return (ip[0] & 255) + "." + (ip[1] & 255) + "." + (ip[2] & 255) + "." + (ip[3] & 255); 


Mixer


Marina perlu mencampur item daftar dalam urutan acak. Mengapa opsi ini tidak cocok, dan bagaimana Anda memperbaikinya?

 Random random = ThreadLocalRandom.current(); list.sort((o1, o2) -> { return random.nextBoolean() ? +1 : -1; }); 

Solusi
Marina, jelas, lupa bahwa kontrak Comparator membutuhkan stabilitas: ketika membandingkan dua nilai yang identik, hasil perbandingan harus sama. Dan dalam implementasi Marina, hasil untuk setiap pasangan benar-benar acak, yang dengan mudah dapat menyebabkan java.lang.IllegalArgumentException: Comparison method violates its general contract pengecualian java.lang.IllegalArgumentException: Comparison method violates its general contract ! Jika Marina membaca dokumentasi di malam hari, dia akan tahu bahwa dalam hal ini yang terbaik adalah menggunakan metode Collections.shuffle() .

Jawab: Kontrak pembanding dilanggar. Penyortiran dapat menimbulkan pengecualian. Lebih baik menggunakan metode Collections.shuffle() .

Adegan kelahiran fungsional


Egor suka menulis dengan gaya fungsional, tidak peduli dengan efektivitas kode. Perkirakan berapa banyak objek setiap panggilan ke metode ini dibuat jika ArrayList 10 baris dilewatkan ke sana?

 Predicate<String> equalsAny(List<String> list) { Predicate<String> p = s -> false; for (String s : list) { p = p.or(s::contains); } return p; } 

Solusi
Tidak seperti Yegor, Alina yang bertele-tele tidak suka menulis semuanya dengan gaya fungsional, karena dia tahu bagaimana menghitung biaya overhead. Satu baris

p = p.or(s::contains);

Itu membuat dua objek sekaligus: satu sebagai hasil dari memanggil p.or() , dan yang kedua untuk membuat s::contains predikat. Yang terakhir tidak bisa di-cache karena menangkap variabel dalam konteks. Mengalikan dengan jumlah iterasi, kita mendapatkan 20 objek. Tetapi juga Iterator tersembunyi dapat dibuat jika JIT tidak mengoptimalkannya. "20 atau bahkan 21 objek, jika kamu tidak beruntung, adalah orang berdosa," pikir Alina.

Jawaban: 10 predikat or + 10 predikat contains +1 Iterator tergantung pada optimasi JIT.

Pepatah menyala ke maksimum


Maxim menghitung maksimum dalam program multi-utas, tetapi ingin melakukannya tanpa kunci. Bantu dia memperbaiki kesalahan.

 AtomicLong max = new AtomicLong(); void addValue(long v) { if (v > max.get()) { max.set(v); } } 

Solusi
Oh, Maxim! Menggunakan AtomicLong tidak membuat utas program aman. Ada operasi atom AtomicLong.compareAndSwap . Dan mulai dengan Java 8, sama sekali tidak perlu untuk menulis siklus CAS sendiri, karena metode atom yang hebat accumulateAndGet . Dan di sini nyaman untuk menggunakannya:

 void addValue(long v) { max.accumulateAndGet(v, Math::max); } 

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


All Articles