4

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

8 Answers 8

10

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...

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much! That worked really well. Just needed another set of eyes.
8

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.

1 Comment

technically it is an Enumeration, not a List.
6

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.

P.S. Why are you using Hashtable and not HashMap?

Comments

2

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();

}

Comments

1

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.

Comments

0

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

Comments

0

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()){

Comments

0

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())

2 Comments

or even wordsHash.get(currentWord).compareTo(mostPopularCount) == 0 should be used here :)
yes but the very existence of 'compareTo' and the need for it in cases like this makes a person hate the very idea of java and want to become a project manager ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.