我想知道HashMap中是否存在特定的键,所以我使用的是containsKey(key)方法。但它区分大小写,即如果有一个带有Name的键并且我正在搜索名称,则它不会返回true。那么有什么方法我可以知道而不用打扰密钥的情况呢?
感谢
答案 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)
Apache commons中有CaseInsensitiveMap class
答案 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
使用equals
和hashCode
来测试密钥相等性,并且不能为String
覆盖这些密钥。你可以做的是定义你自己的Key类,它包含一个字符串值,但是以不区分大小写的方式实现equals
和hashCode
。
答案 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中循环,但对我有用。