Puzzles Joker 2018



Aloha!

Ainsi, l'une des conférences les plus hardcore du monde Java - Joker 2018, qui se déroule traditionnellement à Saint-Pétersbourg à l'Expoforum, est terminée. Cette année, la conférence a réuni un nombre record de participants. Odnoklassniki proposait traditionnellement d'aider nos développeurs à résoudre les problèmes non triviaux qui se posent lors de la création de l'un des projets Java les plus chargés.

Ceux qui ont répondu aux questions ont bien reçu des prix, et nous vous proposons une brève analyse de nos problèmes. Nous avons caché les bonnes réponses sous le spoiler, chur, pour ouvrir seulement après avoir nous-mêmes trouvé la solution ;-)

C'est parti!

Déduplicateur


Cyril veut économiser de la mémoire en dédoublant des objets égaux en equals() . Aidez-le à implémenter la méthode de dédoublonnage thread-safe par analogie avec String.intern , mais pas seulement pour les chaînes.

 public static Object dedup(Object obj) { } 

Solution
Après s'être gratté l'arrière de la tête, Cyril a été en mesure de proposer plusieurs options pour résoudre ce problème, mais ils se sont tous trompés. Puis, se grattant le nez et s'arrêtant sur java.util.concurrent , il se souvint de la merveilleuse méthode computeIfAbsent . Cette méthode exécutera le lambda qui lui est passé dans le paramètre uniquement s'il n'y a pas de clé dans la Map , écrit son résultat et retourne. Si une telle clé existe déjà, lambda ne sera pas calculé et la valeur actuelle associée à la clé sera retournée. En outre, Kirill a rappelé que pour ConcurrentHashMap cette méthode fonctionne de manière atomique, ce qui vous permet de résoudre le problème de manière très élégante. Cyril satisfait a écrit ce code:

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

et gratta son nez avec plaisir.

Adresse IP


Dima développe un nouveau protocole réseau. Corrigez l'erreur dans sa méthode de traduction d'une adresse IPv4 représentée sous la forme d'un tableau d'octets en une chaîne.

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

Solution
La première erreur a été immédiatement montrée par l'IDE, empêchant Dima d'ajouter même la méthode à la fin. Le symbole '.' ayant le type char est ajouté à l'octet comme un type entier. Remplacement de '.' à "." , Dima était tellement content du code compilé avec succès qu'il l'a immédiatement lancé sans test. "Ay-ah-ah, Dima", a pensé la JVM et a donné un non-sens au lieu de l'adresse IP. Contrairement à Dima, la JVM savait avec certitude qu'en Java, le type d' byte est utilisé pour stocker des nombres signés, c'est-à-dire que toutes les adresses avec des octets supérieurs à 127 seront représentées par des nombres négatifs en Java. Selon les règles de conversion de ces nombres en int , le signe négatif du nombre est le même que dans l'octet d'origine. Ah, Dmitry, il a fallu prendre des mesures supplémentaires pour jeter la partie signe, par exemple comme ceci:

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


Mixeur


Marina doit mélanger les éléments de la liste dans un ordre aléatoire. Pourquoi cette option ne convient-elle pas et comment la corrigeriez-vous?

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

Solution
Marina, évidemment, a oublié que le contrat Comparator requiert de la stabilité: lors de la comparaison de deux valeurs identiques, le résultat de la comparaison doit être le même. Et dans l'implémentation de Marina, le résultat pour chaque paire est strictement aléatoire, ce qui peut facilement conduire à une exception java.lang.IllegalArgumentException: Comparison method violates its general contract ! Si Marina lisait la documentation le soir, elle saurait que dans ce cas, il est préférable d'utiliser la méthode Collections.shuffle() .

Réponse: Le contrat du comparateur est violé. Le tri peut lever une exception. Il est préférable d'utiliser la méthode Collections.shuffle() .

Crèche fonctionnelle


Egor aime écrire dans un style fonctionnel, sans se soucier de l'efficacité du code. Estimer le nombre d'objets créés par chaque appel à cette méthode si une liste de ArrayList de 10 lignes lui est transmise?

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

Solution
Contrairement à Yegor, la pédante Alina n'aime pas tout écrire dans un style fonctionnel, car elle sait calculer les frais généraux. Une seule ligne

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

Il crée deux objets à la fois: l'un à la suite de l'appel de p.or() , et le second pour créer le prédicat s::contains . Ce dernier ne peut pas être mis en cache car il capture la variable s dans le contexte. En multipliant par le nombre d'itérations, nous obtenons 20 objets. Mais un Iterator caché peut également être créé si le JIT ne l'optimise pas. "20 ou même 21 objets, si vous n'avez pas de chance, c'est un pécheur", pensa Alina.

Réponse: 10 prédicats or + 10 prédicats contains + 1 Iterator fonction des optimisations JIT.

Maxim s'active au maximum


Maxim calcule le maximum dans un programme multithread, mais veut se passer de verrous. Aidez-le à corriger l'erreur.

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

Solution
Oh, Maxim! L'utilisation d' AtomicLong ne rend pas le thread du programme sûr. Il existe une opération atomique AtomicLong.compareAndSwap . Et à partir de Java 8, il n'est pas du tout nécessaire d'écrire le cycle CAS vous-même, car la merveilleuse méthode atomique accumulateAndGet . Et ici, il est pratique de l'utiliser uniquement:

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

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


All Articles