【发布时间】:2021-12-23 12:47:09
【问题描述】:
在包括 Java 在内的大多数语言中,都有一个类似于 java.util.Map 的 API,它旨在简化循环一个值,给定映射到它的键。但是并不总是有一种方便的方法来查找密钥,给定密钥(我很确定 Python 使它变得困难,C++ 使它变得容易(只需要一个迭代器),这个问题是关于 Java 的,我怀疑它是和 Python 一样糟糕)。起初这听起来很愚蠢:为什么需要查找已有的密钥?但是考虑这样的事情(下面的例子使用Set而不是Map,但同样的想法):
TreeSet<String> dictionary = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
dictionary.add("Monday"); // populate dictionary
String word = "MONDAY"; // user input, or something
if(dictionary.contains(word)) System.out.println(word + " already in dictionary");
上面的代码 sn-p 将打印MONDAY already in dictionary。这当然是错误的,因为字典里没有“MONDAY”;相反,“星期一”是。我们怎样才能使信息更准确?在这种情况下,我们可以利用 TreeSet 是 NavigableSet 的事实创建一个帮助函数(实际上,类似的技巧适用于 SortedSet,虽然它不太方便。):
String lookup(NavigableSet<String> set, String key) {
assert set.contains(key) : key + " not in set";
return set.floor(key);
}
现在我们可以修复之前代码sn -p的最后一行了:
if(dictionary.contains(word)) System.out.println(lookup(word) + " already in dictionary");
这将打印正确的内容。但是现在让我们尝试一个带有哈希集的示例:
import java.util.HashSet;
/** Maintains a set of strings; useful as a replacement for String.intern() */
class StringInterner {
private final HashSet<String> set = new HashSet<>();
/** use this instead of String.intern() */
String intern(String s) {
if(!set.contains(s)) {
s.add(s);
return s;
}
for(String str : set) // linear scan!!
if(str.equals(s)) return str;
throw new AssertionError("something went very wrong");
}
}
上面的代码使用线性扫描来查找它已经知道的东西。请注意HashSet 可以很容易地给我们我们正在寻找的东西,因为它需要能够做到这一点只是为了实现contains()。但是它没有API,所以我们甚至不能问这个问题。 (实际上,HashMap 有一个名为 getNode 的内部方法,这几乎是我们想要的,但它是内部的。)在这种情况下,一个简单的解决方法是使用映射而不是集合:我们不是 set.add(s)可以改为使用map.put(s,s)。但是如果我们已经在使用地图了,因为我们已经有了想要与我们的键关联的数据呢?然后我们可以使用两个映射,并小心地保持它们同步,或者在我们的映射中存储一个大小为 2 的元组作为“值”,其中元组中的第一项只是映射键。这两种解决方案似乎都不必要地笨拙。
有没有更好的办法?
【问题讨论】:
-
使用 hashmap... 遍历 EntrySet。
-
当然,您可以随时进行线性扫描。但是哈希映射的好处是它们通常具有快速查找功能。似乎很遗憾失去它并使用暴力扫描。
标签: java dictionary collections