アロハ!
したがって、Javaの世界で最もハードコアなカンファレンスの1つであるジョーカー2018は、伝統的にサンクトペテルブルクで開催されたExpoforumで終了しました。 今年、会議には記録的な数の参加者が参加しました。 Odnoklassnikiは従来、開発者が最も負荷の高いJavaプロジェクトの1つを作成するときに発生する重要なタスクを解決するのを支援するために提供していました。
質問によく答えた人は賞品を受け取り、私たちの問題の簡単な分析を提供します。 私たちは、ネタバレである正解を正解を隠し、決定を考えた後にのみ開くようにした;-)
行こう!
デデュプリケーター
Cyrilは、
equals()
に
equals()
オブジェクトを重複排除することでメモリを節約したいと考えています。 彼が
String.intern
に似たスレッドセーフなdedupメソッドを実装するのを助けてください。
public static Object dedup(Object obj) { }
解決策
頭の後ろを掻いて、シリルはこの問題を解決するためのいくつかのオプションを思いつくことができましたが、それらはすべて何らかの形で間違っていました。 それから、
について鼻とドックを
て、彼は素晴らしい
メソッドを思い出しました。 このメソッドは、
にキーが存在しない場合にのみ、パラメーターで渡されたラムダを実行し、その結果を書き込んで戻ります。 そのようなキーが既に存在する場合、ラムダは計算されず、キーに関連付けられた現在の値が返されます。 さらに、Kirillは、
このメソッドはアトミックに機能するため、問題を非常にエレガントに解決できることを思い出しました。 満足したCyrilはこのコードを書きました:
喜んで再び鼻を掻いた。
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はメソッドを最後に追加することさえできませんでした。 記号
型
を持つことは、整数型としてバイトに追加されます。
置き換える
、Dimaは正常にコンパイルされたコードに非常に満足していたため、テストなしですぐに起動しました。 「Ai-ai-ai、Dima」は、JVMを考え、IPアドレスの代わりにナンセンスを与えました。 Dimaとは異なり、JVMはJavaでは
型が符号付き数値の格納に使用されることを知っていました。つまり、127を超えるオクテットを持つすべてのアドレスはJavaでは負の数値で表されます。 これらの数値を
にキャストする規則により、数値の負符号は元のバイトと同じです。 ああ、ドミトリー、次のように、サイン部分を破棄するために追加の措置を講じる必要がありました。
'.'
型
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; });
解決策
マリーナは、明らかに、
コントラクトには安定性が必要であることを忘れていました。2つの同一の値を比較する場合、比較の結果は同じになるはずです。 また、Marinaの実装では、各ペアの結果は厳密にランダムであるため、例外
可能性があり
。 マリーナが夕方にドキュメントを読んだ場合、この場合、
メソッドを使用するのが最善であることを知っています。
回答:コンパレータ契約に違反しています。 ソートは例外をスローする場合があります。
メソッドを使用することをお勧めします。
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; }
解決策
イェゴルとは異なり、ペダンティックなアリーナは、オーバーヘッドコストの計算方法を知っているため、すべてを機能的なスタイルで書くことを好みません。 単線
は2つのオブジェクトを一度に作成します。1つは
を呼び出した結果として、もう1つは
述語を作成します。 後者は、コンテキスト内の変数
をキャプチャするため、キャッシュできません。 反復回数を掛けると、20個のオブジェクトが得られます。 ただし、JITが最適化しない場合は、非表示の
も作成できます。 「運が良くないなら、20個、あるいは21個の物体は罪人です」とアリナは考えました。
回答:JITの最適化に応じて、10の述語
+ 10の述語
+ 1の
。
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); } }
解決策
ああ、マキシム!
使用しても、プログラムはスレッドセーフになりません。 これにはアトミック操作
。 また、Java 8以降では、CASサイクルを自分で作成する必要はまったくありません。素晴らしいアトミックメソッドの
です。 そして、ここでそれを使うのが便利です:
AtomicLong
使用しても、プログラムはスレッドセーフになりません。 これにはアトミック操作
AtomicLong.compareAndSwap
。 また、Java 8以降では、CASサイクルを自分で作成する必要はまったくありません。素晴らしいアトミックメソッドの
accumulateAndGet
です。 そして、ここでそれを使うのが便利です:
void addValue(long v) { max.accumulateAndGet(v, Math::max); }