无限循环遍历哈希表

4
我正试图使用哈希表查找数组中最流行的单词。出现了无限循环的情况,我已经进行过调试,但元素始终不会从获取的第一个元素更改。你对这种情况有什么想法吗?
以下是我的代码:
import java.util.Hashtable;

public class MyClass {
  public String mostPopularString (String []words) {
    if (words == null)
            return null;
    if (words.length == 0)
            return null;
    Hashtable<String, Integer> wordsHash = new Hashtable<String, Integer>();
    for (String thisWord : words)
    {
        if (wordsHash.containsKey(thisWord))
        {
            wordsHash.put(thisWord, wordsHash.get(thisWord) + 1);
        }
        else
        {
            wordsHash.put(thisWord, 1);
        }
    }
    Integer mostPopularCount = 0;
    String mostPopularWord = null;
    boolean tie = false;
    while (wordsHash.keys().hasMoreElements())
    {
        String currentWord = (String) wordsHash.keys().nextElement();
        if (wordsHash.get(currentWord) > mostPopularCount)
        {
            mostPopularCount = wordsHash.get(currentWord);
            mostPopularWord = currentWord;
            tie = false;
        }
        else if (wordsHash.get(currentWord) == mostPopularCount)
        {
            tie = true;
        }
    }
    if (tie)
        return null;
    else
        return mostPopularWord;
  }
}
8个回答

10

在每次循环中调用wordsHash.keys()会给你一个新的Enumeration<String>,然后你在循环内再次调用它。

你想要只调用一次,并迭代单个的Enumeration<String>

Enumeration<String> iterator = wordsHash.keys();
while (iterator.hasMoreElements())
{
    String currentWord = iterator.nextElement();
    ...
}

请注意,由于您还需要获取每个元素的值,因此最好迭代entrySet()而不是keys()

您还可以更好地使用HashMap而不是Hashtable,这样您就可以使用增强的for循环...


非常感谢!那个方法真的很好用。只是需要另一双眼睛。 - kmaz13

8
问题出在这一行。
while (wordsHash.keys().hasMoreElements())

每次循环都会获得一个新的枚举副本。您需要获取一次键集,然后对其进行迭代。

在这里使用增强型for循环可能更容易。

   for (Map.Entry<String,Integer> entry : wordsHash.entrySet()) {
        String currentWord = entry.getKey();
        Integer currentCount = entry.getValue();
        //more code here
    }

这应该能提供您想要的行为,同时更简单易读。

严格来说,它是一个枚举类型,而不是列表。 - jtahlborn

6
问题在于每当你调用 wordsHash.keys() 时,它都会返回一个新的枚举。
while (wordsHash.keys().hasMoreElements())                        // <=== HERE
{
    String currentWord = (String) wordsHash.keys().nextElement(); // <=== AND HERE

您需要做的是创建一个单一的枚举并在整个循环中使用它。
附注:为什么您使用Hashtable而不是HashMap?

2
每次调用 .keys() 都会返回一个新的枚举对象,它有一个新的内部指针以用于迭代:
Hashtable table = new Hashtable();
table.put("a", "a");
table.put("b", "b");
boolean b = table.keys() == table.keys();
System.out.println(b); // false
                       // the two calls to `.keys()` returned different instances of Enumeration

将你的keys枚举分配给一个变量:

Enumeration keys = wordsHash.keys();
while (keys.hasMoreElements())
{
    String currentWord = (String) keys.nextElement();

}

1

将您的代码更改为:

Enumeration<String> keys = wordsHash.keys();
while (keys.hasMoreElements()) {
    String currentWord = keys.nextElement();

为了避免在每次进入循环时创建指向HashTable第一个键的新枚举。

0

每次循环迭代,您都会获得一个新的可迭代对象,其中包含所有键:wordsHash.keys() 只要它至少有一个键,while 循环就不会结束。

替换为:

while (wordsHash.keys().hasMoreElements()){
   String currentWord = (String) wordsHash.keys().nextElement();

通过

for (String currentWord: wordsHash.keys()){

0

另外,与您的枚举问题无关,这可能是一个缺陷:

else if (wordsHash.get(currentWord) == mostPopularCount)

这是一个java.lang.Integer与另一个java.lang.Integer的引用比较。它不是对它们所代表的实际值进行比较。它适用于“小”数字,因为自动装箱使用缓存引用,但最终会出现问题。你可能想要:

else if (wordsHash.get(currentWord) == mostPopularCount.intValue())

甚至可以在这里使用wordsHash.get(currentWord).compareTo(mostPopularCount) == 0 :) - Michał Kupisiński
是的,但是像这种情况下存在“compareTo”并需要它的存在会让人讨厌Java的想法并想成为项目经理 ;) - Affe

0

没有任何东西在修改wordsHash。这意味着如果wordsHash.keys().hasMoreElements()在程序中的某个时刻为真,它将继续保持为真。这会导致无限循环。你需要在进行操作时删除键或者直接使用for循环。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接