ジョーカーパズル2018





アロハ!



したがって、Javaの世界で最もハードコアなカンファレンスの1つであるジョーカー2018は、伝統的にサンクトペテルブルクで開催されたExpoforumで終了しました。 今年、会議には記録的な数の参加者が参加しました。 Odnoklassnikiは従来、開発者が最も負荷の高いJavaプロジェクトの1つを作成するときに発生する重要なタスクを解決するのを支援するために提供していました。



質問によく答えた人は賞品を受け取り、私たちの問題の簡単な分析を提供します。 私たちは、ネタバレである正解を正解を隠し、決定を考えた後にのみ開くようにした;-)



行こう!



デデュプリケーター



Cyrilは、 equals()



equals()



オブジェクトを重複排除することでメモリを節約したいと考えています。 彼がString.intern



に似たスレッドセーフなdedupメソッドを実装するのを助けてください。



 public static Object dedup(Object obj) { }
      
      





解決策
頭の後ろを掻いて、シリルはこの問題を解決するためのいくつかのオプションを思いつくことができましたが、それらはすべて何らかの形で間違っていました。 それから、 java.util.concurrent



について鼻とドックをcomputeIfAbsent



て、彼は素晴らしいcomputeIfAbsent



メソッドを思い出しました。 このメソッドは、 Map



にキーが存在しない場合にのみ、パラメーターで渡されたラムダを実行し、その結果を書き込んで戻ります。 そのようなキーが既に存在する場合、ラムダは計算されず、キーに関連付けられた現在の値が返されます。 さらに、Kirillは、 ConcurrentHashMap



このメソッドはアトミックに機能するため、問題を非常にエレガントに解決できることを思い出しました。 満足したCyrilはこのコードを書きました:



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





喜んで再び鼻を掻いた。



IPアドレス



Dimaは新しいネットワークプロトコルを開発しています。 バイト配列として表されるIPv4アドレスを文字列に変換する彼の方法のエラーを修正します。



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





解決策
最初のエラーはIDEによってすぐに表示され、Dimaはメソッドを最後に追加することさえできませんでした。 記号'.'



char



を持つことは、整数型としてバイトに追加されます。 '.'



置き換える "."



、Dimaは正常にコンパイルされたコードに非常に満足していたため、テストなしですぐに起動しました。 「Ai-ai-ai、Dima」は、JVMを考え、IPアドレスの代わりにナンセンスを与えました。 Dimaとは異なり、JVMはJavaではbyte



型が符号付き数値の格納に使用されることを知っていました。つまり、127を超えるオクテットを持つすべてのアドレスはJavaでは負の数値で表されます。 これらの数値をint



にキャストする規則により、数値の負符号は元のバイトと同じです。 ああ、ドミトリー、次のように、サイン部分を破棄するために追加の措置を講じる必要がありました。



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







ミキサー



マリーナは、リスト項目をランダムな順序で混ぜる必要があります。 このオプションが適切ではないのはなぜですか?どのように修正しますか?



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





解決策
マリーナは、明らかに、 Comparator



コントラクトには安定性が必要であることを忘れていました。2つの同一の値を比較する場合、比較の結果は同じになるはずです。 また、Marinaの実装では、各ペアの結果は厳密にランダムであるため、例外java.lang.IllegalArgumentException: Comparison method violates its general contract



可能性がありjava.lang.IllegalArgumentException: Comparison method violates its general contract



。 マリーナが夕方にドキュメントを読んだ場合、この場合、 Collections.shuffle()



メソッドを使用するのが最善であることを知っています。



回答:コンパレータ契約に違反しています。 ソートは例外をスローする場合があります。 Collections.shuffle()



メソッドを使用することをお勧めします。



機能的なキリスト降誕のシーン



Egorは、コードの有効性を気にすることなく、機能的なスタイルで書くことを好みます。 10行のArrayList



が渡された場合、このメソッドの各呼び出しが作成するオブジェクトの数を推定しますか?



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





解決策
イェゴルとは異なり、ペダンティックなアリーナは、オーバーヘッドコストの計算方法を知っているため、すべてを機能的なスタイルで書くことを好みません。 単線



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







は2つのオブジェクトを一度に作成します。1つはp.or()



を呼び出した結果として、もう1つはs::contains



述語を作成します。 後者は、コンテキスト内の変数s



をキャプチャするため、キャッシュできません。 反復回数を掛けると、20個のオブジェクトが得られます。 ただし、JITが最適化しない場合は、非表示のIterator



も作成できます。 「運が良くないなら、20個、あるいは21個の物体は罪人です」とアリナは考えました。



回答:JITの最適化に応じて、10の述語or



+ 10の述語contains



+ 1のIterator



contains







マキシムが最大になります



マキシムはマルチスレッドプログラムの最大値を計算しますが、ロックなしで実行したいと考えています。 彼が間違いを修正するのを助けてください。



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





解決策
ああ、マキシム! AtomicLong



使用しても、プログラムはスレッドセーフになりません。 これにはアトミック操作AtomicLong.compareAndSwap



。 また、Java 8以降では、CASサイクルを自分で作成する必要はまったくありません。素晴らしいアトミックメソッドのaccumulateAndGet



です。 そして、ここでそれを使うのが便利です:



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








All Articles