小写所有HashMap键

时间:2016-12-19 14:41:39

标签: java hashmap java-8 java-stream

我遇到了一个场景,我想要小写HashMap的所有键(不要问为什么,我只需要这样做)。 HashMap有数百万个条目。

首先,我想我只是创建一个新的Map,迭代要小写的地图条目,然后添加相应的值。这项任务应该每天只运行一次或类似的事情,所以我想我可以裸露这个。

Map<String, Long> lowerCaseMap = new HashMap<>(myMap.size());
for (Map.Entry<String, Long> entry : myMap.entrySet()) {
   lowerCaseMap.put(entry.getKey().toLowerCase(), entry.getValue());
}
但是,当我的服务器过载时,这会导致一些OutOfMemory错误,因为我要复制Map。

现在我的问题是,如何以最小的内存占用完成此任务?

在小写之后删除每个键 - 添加到新的Map帮助吗?

我可以利用java8流来加快速度吗? (例如这样的事情)

Map<String, Long> lowerCaseMap = myMap.entrySet().parallelStream().collect(Collectors.toMap(entry -> entry.getKey().toLowerCase(), Map.Entry::getValue));

更新 它似乎是Collections.unmodifiableMap所以我没有

的选项
  

在小写后删除每个键 - 添加到新地图

4 个答案:

答案 0 :(得分:19)

您可以尝试使用不区分大小写排序的HashMap,而不是使用TreeMap。这样可以避免创建每个密钥的小写版本:

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

构建此映射后,put()get()将表现不区分大小写,因此您可以使用全小写键保存和获取值。迭代键将以原始形式(可能是大写形式)返回它们。

以下是一些类似的问题:

答案 1 :(得分:3)

迭代地图时无法删除条目。如果你尝试这样做,你将有一个ConcurentModificationException。

由于问题是OutOfMemoryError,而不是性能错误,因此使用并行流也无济于事。

尽管Stream API上的某些任务最近会完成,但这仍然会导致内存中有两张地图,所以你仍然会遇到问题。

要解决它,我只看到两种方式:

  • 为您的进程提供更多内存(通过在Java命令行上增加-Xmx)。这些天记忆很便宜;)
  • 拆分地图并以块的形式工作:例如,您将地图的大小除以10,并且您一次处理一个chunck并在处理新块之前删除已处理的条目。通过这个而不是在内存中有两倍的地图,你将只有地图的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());
    }
}

答案 2 :(得分:0)

以上答案中的担忧是正确的,您可能需要重新考虑更改正在使用的数据结构。

对我来说,我有一个简单的地图,我需要将其键更改为小写

看看我的代码片段,这是一个琐碎的解决方案,并且性能不佳

private void convertAllFilterKeysToLowerCase() {
    HashSet keysToRemove = new HashSet();
    getFilters().keySet().forEach(o -> {
        if(!o.equals(((String) o).toLowerCase()))
            keysToRemove.add(o);
    });
    keysToRemove.forEach(o -> getFilters().put(((String) o).toLowerCase(), getFilters().remove(o)));
}

答案 3 :(得分:0)

不确定内存占用。如果使用 Kotlin,您可以尝试以下操作。

val lowerCaseMap = myMap.mapKeys { it.key.toLowerCase() }

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map-keys.html