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.
P.S. 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())
Related
I have a map of Object string.
I need to iterate over it twice before deciding whether to remove it or not.
protected void removeDayOutOfRangeObjects(Map<Object, String> objects, Calendar[] clientTimeRange) {
String currentPartOfWeek = null;
for(Calendar range: clientTimeRange){
int dayOfWeek = range.get(Calendar.DAY_OF_WEEK);
if(1 == dayOfWeek || 7 == dayOfWeek) {
currentPartOfWeek = "weekends";
} else {
currentPartOfWeek = "weekdays";
}
Iterator<Map.Entry<Object,String>> iter = objects.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Object,String> entry = iter.next();
if(currentPartOfWeek.matches(entry.getValue())){
continue;
} else {
iter.remove(); // I need to remove the object only if true for both entries in the Calendar array
}
}
}
}
The obvious solution would be to reverse the order of the loops. Have the outer loop iterate over the Map entries, and the inner loop iterate over the array. In the inner loop, set some flag to true the first time your currentPartOfWeek.matches(entry.getValue()) is false (or true, it wasn't clear from your question when you want to remove the entry), and only remove that entry if that flag is true.
This way you only iterate once over the Map.
Basically, it will look like this:
Iterator<Map.Entry<Object,String>> iter = objects.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Object,String> entry = iter.next();
boolean remove = false;
for(Calendar range: clientTimeRange) {
if (someCondition) {
if (remove) {
iter.remove();
} else {
remove = true;
}
}
}
}
Note that this relies on having two elements in your clientTimeRange array. If there are more elements, you should adjust the logic accordingly (i.e. decide when you want to remove the entry - after two elements of the array match the current Map entry? after all the elements of the array match the current entry?).
This function loops through a dictionary (allWords) and uses the
getKey function to generate a key. wordListMap is a HashMap> so I need to loop through and put the key and and a List. If there is not a list I put one if there is I just need to append the next dictionary word. This is where I need help. I just can't figure out the syntax to simply append the next word to the list that is already there. Any Help would be appreciated.
public static void constructWordListMap() {
wordListMap = new HashMap<>();
for (String w : allWords) {
int key = getKey(w);
if (isValidWord(w) && !wordListMap.containsKey(key)) {
List list = new ArrayList();
list.add(w);
wordListMap.put(key, list);
} else if (isValidWord(w) && wordListMap.containsKey(key)) {
wordListMap.put(key, wordListMap.get(key).add(w));
}
}
}
map.get(key).add(value)
Simple as that.
So I've gathered that you want to, given HashMap<Integer, List<String>>, you'd like to:
create a List object
add String objects to said List
add that List object as a value to be paired with a previously generated key (type Integer)
To do so, you'd want to first generate the key
Integer myKey = getKey(w);
Then, you'd enter a loop and add to a List object
List<String> myList = new List<String>;
for(int i = 0; i < intendedListLength; i++) {
String myEntry = //wherever you get your string from
myList.add(myEntry);
}
Lastly, you'd add the List to the HashMap
myHash.put(myKey, myList);
Leave any questions in the comments.
else if (isValidWord(w) && wordListMap.containsKey(key)) {
wordListMap.put(key, wordListMap.get(key).add(w));
}
If you want to add a new value to your list, you need to retrieve that list first. In the code above, you are putting the return value of add into the table (which is a boolean), and that is not what you want.
Instead, you will want to do as Paul said:
else if (isValidWord(w) && wordListMap.containsKey(key)) {
wordListMap.get(key).add(w);
}
The reason this works is because you already added an ArrayList to the table earlier. Here, you are getting that ArrayList, and adding a new value to it.
I know that there are lots of threads on NoSuchElementException in Java here but I still cannot figure out what is going on here
I am trying to come up with a solution for Transitive Dependencies Kata 18 which is posted at http://codekata.pragprog.com/2007/01/kata_eighteen_t.html
dependencies_for method is supposed to take in a char item and compute all dependencies for the item. The exception occurs when I try to add an element to finalDependencies ArrayList
This is the place where my NullPointerException occurs. I have traced all of these data structures and none of them have a Null value. I don't understand what is causing my exception here. Please see my code:
public class Test_Dependencies
{
public static void main(String[] args) {
Dependencies Dep = new Dependencies();
Dep.add_direct('A', "B C");
Dep.add_direct('B', "C D");
Dep.dependencies_for('A');
}
}
public class Dependencies {
HashMap dependenciesList;
public Dependencies()
{
HashMap<Character, ArrayList> dependenciesList = new HashMap<Character, ArrayList>();
}
public void add_direct(char mainItem, String dependentItems)
{
// code that works here
}
public String dependencies_for(char item)
{
ArrayList finalDependencies = new ArrayList<Character>();
Character key = new Character(item);
//get initial dependencies for the item and add them
ArrayList processingDependencies = dependenciesList.get(key);
Iterator itr = processingDependencies.iterator();
while(itr.hasNext())
{
if(finalDependencies.contains(itr.next()) == false && itr.next() != key)
{
// NoSuchElement exception here
finalDependencies.add(itr.next());
// look again at each item in dependenciesList. If it is in the list then add it to processingDependencies
if(dependenciesList.containsKey(itr.next()) && !processingDependencies.contains(itr.next()))
{
processingDependencies.add(itr.next());
}
}
}
// turn finalDependencies into a string
itr = finalDependencies.iterator();
String allDependencies = "";
while(itr.hasNext())
{
allDependencies = allDependencies + " " + itr.next();
}
return allDependencies;
}
}
I am a bit perprlexed because processingDependencies and finalDependencies ArrayLists are not null. And processingDependencies arraylist contains an item
You are calling twice. The first call is "protected" by a matching hasNext Call. The second is not. Save the result of next into a temporary variable and use that, instead of using the value directly, since every call to next will try to advance the iterator first. In the good case, you get an exception. In the bad case, things seem to work, but your program is dealing with the wrong value.
You can't do this:
while(itr.hasNext())
{
if(finalDependencies.contains(itr.next()) == false && itr.next() != key)
{
// NoSuchElement exception here
finalDependencies.add(itr.next());
// stuff removed
}
}
You must verify that iter.hasNext() is true prior to each call of itr.next(). What happens when you reach the last item in itr, but then call itr.next() three times?
Answer: NoSuchElementException. Check out Iterator
The problem is here:
HashMap dependenciesList;
public Dependencies()
{
HashMap<Character, ArrayList> dependenciesList = new HashMap<Character, ArrayList>();
}
You declare a hashmap called dependenciesList. You then try to instantiate that list, but what you actually do is create a local variable named the same thing. They are two separate variables. Then you try to use the one that hasn't been instantiated here:
ArrayList processingDependencies = dependenciesList.get(key);
What you need to do is instantiate the first dependenciesList instead of creating a new one
(I'm not a pro at java, but something like dependenciesList = new HashMap....() instead of HashMap<..> dependenciesList = new HashMap...() )
I have written a code to check if a string has the key in a give Map and replace the key with the value in the string.
Below is the code
String s ="VamshiKrishnaA";
Map<String,String> h = new HashMap<String,String>();
h.put("Vamshi", "89");
h.put("VamshiKrishnaA","dataDir");
h.put("VamshiKrishna","dataDira");
h.put("VamshiK", "krihsn");
String key="";
Iterator<String> i =h.keySet().iterator();
while(i.hasNext()){
key=i.next();
if(s.contains(key)){
s=s.replace(key, h.get(key));
}
}
System.out.println(s);
When i run the above code, i got the output as dataDiraA but i need output to be dataDir.
I dont have control over the order of the key and values it is autogenerated.
Need any help in this regard
You need to track the length of the currently matched key, and only use the one which is the longest:
String matchedKey = null;
while (i.hasNext()) {
key = i.next();
if (s.contains(key) && (matchedKey == null || matchedKey.length() < key.length())) {
matchedKey = key;
}
}
if (matchedKey != null) {
s = s.replace(matchedKey, h.get(matchedKey));
}
You should call equals() instead of contains():
if(s.equals(key))
If there is some way to order your keys, for example, by length, you might have look at TreeMap, where all keys will be ordered using provided comparator.
On the other hand, if there are not much keys and you knows exact ordering you might just use LinkedHashMap. This way keys will be iterated in the order you have put them. Like this:
String s = "VamshiKrishnaA";
Map<String, String> h = new LinkedHashMap<String, String>();
h.put("VamshiKrishnaA", "dataDir");
h.put("VamshiKrishna", "dataDira");
h.put("VamshiK", "krihsn");
h.put("Vamshi", "89");
for (String key : h.keySet()) {
if (s.contains(key)) {
s = s.replace(key, h.get(key));
}
}
System.out.println(s);
contains() checks for a sub string , where as equals() checks for whole string , and other difference is , contains() takes object of CharSequence class where as equals() takes Object as its parameter !
Using equals() method ::
String s = "VamshiKrishnaA";
Map<String, String> h = new HashMap<String, String>();
h.put("Vamshi", "89");
h.put("VamshiKrishnaA", "dataDir");
h.put("VamshiKrishna", "dataDira");
h.put("VamshiK", "krihsn");
String key = "";
Iterator<String> i = h.keySet().iterator();
while (i.hasNext()) {
key = i.next();
if (s.equals(key)) {
//s = s.replace(key, h.get(key));
System.out.println(h.get(key));
}
}
I want to remove strings of length 5 from a set, but it keeps outputting the set itself.
public void remove5()
{
Set<String> newSet = new HashSet<String>();
newSet.add("hello");
newSet.add("my");
newSet.add("name");
newSet.add("is");
newSet.add("nonsense");
for(String word: newSet)
{
if(word.length()==5)
{
newSet.remove(word); // Doesn't Help - throws an error Exception in thread "main" java.util.ConcurrentModificationException
}
}
System.out.println(newSet);
}
I want the output to be:
my
name
is
nonsense
(hello was removed because it's 5 characters)
But I get this everytime:
hello
my
name
is
nonsense
Can you please help?
Iterator<String> it= newStr.iterator();
while(it.hasNext()) { // iterate
String word = it.next();
if(word.length() == 5) { // predicate
it.remove(); // remove from set through iterator - action
}
}
For actually modifying your set, you need to do something like this:
Iterator<String> iter = newSet.iterator();
while (iter.hasNext())
if (iter.next().length() == 5)
iter.remove();
Since Strings are immutable, you can't modify the ones that were already added to the set, and anyway, even if you could modify them in-place, replacing them by "" would not remove them from the set.
As other suggested you cannot change a String reason being, Code snippet:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class TestString {
public void remove5() {
Set<String> newSet = new HashSet<String>();
newSet.add("hello");
newSet.add("my");
newSet.add("name");
newSet.add("is");
newSet.add("nonsense");
for (Iterator<String> iter = newSet.iterator(); iter.hasNext();) {
if (iter.next().length() == 5) {
iter.remove();
}
}
System.out.println(newSet);
}
public static void main(String[] args) {
new TestString().remove5();
}
}
If you iterate over the set and in the loop you remove the object, it will throw you ConcurrentModificationExceptionas HastSet iterator is a fail fast Iterator.
When you find a string of length 5, you need to remove it from the set:
newSet.remove(word);
As it is, you appear to be trying to change word to an empty string, but strings are immutable. What your call actually does is return an empty string.
Strings are immutable, changes made to the String word or any other String will not reflect in the string of Set
add
if(word.length()==5)
{
word.replaceAll(word, "");
newSet.remove(word);
}
you can refer to this function of HashSet
remove(Object o)
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/HashSet.html
Strings are immutable in Java, that means when you call word.replaceAll(word,""), it returns the String "" (which you aren't assigning to anything). The word doesn't change and the Set is still pointing to the old value of word. You need to remove word from the Set itself.
int i = 0;
Set<String> newSet = new HashSet<String>();
newSet.add("hello");
newSet.add("my");
newSet.add("name");
newSet.add("is");
newSet.add("nonsense");
for(String word: newSet)
{
if(word.length()==5)
{
newSet.remove(i);
}
i++;
}