出于以下原因,我想使用不区分大小写的字符串作为HashMap键。

在初始化过程中,我的程序用用户定义的字符串创建HashMap 在处理事件(在我的情况下是网络流量)时,我可能会在不同的情况下收到字符串,但我应该能够定位<键,值>从HashMap忽略我从流量收到的情况。

我采用了这种方法

CaseInsensitiveString.java

    public final class CaseInsensitiveString {
            private String s;

            public CaseInsensitiveString(String s) {
                            if (s == null)
                            throw new NullPointerException();
                            this.s = s;
            }

            public boolean equals(Object o) {
                            return o instanceof CaseInsensitiveString &&
                            ((CaseInsensitiveString)o).s.equalsIgnoreCase(s);
            }

            private volatile int hashCode = 0;

            public int hashCode() {
                            if (hashCode == 0)
                            hashCode = s.toUpperCase().hashCode();

                            return hashCode;
            }

            public String toString() {
                            return s;
            }
    }

LookupCode.java

    node = nodeMap.get(new CaseInsensitiveString(stringFromEvent.toString()));

因此,我为每个事件创建了CaseInsensitiveString的新对象。因此,它可能会影响性能。

有没有其他办法解决这个问题?


当前回答

我发现需要你改变键的解决方案(例如,toLowerCase)非常不受欢迎,需要TreeMap的解决方案也不受欢迎。

由于TreeMap改变了时间复杂度(与其他hashmap相比),我认为简单地使用O(n)的实用方法更可行:

public static <T> T getIgnoreCase(Map<String, T> map, String key) {
    for(Entry<String, T> entry : map.entrySet()) {
        if(entry.getKey().equalsIgnoreCase(key))
            return entry.getValue();
    }
    return null;
}

这就是那个方法。由于牺牲性能(时间复杂度)看起来是不可避免的,至少不需要更改底层映射以适应查找。

其他回答

我想到了两个选择:

你可以直接使用s.toUpperCase().hashCode();作为地图的钥匙。 你可以使用TreeMap<String>和一个忽略大小写的自定义比较器。

否则,如果您更喜欢自己的解决方案,我宁愿不定义一种新的String,而是实现一个具有所需的大小写不敏感功能的新Map。

你可以使用CollationKey对象来代替字符串:

Locale locale = ...;
Collator collator = Collator.getInstance(locale);
collator.setStrength(Collator.SECONDARY); // Case-insensitive.
collator.setDecomposition(Collator.FULL_DECOMPOSITION);

CollationKey collationKey = collator.getCollationKey(stringKey);
hashMap.put(collationKey, value);
hashMap.get(collationKey);

使用排序器。忽略口音差异。

CollationKey API并不保证实现hashCode()和equals(),但实际上您将使用RuleBasedCollationKey,它实现了这些。如果你是偏执的,你可以使用TreeMap,它保证以O(log n)时间而不是O(1)的成本工作。

继承HashMap的子类,并创建一个在put和get(可能还有其他面向键的方法)时小写键的版本。

或者将HashMap合成到新类中,并将所有内容委托给映射,但要转换键。

如果需要保留原始键,可以维护双映射,或者将原始键与值一起存储。

您可以使用来自Eclipse Collections的基于HashingStrategy的映射

HashingStrategy<String> hashingStrategy =
    HashingStrategies.fromFunction(String::toUpperCase);
MutableMap<String, String> node = HashingStrategyMaps.mutable.of(hashingStrategy);

注意:我是Eclipse Collections的贡献者。

Map<String, String> nodeMap = 
    new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

这就是你所需要的。