Joker-Rätsel 2018



Aloha!

Damit ist eine der härtesten Konferenzen der Java-Welt - Joker 2018, die traditionell im Expoforum in St. Petersburg stattfindet - beendet. In diesem Jahr nahmen an der Konferenz eine Rekordzahl von Teilnehmern teil. Odnoklassniki bot traditionell an, unseren Entwicklern bei der Lösung nicht trivialer Probleme zu helfen, die beim Erstellen eines der am stärksten ausgelasteten Java-Projekte auftreten.

Diejenigen, die die Fragen gut beantwortet haben, haben Preise erhalten, und wir bieten Ihnen eine kurze Analyse unserer Probleme. Wir haben die richtigen Antworten unter dem Spoiler versteckt, chur, um sie erst zu öffnen, nachdem wir selbst die Lösung gefunden haben ;-)

Lass uns gehen!

Deduplikator


Cyril möchte Speicherplatz sparen, indem er Objekte in equals() . Helfen Sie ihm, die thread-sichere Dedup-Methode analog zu String.intern zu String.intern , aber nicht nur für Strings.

 public static Object dedup(Object obj) { } 

Lösung
Cyril hatte sich am Hinterkopf gekratzt und konnte verschiedene Möglichkeiten zur Lösung dieses Problems finden, aber alle waren irgendwie falsch. Dann kratzte er sich an der Nase und computeIfAbsent java.util.concurrent und erinnerte sich an die wunderbare Methode computeIfAbsent . Diese Methode führt das im Parameter übergebene Lambda nur aus, wenn die Map keinen Schlüssel enthält, schreibt das Ergebnis und gibt es zurück. Wenn ein solcher Schlüssel bereits vorhanden ist, wird Lambda nicht berechnet und der dem Schlüssel zugeordnete aktuelle Wert wird zurückgegeben. Kirill erinnerte sich außerdem, dass diese Methode für ConcurrentHashMap atomar funktioniert, wodurch Sie das Problem sehr elegant lösen können. Zufrieden Cyril schrieb diesen Code:

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

und kratzte sich gerne wieder an der Nase.

IP-Adresse


Dima entwickelt ein neues Netzwerkprotokoll. Korrigieren Sie den Fehler in seiner Methode zum Übersetzen einer als Byte-Array dargestellten IPv4-Adresse in eine Zeichenfolge.

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

Lösung
Der erste Fehler wurde sofort von der IDE angezeigt, sodass Dima die Methode nicht einmal am Ende hinzufügen konnte. Das Symbol '.' Der Typ char wird dem Byte als ganzzahliger Typ hinzugefügt. '.' Ersetzen zu "." Dima freute sich so über den erfolgreich kompilierten Code, dass er ihn sofort ohne Test startete. "Ay-ah-ah, Dima", dachte die JVM und gab etwas Unsinn anstelle der IP-Adresse heraus. Im Gegensatz zu Dima wusste die JVM mit Sicherheit, dass in Java der byte zum Speichern von vorzeichenbehafteten Zahlen verwendet wird, dh, alle Adressen mit Oktetten größer als 127 werden in Java durch negative Zahlen dargestellt. Nach den Regeln für das Umwandeln dieser Zahlen in int ist das negative Vorzeichen der Zahl dasselbe wie im ursprünglichen Byte. Ah, Dmitry, es war notwendig, zusätzliche Maßnahmen zu ergreifen, um den Schilderteil zu verwerfen, zum Beispiel wie folgt:

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


Mischer


Marina muss die Listenelemente in zufälliger Reihenfolge mischen. Warum ist diese Option nicht geeignet und wie würden Sie sie beheben?

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

Lösung
Marina hat offensichtlich vergessen, dass der Comparator Stabilität erfordert: Beim Vergleich zweier identischer Werte sollte das Ergebnis des Vergleichs dasselbe sein. Bei der Implementierung von Marina ist das Ergebnis für jedes Paar streng zufällig, was leicht zu einer Ausnahme von java.lang.IllegalArgumentException: Comparison method violates its general contract ! Wenn Marina abends die Dokumentation lesen würde, würde sie wissen, dass es in diesem Fall am besten ist, die Collections.shuffle() -Methode zu verwenden.

Antwort: Der Vergleichsvertrag wird verletzt. Das Sortieren kann eine Ausnahme auslösen. Es ist besser, die Collections.shuffle() -Methode zu verwenden.

Funktionale Krippe


Egor liebt es, in einem funktionalen Stil zu schreiben, ohne sich um die Effektivität des Codes zu kümmern. Schätzen Sie, wie viele Objekte jeder Aufruf dieser Methode erstellt, wenn eine ArrayList mit 10 Zeilen an sie übergeben wird.

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

Lösung
Im Gegensatz zu Yegor schreibt die pedantische Alina nicht gerne alles in einem funktionalen Stil, weil sie weiß, wie man Gemeinkosten berechnet. Einzeilige Zeile

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

Es werden zwei Objekte gleichzeitig erstellt: eines als Ergebnis des Aufrufs von p.or() und das zweite zum Erstellen des Prädikats s::contains . Letzteres kann nicht zwischengespeichert werden, da es die Variablen s im Kontext erfasst. Multipliziert mit der Anzahl der Iterationen erhalten wir 20 Objekte. Es kann aber auch ein versteckter Iterator erstellt werden, wenn die JIT ihn nicht optimiert. "20 oder sogar 21 Objekte, wenn Sie kein Glück haben, sind ein Sünder", dachte Alina.

Antwort: 10 Prädikate or + 10 Prädikate contains je nach JIT-Optimierung + 1 Iterator .

Maxim schaltet sich auf das Maximum ein


Maxim berechnet das Maximum in einem Multithread-Programm, möchte jedoch auf Sperren verzichten. Helfen Sie ihm, den Fehler zu beheben.

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

Lösung
Oh, Maxim! AtomicLong Verwendung von AtomicLong macht den Programm-Thread nicht sicher. AtomicLong.compareAndSwap gibt es eine atomare Operation AtomicLong.compareAndSwap . Und ab Java 8 ist es überhaupt nicht notwendig, den CAS-Zyklus selbst zu schreiben, da die wunderbare atomare Methode accumulateAndGet . Und hier ist es bequem, nur es zu verwenden:

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

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


All Articles