不区分大小写的字符串作为HashMap键

r.s*_*r.s 157 java dictionary case-insensitive

我想使用不区分大小写的字符串作为HashMap键,原因如下.

  • 在初始化期间,我的程序使用用户定义的String创建HashMap
  • 在处理事件(在我的情况下是网络流量)时,我可能会在不同的情况下收到String但我应该能够<key, value>从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;
            }
    }
Run Code Online (Sandbox Code Playgroud)

LookupCode.java

    node = nodeMap.get(new CaseInsensitiveString(stringFromEvent.toString()));
Run Code Online (Sandbox Code Playgroud)

因此,我正在为每个事件创建一个CaseInsensitiveString的新对象.因此,它可能会影响性能.

有没有其他方法可以解决这个问题?

Roe*_*ker 285

Map<String, String> nodeMap = 
    new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
Run Code Online (Sandbox Code Playgroud)

这就是你真正需要的一切.

  • 请记住,TreeMap不是基本操作的固定时间.对于大多数应用程序来说不是问题,但值得记住.来自JavaDoc:"这个实现为containsKey,get,put和remove操作提供了有保证的log(n)时间成本.算法是对Cormen,Leiserson和Rivest的算法导论中的那些算法的改编." (17认同)
  • 到目前为止,这是最简单的,并且在迭代它们时也保留了键的大小写. (6认同)
  • 不需要`<K extends String>`因为`String`是final:`public static <V> Map <String,V> caseInsensitiveMap(){return new TreeMap <String,V>(String.CASE_INSENSITIVE_ORDER); }` (5认同)

Vis*_*hal 56

正如GuidoGarcía在回答中所建议的那样:

import java.util.HashMap;

public class CaseInsensitiveMap extends HashMap<String, String> {

    @Override
    public String put(String key, String value) {
       return super.put(key.toLowerCase(), value);
    }

    // not @Override because that would require the key parameter to be of type Object
    public String get(String key) {
       return super.get(key.toLowerCase());
    }
}
Run Code Online (Sandbox Code Playgroud)

要么

http://commons.apache.org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections4/map/CaseInsensitiveMap.html

  • 包含,putAll等怎么样? (26认同)
  • 这在某些语言中不起作用,例如土耳其语.谷歌"火鸡测试" (14认同)
  • @assylias:true,`containsKey()`和`remove()`应该以与`get()`相同的方式覆盖.`HashMap.putAll()`实现使用`put()`,所以这不应该是一个问题 - 只要HashMap实现保持不变.;)`get()`方法签名也将`Object`作为参数,而不是`String`.代码也不测试空键:`super.get(key == null?null:key.toString().toLowercase());` (4认同)

Ste*_*n C 14

一种方法是创建Apache Commons AbstractHashedMap类的自定义子类,重写hashisEqualKeys方法以执行不区分大小写的散列和键的比较.(注意 - 我自己从未尝试过这个...)

这样可以避免每次需要进行地图查找或更新时创建新对象的开销.常见的Map操作应该是O(1)......就像常规操作一样HashMap.

如果您准备接受他们所做的实施选择,Apache Commons 将为您CaseInsensitiveMap定制/专业化AbstractHashedMap.


但是如果O(logN)getput操作是可接受的,那么TreeMap带有不区分大小写的字符串比较器是一个选项; 例如使用String.CASE_INSENSITIVE_ORDER.

如果你不介意每次做一个put或者创建一个新的临时String对象get,那么Vishal的答案就好了.(虽然,我注意到如果你这样做,你就不会保留钥匙的原始情况......)


Dav*_*ton 6

子类HashMap并创建一个版本,用于降低键入putget(以及可能是其他面向键的方法).

或者将a HashMap合成到新类中并将所有内容委托给地图,但转换键.

如果您需要保留原始密钥,则可以维护双映射,也可以将原始密钥与值一起存储.


Gab*_*res 5

我想到了两个选择:

  1. 您可以直接使用 thes.toUpperCase().hashCode();作为Map.
  2. 您可以将 aTreeMap<String>Comparator忽略大小写的自定义一起使用。

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