简体   繁体   中英

Infinite Loop Iterating Through Hashtable

I'm trying find the most popular word in an array using Hashtables. For some reason the while loop is looping infinitely. I've debugged and the element never changes from the first one it gets. Any ideas on why this is happening?

Here is my code:

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;
  }
}

You're calling wordsHash.keys() on each iteration of the loop, which gives you a fresh Enumeration<String> on each iteration - you're then calling it again inside the loop.

You want to call it once , and then iterate over the single Enumeration<String> :

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

Note that as you're also getting the value for each element, you'd be better off iterating over the entrySet() rather than the keys() .

You'd also be better off using HashMap instead of Hashtable , as then you could just use an enhanced for loop...

The problem is in line

while (wordsHash.keys().hasMoreElements())

each time through the loop, you are getting a new copy of the enumeration. You'll want to get the keyset once, and iterate over that.

It would probably be easier to use an enhanced for Loop here as well

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

This should provide the behavior you want, while being simpler and easier to read.

The problem is that whenever you call wordsHash.keys() , it returns a new enumeration:

while (wordsHash.keys().hasMoreElements())                        // <=== HERE
{
    String currentWord = (String) wordsHash.keys().nextElement(); // <=== AND HERE

What you need to do is create a single enumeration and use it throughout the loop.

PS Why are you using Hashtable and not HashMap ?

Every call to .keys() returns a new enumeration, with a new internal pointer for iterating:

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

So assign your keys enumeration to a variable:

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

}

Change your code to:

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

So that a new enumeration pointing to the first key of the HashTable is not created every time that you enter the loop.

Nothing is modifying the wordsHash . That means that if wordsHash.keys().hasMoreElements() is true once, it'll continue to be true for the rest of the program. This causes an infinite loop. You either need to remove the keys as you go along or you should just use a for

you get a new Iterable ofer all keys each loop iteration: wordsHash.keys() as long as there is at least one key in it the while loop never ends.

Replace:

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

by

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

Also, unrelated to your Enumeration issue, this is probably a defect:

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

That's a reference comparison of a java.lang.Integer to another java.lang.Integer. It is not a comparison of the actual values they represent. It is working for "small" numbers because auto-boxing uses cached references, but will eventually break. You probably want:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM