HashMapのすべてのキーを小文字にしたいというシナリオに遭遇しました(理由は尋ねないで、これを行う必要があるだけです)。 HashMapには数百万のエントリがあります。
最初は、新しいマップを作成し、小文字にするマップのエントリを反復処理して、それぞれの値を追加するだけだと思っていました。このタスクは1日に1回だけ実行する必要があるため、これを実行できると思いました。
Map<String, Long> lowerCaseMap = new HashMap<>(myMap.size());
for (Map.Entry<String, Long> entry : myMap.entrySet()) {
lowerCaseMap.put(entry.getKey().toLowerCase(), entry.getValue());
}
ただし、これにより、マップをコピーしようとしたときにサーバーが過負荷になったときにOutOfMemoryエラーが発生しました。
今私の質問は、最小のメモリフットプリントでこのタスクをどのように達成できるでしょうか?
小文字の後に各キーを削除しますか-新しいマップヘルプに追加されましたか?
これを速くするためにJava8ストリームを利用できますか? (例えばこのようなもの)
Map<String, Long> lowerCaseMap = myMap.entrySet().parallelStream().collect(Collectors.toMap(entry -> entry.getKey().toLowerCase(), Map.Entry::getValue));
更新Collections.unmodifiableMap
だから私には選択肢がありません
小文字の後に各キーを削除-新しいマップに追加
HashMap
を使用する代わりに、大文字と小文字を区別しない順序でTreeMap
を使用することもできます。これにより、各キーの小文字バージョンを作成する必要がなくなります。
_Map<String, Long> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
map.putAll(myMap);
_
このマップを作成すると、put()
およびget()
は大文字と小文字を区別せずに動作するため、すべて小文字のキーを使用して値を保存およびフェッチできます。キーを繰り返し処理すると、元の大文字の形式でキーが返されます。
同様の質問をいくつか次に示します。
マップの反復中にエントリを削除することはできません。これを実行しようとすると、ConcurentModificationExceptionが発生します。
問題はパフォーマンスエラーではなくOutOfMemoryErrorであるため、並列ストリームを使用しても効果がありません。
Stream APIの一部のタスクは最近行われる予定ですが、これにより、ある時点でメモリに2つのマップが存在するようになるため、引き続き問題が発生します。
それを回避するために、私は2つの方法だけを見ました:
分割アルゴリズムについては、Stream APIを使用して次のように試すことができます。
Map<String, String> toMap = new HashMap<>();
int chunk = fromMap.size() / 10;
for(int i = 1; i<= 10; i++){
//process the chunk
List<Entry<String, String>> subEntries = fromMap.entrySet().stream().limit(chunk)
.collect(Collectors.toList());
for(Entry<String, String> entry : subEntries){
toMap.put(entry.getKey().toLowerCase(), entry.getValue());
fromMap.remove(entry.getKey());
}
}