troyal
troyal

Reputation: 2519

How do I use a foreach loop in Java to loop through the values in a HashMap?

I am trying to compile the following code:

private String dataToString(){
    Map data = (HashMap<MyClass.Key, String>) getData();
    String toString = "";
    for( MyClass.Key key: data.keySet() ){
        toString += key.toString() + ": " + data.get( key );
    return toString;
}

I get an error in the for line that says:

incompatible types
found : java.lang.Object
required: MyClass.Key

The getData() method returns an Object (but in this case the Object returned has the HashMap structure). MyClass.Key is an enum that I have created for the purposes of my application (in another class file - MyClass).

When I created a foreach loop with the same structure in MyClass.java, I did not encounter this problem.

What am I doing wrong?

Upvotes: 37

Views: 101112

Answers (5)

Peter Štibran&#253;
Peter Štibran&#253;

Reputation: 32901

Motlin's answer is correct.

I have two notes...

  1. Don't use toString += ..., but use StringBuilder instead and append data to it.

  2. Cast which Martin suggested will give you unchecked warning, which you won't be able to get rid of, because it is really unsafe.

Another way, without warning (and with StringBuilder):

private String dataToString(){
    Map<?, ?> data = (Map<?, ?>) getData();
    StringBuilder toString = new StringBuilder();
    for (Object key: data.keySet()) {
        toString.append(key.toString());
        toString.append(": ");
        toString.append(data.get(key));
    }
    return toString.toString();
}

This works, because toString method which you call on key is defined in Object class, so you don't need casting at all.

Using entrySet is even better way, as it doesn't need to do another look-up in map.

Upvotes: 3

dialex
dialex

Reputation: 2876

I found this simple example at java forum. Its syntax is very similar to the List's foreach, which was what I was looking for.

import java.util.Map.Entry;
HashMap nameAndAges = new HashMap<String, Integer>();
for (Entry<String, Integer> entry : nameAndAges.entrySet()) {
        System.out.println("Name : " + entry.getKey() + " age " + entry.getValue());
}

[EDIT:] I tested it and it works perfectly.

Upvotes: 5

Paul Tomblin
Paul Tomblin

Reputation: 182802

A slightly more efficient way to do this:

  Map<MyClass.Key, String> data = (HashMap<MyClass.Key, String>) getData(); 
  StringBuffer sb = new StringBuffer();
  for (Map.Entry<MyClass.Key,String> entry : data.entrySet()) {
       sb.append(entry.getKey());
       sb.append(": ");
       sb.append(entry.getValue());
   }
   return sb.toString();

If at all possible, define "getData" so you don't need the cast.

Upvotes: 43

Craig P. Motlin
Craig P. Motlin

Reputation: 26738

Change:

Map data = (HashMap<MyClass.Key, String>) getData();

to

Map<MyClass.Key, String> data = (HashMap<MyClass.Key, String>) getData();

The problem is that data.keySet() returns a Collection<Object> if data is just a Map. Once you make it generic, keySet() will return a Collection<MyClass.Key>. Even better... iterate over the entrySet(), which will be a Collection<MyClass.Key, String>. It avoids the extra hash lookups.

Upvotes: 38

Richard Campbell
Richard Campbell

Reputation: 3621

You could grab the entrySet instead, to avoid needing the key class:

private String dataToString(){    
    Map data = (HashMap<MyClass.Key, String>) getData();    
    String toString = "";    
    for( Map.Entry entry: data.entrySet() ) {        
        toString += entry.getKey() + ": " + entry.getValue();
    }    
    return toString;
}

Upvotes: 5

Related Questions