无论如何检查地图中的密钥?

Gur*_*lki 21 java

我想知道HashMap中是否存在特定的键,所以我使用的是containsKey(key)方法.但它区分大小写,即如果有一个带有Name的键并且我正在搜索名称,则它不会返回true.那么有什么方法我可以知道没有打扰钥匙的情况?

谢谢

And*_*yle 31

不是传统的地图.

"abc"是来自"ABC"的不同字符串,它们的哈希码是不同的,并且它们的equals()方法将相对于彼此返回false.

最简单的解决方案是在插入/检查之前简单地将所有输入转换为大写(或小写).您甚至可以编写自己的Map包装器,以确保一致性.

如果你想保持所提供的键的大小写,但是通过不区分大小写的比较,你可以考虑使用TreeMap并提供你自己的比较器,它将比较不区分大小写.然而,苦思冥想才去下来,你这条路线结束与一些不可调和的矛盾-如果有人呼叫map.put("abc", 1),然后map.put("ABC", 2),什么情况下是存储在地图的关键?你能说得有意义吗?如果有人用标准包装您的地图,HashMap您是否会失去功能?或者,如果有人碰巧正在迭代你的密钥集,并且通过使用equals()你自己的快速"包含"检查会得到不一致的结果?还会有很多其他类似的案例. 请注意,您通过执行此操作违反了Map的约定(因为键的相等性是根据键上的equals()方法定义的)所以它在任何意义上都是不可行的.

维护严格的大写映射容易使用和维护,并且具有实际上是合法的Map实现的优点.

  • 不幸的是,转换为大写/小写失败了"土耳其测试"(谷歌它,看看字母"我"会发生什么).如果国际化很重要,最好使用`TreeMap`并且是 - 请注意合同违规问题.(IIRC,使用某些集合类调用`remove/retainAll`可能会产生意想不到的结果.)更好的是,尽可能使用Guava的`ImmutableSortedMap/Set`,这样可以避免在给定自定义比较器时出现不一致的行为. (2认同)

Boz*_*zho 13

您可以使用TreeMap自定义,不区分大小写Comparator(使用String.compareToIgnoreCase())

例如:

Map<String, Something> map = 
    new TreeMap<String, Something>(CaseInsensitiveComparator.INSTANCE);

class CaseInsensitiveComparator implements Comparator<String> {
    public static final CaseInsensitiveComparator INSTANCE = 
           new CaseInsensitiveComparator();

    public int compare(String first, String second) {
         // some null checks
         return first.compareToIgnoreCase(second);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:似乎String已将此定义Comparator为常量.

  • 有趣的是你提到String定义了这个`Comparator`,但是不要告诉我们它是什么:`String.CASE_INSENSITIVE_ORDER` (4认同)

Bal*_*usC 13

使用TreeMap构造的String#CASE_INSENSITIVE_ORDER.

Map<String, String> map = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
map.put("FOO", "FOO");

System.out.println(map.get("foo")); // FOO
System.out.println(map.get("Foo")); // FOO
System.out.println(map.get("FOO")); // FOO
Run Code Online (Sandbox Code Playgroud)


gus*_*afc 6

要保留Map不变量,您可以自己创建密钥.实施合理hashCode/ equals你很高兴:

final class CaseInsensitive {
    private final String s;
    private final Local lc;
    public CaseInsensitive (String s, Locale lc) { 
        if (lc == null) throw new NullPointerException();
        this.s = s; 
        this.lc = lc; 
    }

    private s(){ return s == null ? null : s.toUpperCase(lc); }

    @Override
    public int hashCode(){ 
        String u = s();
        return (u == null) ? 0 : u.hashCode(); 
    }

    @Override
    public boolean equals(Object o){ 
        if (!getClass().isInstance(o)) return false;
        String ts = s(), os = ((CaseInsensitive)other).s();
        if (ts == null) return os == null;
        return ts.equals(os);
    }
}

// Usage:
Map<CaseInsensitive, Integer> map = ...;
map.put(new CaseInsensitive("hax", Locale.ROOT), 1337);
assert map.get(new CaseInsensitive("HAX", Locale.ROOT) == 1337;
Run Code Online (Sandbox Code Playgroud)

注意:并非全世界的每个人都同意什么是大写的 - 一个着名的例子是土耳其语中的"i"的大写版本是"İ"而不是"I".


Pét*_*rök 5

Map使用equalshashCode来测试密钥相等性,并且您不能为String. 你可以做的是定义包含一个字符串值,你自己的Key类,但工具equalshashCode在不区分大小写的方式。