我想知道某个特定的键是否存在于HashMap中,所以我使用containsKey(key)方法。但它是区分大小写的,即如果名称中有一个键并且我正在搜索名称,它不会返回true。那么有没有什么办法可以在不打扰钥匙的情况下知道?如何检查映射中的键而不管情况如何?
谢谢
我想知道某个特定的键是否存在于HashMap中,所以我使用containsKey(key)方法。但它是区分大小写的,即如果名称中有一个键并且我正在搜索名称,它不会返回true。那么有没有什么办法可以在不打扰钥匙的情况下知道?如何检查映射中的键而不管情况如何?
谢谢
不符合传统的地图。
“abc”是与“ABC”不同的字符串,它们的哈希码不同,它们的equals()方法将相对于彼此返回false。
最简单的解决方案是在插入/检查前简单地将所有输入转换为大写(或小写)。你甚至可以编写你自己的Map
包装,这将确保一致性。
如果您想维护所提供的密钥的大小写,但不区分大小写,可以使用TreeMap并提供您自己的比较器来比较不区分大小写。然而,在走下这条路线之前,请仔细想一想,因为将会由于而导致一些不可调和的不一致 - 如果有人拨打map.put("abc", 1)
然后map.put("ABC", 2)
,那么存储在地图中的密钥是什么?你甚至可以让这有意义吗?您是否满意这样一个事实,即如果有人将您的地图用标准HashMap
你会失去功能?或者,如果有人碰巧通过你的密钥集进行迭代,并通过使用equals()
自己快速“包含”检查,你会得到不一致的结果?还会有很多其他类似的情况。 请注意,您违反了Map的合同(因为关键相等性为defined in terms of the equals() method on the keys),所以在任何意义上它都不可行。
维护一个严格的大写地图是很多更容易使用和维护,并具有实际上合法的Map实现的优势。
Map
使用equals
和hashCode
来测试关键相等性,并且您不能覆盖这些关于String
。你可以做的是定义你自己的Key类,它包含一个字符串值,但以不区分大小写的方式实现equals
和hashCode
。
有一个CaseInsensitiveMap class在Apache的百科全书
关于commons-collections的坏处在于它们不支持泛型,但最好不要重新发明轮子。 – Bozho 2010-06-22 11:49:46
您可以使用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
定义为常量。
'TreeMap'在这种情况下有点狡猾,因为它很容易违反'Map'的约定。 'containsKey()'应该“返回true当且仅当这个映射包含一个键'k'的映射,使得'(key == null?k == null:key.equals(k))''但当然它不会在这种情况下。如果地图以最普通的方式使用,某些时候这将导致不一致。 – 2010-06-22 11:54:28
@Andrzej是真实的,但并不比其他一些违反“Map”合同的行为,例如, 'IdentityHashMap
@Andrzej:只要你承认它没有'Map'定义的语义,就没问题。在'SortedMap' javadoc中已经声明了这一点:*请注意,如果有序映射要正确实现Map接口,那么由有序映射(无论是否提供显式比较器)维护的排序必须与equals保持一致。如果它们不一致,它仍然有效,它只是不再遵守'Map'。 – 2010-06-22 14:46:54
使用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
有一个潜伏在这里的bug,不常见但是破碎了:调用map.keyset( )。removeAll(c)'可能会或可能不会使用地图的比较器 - 取决于c的大小!参见[Sun bug 6394757](http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6394757)。 (我仍然使用它)。除此之外:番石榴的'ImmutableSorted(Map | Set)'集合让你的大脑休息,严格来说只使用比较器,永远不会等于。 – 2013-10-04 22:07:13
创建自己的字符串类的包装,实现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
}
}
这可能会更好,使关键不可变 – finnw 2010-06-22 13:48:14
要保留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;
注:在全世界并不是每个人都同意关于什么是什么的大写字母 - 一个著名的例子是,“大写版本我“在土耳其语中是”İ“,而不是”我“。
最简单的方法是在插入键并查找时自己折叠键。即
map.put(key.toLowerCase(), value);
和
map.get(key.toLowerCase());
你可以继承例如如果你想自动完成这些,使用HashMap来获得你自己的类。
在试图给出符合你的问题的要求“而不打扰键的情况下,”答案...
如果您在很多,很多地方添加到您的地图这样的回答可能是乏味的。在我的例子中,它只发生在用户创建一个新角色时(在我的游戏中)。下面是我如何处理这个问题:
boolean caseInsensitiveMatch = false;
for (Map.Entry<String, Character> entry : MyServer.allCharacterMap.entrySet()) {
if (entry.getKey().toLowerCase().equals(charNameToCreate.toLowerCase())){
caseInsensitiveMatch = true;
break;
}
}
当然这需要遍历我的大型ConcurrentHashMap,但对我有用。
同意最好的方法是只使用大写或小写插入。 – bwawok 2010-06-22 12:10:40
不幸的是,转换为大写/小写失败“土耳其测试”(谷歌,看看发生了什么字母'我')。如果国际化很重要,那么最好使用'TreeMap'并且是 - 请注意合同违规问题。 (IIRC,使用某些集合类调用'remove/retainAll'可能会带来意想不到的结果。)更好的是,在可能的情况下使用Guava的ImmutableSortedMap/Set,这可以避免给定一个自定义比较器时的不一致行为。 – 2015-12-27 21:43:32