如何检查地图中的密钥而不管情况如何?
我想知道HashMap中是否存在特定的键,所以我使用的是containsKey(key)方法。 但它区分大小写,即如果有一个带有Name的键并且我正在搜索名称,则它不会返回true。 那么有什么方法我可以知道而不用打扰钥匙的情况?
谢谢
不是传统的地图。
“abc”是来自“ABC”的不同字符串,它们的哈希码是不同的,并且它们的equals()方法将相对于彼此返回false。
最简单的解决方案是在插入/检查之前简单地将所有输入转换为大写(或小写)。 您甚至可以编写自己的Map
包装器,以确保一致性。
如果你想保持所提供的密钥的大小写,但是使用不区分大小写的比较,你可以考虑使用TreeMap并提供你自己的比较器,它将比较不区分大小写。 但是,在走这条路之前要认真思考,因为你最终会遇到一些不可调和的不一致 – 如果有人调用map.put("abc", 1)
然后map.put("ABC", 2)
,那么密钥存储的是什么情况在地图上? 你能说得有意义吗? 如果有人用标准的例如HashMap
包装您的地图,您会失去function吗? 或者,如果有人碰巧正在迭代你的密钥集,并使用equals()
进行自己的快速“包含”检查,你会得到不一致的结果吗? 还会有很多其他类似的案例。 请注意,这样做违反了Map的契约 (因为键的相等性是根据键上的equals()方法定义的 )所以它在任何意义上都是不可行的。
维护严格的大写映射更容易使用和维护,并且具有实际上是合法的Map实现的优点。
您可以将TreeMap
与自定义的不区分大小写的Comparator
(使用String.compareToIgnoreCase()
)一起使用
例如:
Map map = new TreeMap(CaseInsensitiveComparator.INSTANCE); class CaseInsensitiveComparator implements Comparator { public static final CaseInsensitiveComparator INSTANCE = new CaseInsensitiveComparator(); public int compare(String first, String second) { // some null checks return first.compareToIgnoreCase(second); } }
更新:似乎String
已将此Comparator
定义为常量。
使用由String#CASE_INSENSITIVE_ORDER
构造的TreeMap
。
Map map = new TreeMap(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
Apache commons中有一个CaseInsensitiveMap类
要保留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 map = ...; map.put(new CaseInsensitive("hax", Locale.ROOT), 1337); assert map.get(new CaseInsensitive("HAX", Locale.ROOT) == 1337;
注意:并非全世界的每个人都同意什么是大写的 – 一个着名的例子是土耳其语中的“i”的大写版本是“İ”而不是“我”。
Map
使用equals
和hashCode
来测试密钥相等性,并且不能为String
覆盖这些。 你可以做的是定义你自己的Key类,它包含一个字符串值,但以不区分大小写的方式实现equals
和hashCode
。
最简单的方法是在插入按键并查找按键时自行折叠按键。 即
map.put(key.toLowerCase(), value);
和
map.get(key.toLowerCase());
如果你想自动完成这个,你可以将例如HashMap子类化为自己的类。
创建自己的字符串类包装器,实现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 } }
试图提出符合您的问题要求的答案“不用打扰钥匙的情况” ……
如果您在许多地方添加到地图中,这个答案可能会很乏味。 在我的例子中,它只发生在用户创建一个新角色时(在我的游戏中)。 这是我如何处理这个:
boolean caseInsensitiveMatch = false; for (Map.Entry entry : MyServer.allCharacterMap.entrySet()) { if (entry.getKey().toLowerCase().equals(charNameToCreate.toLowerCase())){ caseInsensitiveMatch = true; break; } }
当然这需要在我的大型ConcurrentHashMap中循环,但对我有用。