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

时间:2010-06-22 11:41:52

标签: java

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

感谢

9 个答案:

答案 0 :(得分:30)

不是传统的地图。

“abc”是来自“ABC”的不同字符串,它们的哈希码是不同的,它们的equals()方法将相互返回false。

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

如果你想保持密钥的大小写,但是通过不区分大小写的比较,你可以考虑使用TreeMap并提供你自己的比较器,它将比较不区分大小写。但是,在走下这条路线之前要认真思考,因为最终出现一些不可调和的不一致 - 如果有人调用map.put("abc", 1)然后map.put("ABC", 2),那么密钥存储在地图中的情况是什么? ?你能说得有意义吗?你是否觉得如果有人用你的地图包装你的地图,例如HashMap你会失去功能吗?或者,如果有人碰巧正在迭代你的密钥集,并使用equals()进行自己的快速“包含”检查,你会得到不一致的结果?还会有很多其他类似的案例。 请注意,您违反了地图合同(因为密钥相等性为defined in terms of the equals() method on the keys),所以它在任何意义上都无法实现。

维护严格的大写地图 更容易使用和维护,并且具有实际上是合法地图实施的优势。

答案 1 :(得分: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);
    }
}

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

答案 2 :(得分:12)

使用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

答案 3 :(得分:8)

答案 4 :(得分: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;

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

答案 5 :(得分:4)

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

答案 6 :(得分:3)

最简单的方法是在插入密钥并查找密钥时自行折叠密钥。即。

map.put(key.toLowerCase(), value);

map.get(key.toLowerCase());

你可以子类化,例如如果您希望自动完成此操作,HashMap可以使用这些来获取您自己的类。

答案 7 :(得分:0)

创建自己的字符串类包装器,实现equals和hashcode,将其用作hashmap中的键:

   class MyStringKey
   {
      private String string;
      public String getString()
      {
         return string;
      }
      public void setString(String string)
      {
         this.string = string;
      }

      public boolean equals(Object o)
      {
         return o instanceof MyStringKey && this.equalsIgnoreCase(((MyStringKey)o).getString());
      }

      public boolean hashCode()
      {
         return string.toLowerCase().hashcode(); //STRING and string may not have same hashcode
      }
   }

答案 8 :(得分:0)

尝试提供符合您问题的要求的答案&#34;不要打扰密钥的情况&#34; ......

如果您在许多地方添加到地图中,这个答案可能会很乏味。在我的例子中,它只发生在用户创建一个新角色时(在我的游戏中)。以下是我处理这个问题的方法:

boolean caseInsensitiveMatch = false;
for (Map.Entry<String, Character> entry : MyServer.allCharacterMap.entrySet()) {
    if (entry.getKey().toLowerCase().equals(charNameToCreate.toLowerCase())){
        caseInsensitiveMatch = true;
        break;
    }
}

当然这需要在我的大型ConcurrentHashMap中循环,但对我有用。