この質問は最初は基本的なことに思えるかもしれませんが、フォーラムでは頻繁に議論されています。この投稿では、マップの中で一度だけキーを検索する方法について説明します。
例を見てみましょう。Mapを使って単語の頻度表を作成し、各キーはカウントされる単語で、値はその頻度です。簡単な実装は次のようになります:
int count = map.containsKey(string) ? map.get(string) : 0;
map.put(string, count + 1);
このコードには時間を浪費する可能性のある操作が3つ含まれているため、あまり効率的ではありません。統計処理を実行するたびに、Map内のキーを検索します。では、これを例にして、Mapに値を追加することでパフォーマンスがどのように向上するかを見てみましょう。
Integer vs MutableInteger vs AtomicInteger
パフォーマンスを消費する操作を3回呼び出さなければならない大きな理由は、カウントにIntegerを使用していることです。Javaでは、Integerは変更できません。一度構築された整数値を変更することはできません。そのため、カウンターを大きくするには、マップから整数を取得し、別の新しい整数を作成し、それを追加してマップに戻す必要があります。
カウンターを変更可能にするには、いくつかの方法があります。そのうちの1つは、以下に示すような独自のMutableIntegerを作成することです:
public class MutableInteger {
private int val;
public MutableInteger(int val) {
this.val = val;
}
public int get() {
return val;
}
public void set(int val) {
this.val = val;
}
}
別のアプローチとしては、JavaのAtomicIntegerを使用することが考えられます。これは、アトミックな成長カウンターを必要とするアプリケーションなどで使用されます。AtomicIntegerを***として使用する理由は、整数を操作する際にスレッドセーフでありたいからです。そのため、Integerの代わりには使えません。このため、プロジェクトでスレッドセーフを重要視しないのであれば、AtomicIntegerはお勧めしません。
検索キーは一度だけ
MutableIntegerを使用した後、上記のコードを以下のように変更します:
if (map.containsKey(string)) {
MutableInteger count = map.get(string);
count.set(count.get() + 1);
} else {
map.put(string, new MutableInteger(1));
}
または
MutableInteger count = map.get(string);
if (count != null) {
count.set(count.get() + 1);
} else {
map.put(string, new MutableInteger(1));
}
キーがまだ存在しない最悪の場合、このコードは2つの検索を実行します:1つはMutableIntegerを取得し、もう1つは値を設定します。これは前のコードよりも最適化されています。しかし、今のところ、[Map.putt()]メソッドをチェックアウトするだけではいけません)。 メソッドをチェックしてみてください。このメソッドは以前に関連付けられたキーの値を返すことがわかります。つまり、reacquireオブジェクトとsetメソッドをマージすることが可能です。しかし、おそらくあなたは、最初にカウンターを取得することなく、新しいカウンターを設定する方法を疑問に思っているのではないでしょうか?これでようやく、この記事で最も厄介な部分にぶち当たったことになります:単純にゼロ頻度カウンターを使えばいいのです!
public int incrementCount(K key, int count) {
MutableInteger tmpCount = new MutableInteger(0);
MutableInteger oldCount = map.put(key, tmpCount);
if (oldCount != null) {
count += oldCount.get();
}
tmpCount.set(count);
return count;
}
必要な操作をすべてクラスに入れておくと、将来使うときにとても便利そうです。そこで、Counterクラスを作成し、一般公開することにしました。このCounterでは、コレクションが定義され、オブジェクトがコレクションに現れる回数を記録します。a,a,b,c}というコレクションを含むCounterがあるとします。getCount() メソッドを呼び出すと "a" に対して 2 が返されますが、keySet() を呼び出すと {a,b,c} が返されます。このクラスは Map のように動作しますが、Map よりもシンプルなメソッドを持っています。
Counter のコンストラクタと addAll() メソッドを使用して、別のカウンタの内容をコピーできます。Counter クラスは、IntCounter と AbstractMapBag で変更できます。
カウンターの主な操作方法は以下の通り:
- と :与えられたキー値に基づいて、現在のカウントから与えられた値をインクリメント/減算します。もしそのキー値が以前に出現したことがなければ、カウントは0であると判断できます。subtractメソッドは、その値を-1に設定します。
- 指定されたキーの現在のカウントを返します。
- and:指定されたキー値のカウントを返します。カウントは、指定されたしきい値と等しいか、それより大きいか、またはそれ以下でなければなりません。この集合の要素は0かもしれませんが、空にはなりません。
- and :このカウンタで最小または***カウントのキーの値を検索して返します。最小または***カウントが複数ある場合は、ランダムな値が返されます。Counter が空の場合は null を返します。