Analyse d'Odnoklassniki au Joker 2019



Du 28 au 29 octobre, Joker 2019 s'est tenu à Saint-Pétersbourg - la conférence la plus grande et la plus hardcore dans l'immensité de la Russie consacrée au développement Java. L'événement a eu lieu pour la septième fois et, comme toujours, a battu le record de fréquentation, cette fois, l'événement a attiré plus de 2000 spécialistes.

Les camarades de classe participent traditionnellement au Joker en tant que partenaires de l'événement. Cette année, sur notre stand, on pourrait essayer de faire face aux fameuses tâches «insolubles» des principaux ingénieurs OK.RU. Les participants à la conférence qui ont répondu correctement aux questions ont reçu des prix.

En toute honnêteté, je dois dire que sur 1 000 dépliants avec les tâches que nous avons confiées, moins de 100 ont été retournés, la meilleure étant la solution, qui a obtenu 4,5 points sur 5.

Nous publions des tâches et leurs solutions afin que vous puissiez tester votre force.

1. Enum héroïque


Le code source d'un jeu peu connu a révélé un tel code. Quelle est la mauvaise implémentation de Group.of et comment y remédier?

 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(); } } 

Solution
Si vous ne parlez pas de styles de codage, ce fragment présente un inconvénient objectif - un problème potentiel de performances. Bien que la recherche linéaire se révèle souvent être un goulot d'étranglement, dans ce cas, ce n'est pas le cas, car cette énumération ne comporte que cinq éléments. Et ce qui peut vraiment affecter les performances, c'est l'allocation excessive de mémoire lors de l'appel de Group.values() . Le problème est que la méthode values() d'énumération renvoie à chaque fois une nouvelle copie du tableau, et HotSpot n'est pas encore en mesure de l'optimiser. Une solution simple consiste à créer votre propre copie du tableau values() et à itérer dessus:

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


2. Rêves


Java 13 a déjà été publié et Nikolai ne comprend toujours que les flux. Indiquez les erreurs dans la méthode qui calcule la différence entre les éléments de flux maximum et minimum.

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

Solution
Les flux en Java sont généralement uniques: l'appel de la deuxième opération de terminal (dans ce cas, max ) échouera:

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

De plus, min et max retournent Optional , l'opération get() sur laquelle NoSuchElementException une NoSuchElementException pour un flux vide. Par conséquent, il est plus correct de vérifier isPresent() avant d'appeler get() ou d'utiliser d'autres méthodes Optional : orElse , orElseThrow , etc.

Enfin, le fait que la différence entre les deux int ne puisse plus tenir dans l' int n'échappera pas au développeur prudent, et il vaudrait la peine de changer le type de la valeur de retour en long .

3. Tampon sûr


ByteBuffer primitive de synchronisation Java peut sécuriser les threads put et get sur un ByteBuffer générique?

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

Choisissez l'option la plus efficace si vous savez qu'il existe de nombreux threads et obtenez des exécutions beaucoup plus souvent que put.

  • synchronisé
  • Reentrantlock
  • ReentrantReadWriteLock
  • Stampedlock
  • Sémaphore
  • La lecture et l'écriture int en Java est toujours atomique

Solution
ReentrantReadWriteLock demande des ReentrantReadWriteLock lecture et d'écriture, et ce sera souvent une solution efficace. Mais notez que dans ce cas, les opérations get et put sont très simples - la probabilité qu'un put concurrentiel puisse interférer avec get est faible, de plus, les conditions de vente sont moins susceptibles de se produire avec les opérations put. Ainsi, vous pouvez appliquer le mécanisme de verrouillage optimiste fourni par StampedLock .

StampedLock sera plus efficace que ReentrantReadWriteLock du fait qu'en cas de réussite d'un chemin rapide optimiste, les variables partagées ne sont pas du tout mises à jour, tandis que ReentrantReadWriteLock effectue au moins un CAS au mieux.

4. Cadeaux


Ilya développe une vitrine de cadeaux sur un réseau social. Aidez-le à écrire la méthode d' add pour une structure qui ne contient pas plus de N des nouveaux cadeaux. Un cadeau ne doit pas être ajouté s'il est déjà présent ou s'il est plus ancien que le reste de N.

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

Solution
TreeSet ou PriorityQueue naturellement comme structure de données afin d'ajouter efficacement des cadeaux et de supprimer les plus anciens pas moins que pour O (log N). Tout l'astuce n'est que dans le comparateur: il ne suffit pas de comparer les cadeaux uniquement avec getCreated() , car la date de création n'a pas à être unique. Par conséquent, vous devez comparer d'abord par getCreated() , puis par getId() . Un tel comparateur garantira à la fois l'unicité des éléments et la commande par date.

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

Cela reste une petite affaire: lors de l'ajout d'un cadeau, vérifiez que la taille ne dépasse pas N, et si nécessaire, supprimez le premier élément le plus ancien de la collection.

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


5. Tu n'attendras pas


Pourquoi Julia n'attendra jamais la fin de ce programme?

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

Solution
La documentation waitTermination suggère que la demande d'arrêt doit précéder l'exécution. C'est simple: Julia a oublié d'appeler executor.shutdown () .

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


All Articles