web-dev-qa-db-ja.com

すべてのHashMapキーを小文字にします

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だから私には選択肢がありません

小文字の後に各キーを削除-新しいマップに追加

18
sestus

HashMapを使用する代わりに、大文字と小文字を区別しない順序でTreeMapを使用することもできます。これにより、各キーの小文字バージョンを作成する必要がなくなります。

_Map<String, Long> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
map.putAll(myMap);
_

このマップを作成すると、put()およびget()は大文字と小文字を区別せずに動作するため、すべて小文字のキーを使用して値を保存およびフェッチできます。キーを繰り返し処理すると、元の大文字の形式でキーが返されます。

同様の質問をいくつか次に示します。

27
Kenster

マップの反復中にエントリを削除することはできません。これを実行しようとすると、ConcurentModificationExceptionが発生します。

問題はパフォーマンスエラーではなくOutOfMemoryErrorであるため、並列ストリームを使用しても効果がありません。

Stream APIの一部のタスクは最近行われる予定ですが、これにより、ある時点でメモリに2つのマップが存在するようになるため、引き続き問題が発生します。

それを回避するために、私は2つの方法だけを見ました:

  • プロセスにメモリを追加します(Javaコマンドラインで-Xmxを増やします)。最近のメモリは安いです;)
  • マップを分割してチャンクで作業します。たとえば、マップのサイズを10で割り、一度に1つのチャンクを処理し、処理されたエントリを削除してから新しいチャンクを処理します。これにより、メモリ内に2倍のマップを持つ代わりに、マップの1.1倍になります。

分割アルゴリズムについては、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());
    }
}
3
loicmathieu