我正在尝试使用哈希表实现一个字典(不使用Java提供的哈希表类,而是从头开始)。下面是我的Dictionary类中的insert()
方法,用于将元素插入到包含在特定数组位置的链表中。
我正在运行一个提供的测试程序来确定我的Dictionary类是否有效,但是当我到达某个点时遇到ArrayIndexOutOfBoundsException: -5980
。以下是特定测试。为什么这个例外会出现? (如果需要,我可以提供更多代码!)
插入:
public int insert(DictEntry pair) throws DictionaryException {
String entryConfig = pair.getConfig();
int found = find(entryConfig);
if (found != -1) {
throw new DictionaryException("Pair already in dictionary.");
}
int entryPosition = hash(entryConfig);
if (dict[entryPosition] == null) { //Dictionary.java:54
LinkedList<DictEntry> list = new LinkedList<DictEntry>();
dict[entryPosition] = list;
list.add(pair);
return 0;
} else {
LinkedList<DictEntry> list = dict[entryPosition];
list.addLast(pair);
return 1;
}
}
测试:
// Test 7: insert 10000 different values into the Dictionary
// NOTE: Dictionary is of size 9901
try {
for (int i = 0; i < 10000; ++i) {
s = (new Integer(i)).toString();
for (int j = 0; j < 5; ++j) s += s;
collisions += dict.insert(new DictEntry(s,i)); //TestDict.java:69
}
System.out.println(" Test 7 succeeded");
} catch (DictionaryException e) {
System.out.println("***Test 7 failed");
}
异常堆栈跟踪:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -5980
at Dictionary.insert(Dictionary.java:54)
at TestDict.main(TestDict.java:69)
哈希函数:
private int hash(String config) {
int expBase = 41;
int exp;
int ASCIIChar;
int hashedConfig = 0;
for (int i = 0; i < config.length(); i++) {
ASCIIChar = (int)config.charAt(i);
exp = (int)Math.pow(expBase, i);
hashedConfig = hashedConfig + (ASCIIChar * exp);
}
hashedConfig = hashedConfig % dictSize;
return hashedConfig;
}
答案 0 :(得分:3)
你的
exp = (int)Math.pow(expBase, i);
hashedConfig = hashedConfig + (ASCIIChar * exp);
将溢出整数范围,因此生成负数。在返回hashedConfig之前添加Math.abs
。
您可能应该分析它如何影响哈希函数的分布。
答案 1 :(得分:0)
正确 - 正如所怀疑的那样,问题出在hash()
。这样:
hashedConfig = hashedConfig % dictSize;
...不保证会是积极的。如果它开头是负面的,你仍然会得到一个负数。你可以使用:
hashedConfig = Math.abs(hashedConfig % dictSize);
或:
hashedConfig = hashedConfig % dictSize;
if (hashedConfig < 0) {
hashedConfig += dictSize;
}
两者都可以获得正确范围内的值,但可能发现后者更均匀。
请注意,您的散列代码效率也非常低。一点也不清楚为什么你不使用String.hashCode()
开头。 (您仍然需要将任意32位整数转换为数组元素范围内的值,但至少散列本身会更快。)