Odnoklassniki-Analyse beim Joker 2019



Vom 28. bis 29. Oktober fand in St. Petersburg der Joker 2019 statt - die größte und härteste Konferenz in der Weite Russlands, die sich der Java-Entwicklung widmet. Die Veranstaltung fand zum siebten Mal statt und brach wie immer den Besucherrekord. Diesmal zog die Veranstaltung mehr als 2000 Spezialisten an.

Klassenkameraden nehmen traditionell als Eventpartner an Joker teil. In diesem Jahr könnte man an unserem Stand versuchen, die berühmten "unlösbaren" Aufgaben führender OK.RU-Ingenieure zu bewältigen. Konferenzteilnehmer, die die Fragen richtig beantwortet haben, erhielten Preise.

Fairerweise muss ich sagen, dass von 1.000 Flugblättern mit den von uns verteilten Aufgaben weniger als 100 zurückgegeben wurden. Das Beste war die Lösung, die 4,5 von 5 Punkten erzielte.

Wir veröffentlichen Aufgaben und deren Lösungen, damit Sie Ihre Stärke testen können.

1. Heroische Aufzählung


Der Quellcode für ein wenig bekanntes Spiel enthüllte einen solchen Code. Was ist die schlechte Implementierung von Group.of und wie kann dies 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(); } } 

Lösung
Wenn Sie nicht über Codierungsstile sprechen, hat dieses Fragment einen objektiven Nachteil - ein potenzielles Leistungsproblem. Obwohl sich die lineare Suche häufig als Engpass herausstellt, ist dies in diesem Fall nicht der Fall, da diese Aufzählung nur fünf Elemente enthält. Und was die Leistung wirklich negativ beeinflussen kann, ist die übermäßige Speicherzuweisung beim Aufrufen von Group.values() . Das Problem ist, dass die Methode values() von enum jedes Mal eine neue Kopie des Arrays zurückgibt und HotSpot diese noch nicht optimieren kann. Eine einfache Lösung besteht darin, eine eigene Kopie des Arrays values() erstellen und darüber zu iterieren:

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


2. Träume


Java 13 wurde bereits veröffentlicht, und Nikolai versteht immer noch nur Streams. Geben Sie Fehler in der Methode an, mit der die Differenz zwischen den maximalen und minimalen Stream-Elementen berechnet wird.

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

Lösung
Streams in Java sind normalerweise einmalig: Das Aufrufen der zweiten Terminaloperation (in diesem Fall max ) schlägt fehl:

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

Außerdem geben min und max Optional , die get() NoSuchElementException bei der eine NoSuchElementException für einen leeren Stream NoSuchElementException . Daher ist es korrekter, isPresent() vor dem Aufruf von get() zu überprüfen oder andere Optional Methoden zu verwenden: orElse , orElseThrow usw.

Schließlich int dem vorsichtigen Entwickler die Tatsache, dass der Unterschied zwischen den beiden int nicht mehr in das int passt, nicht, und es lohnt sich, den Typ des Rückgabewerts auf long zu ändern.

3. Sicherer Puffer


ByteBuffer Java-Synchronisierung kann dafür sorgen put dass put und get Operations-Threads für einen generischen ByteBuffer sicher 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); } 

Wählen Sie die effizienteste Option, wenn Sie wissen, dass es viele Threads gibt und viel häufiger ausgeführt wird als Put.

  • synchronisiert
  • Wiedereintrittssperre
  • ReentrantReadWriteLock
  • Stempelschloss
  • Semaphor
  • Das Lesen und Schreiben von Int in Java ist immer atomar

Lösung
ReentrantReadWriteLock bittet um Lese- und ReentrantReadWriteLock , und oft ist dies eine effektive Lösung. Beachten Sie jedoch, dass in diesem Fall die get- und put-Operationen sehr einfach sind - die Wahrscheinlichkeit, dass ein konkurrierender put den get-Vorgang stören kann, ist gering, und außerdem ist es weniger wahrscheinlich, dass put-Bedingungen bei put-Operationen auftreten. Sie können also den optimistischen Sperrmechanismus von StampedLock anwenden .

StampedLock ist effizienter als ReentrantReadWriteLock , da bei einem optimistischen Erfolg auf schnellem Weg gemeinsam genutzte Variablen überhaupt nicht aktualisiert werden, während ReentrantReadWriteLock mindestens ein CAS ausführt.

4. Geschenke


Ilya entwickelt ein Schaufenster mit Geschenken in einem sozialen Netzwerk. Helfen Sie ihm, die add Methode für eine Struktur zu schreiben, die nicht mehr als N der neuesten Geschenke enthält. Ein Geschenk sollte nicht hinzugefügt werden, wenn es bereits vorhanden ist oder älter als der Rest von N.

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

Lösung
TreeSet oder PriorityQueue natürlich als Datenstruktur, um Geschenke effektiv hinzuzufügen und die ältesten nicht schlechter als für O (log N) zu löschen. Der ganze Trick liegt nur im Komparator: Es reicht nicht aus, Geschenke nur mit getCreated() zu vergleichen, da das Erstellungsdatum nicht eindeutig sein muss. Daher müssen Sie zuerst mit getCreated() und dann mit getId() . Ein solcher Komparator gewährleistet sowohl die Eindeutigkeit der Elemente als auch die Sortierung nach Datum.

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

Es bleibt eine Kleinigkeit: Wenn Sie ein Geschenk hinzufügen, stellen Sie sicher, dass die Größe N nicht überschreitet, und löschen Sie gegebenenfalls das erste, älteste Element der Sammlung.

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


5. Sie werden nicht warten


Warum wird Julia niemals auf das Ende dieses Programms warten?

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

Lösung
In der Dokumentation zu awaitTermination wird vorgeschlagen , dass die Anforderung zum Herunterfahren der Ausführung vorausgehen sollte. Es ist ganz einfach: Julia hat vergessen, executor.shutdown () aufzurufen.

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


All Articles