I have something like this:
Map<String, String> myMap = ...;
for(String key : myMap.keySet()) {
System.out.println(key);
System.out.println(myMap.get(key));
}
So is myMap.keySet()
called once in the foreach loop?
I think it is, but want your opinion.
I would like to know if using foreach in this way (myMap.keySet()
) has a performance impact or it is equivalent to this:
Set<String> keySet = myMap.keySet();
for (String key : keySet) {
...
}
If you want to be absolutely certain, then compile it both ways and decompile it and compare. I did this with the following source:
public void test() {
Map<String, String> myMap = new HashMap<String, String>();
for (String key : myMap.keySet()) {
System.out.println(key);
System.out.println(myMap.get(key));
}
Set<String> keySet = myMap.keySet();
for (String key : keySet) {
System.out.println(key);
System.out.println(myMap.get(key));
}
}
and when I decompiled the class file with Jad, I get:
public void test()
{
Map myMap = new HashMap();
String key;
for(Iterator iterator = myMap.keySet().iterator(); iterator.hasNext(); System.out.println((String)myMap.get(key)))
{
key = (String)iterator.next();
System.out.println(key);
}
Set keySet = myMap.keySet();
String key;
for(Iterator iterator1 = keySet.iterator(); iterator1.hasNext(); System.out.println((String)myMap.get(key)))
{
key = (String)iterator1.next();
System.out.println(key);
}
}
So there's your answer. It is called once with either for-loop form.
It's only called once. In fact it uses an iterator to do the trick.
Furthermore, in your case, I think you should use
for (Map.Entry<String, String> entry : myMap.entrySet())
{
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
to avoid searching in the map each time.
keySet()
is called only once. The "enhanced for loop" is based on the Iterable
interface, which it uses to obtain an Iterator
, which is then used for the loop. It's not even possible to iterate over a Set
in any other way, since there is no index or anything by which you could obtain individual elements.
However, what you really should do is abandon this kind of micro-optimization worries entirely - if you ever have real performance problems, the chance is about 99% that it's something you'd never thought about on your own.
The answer is in the Java Language Specification, not need to decompile :) This is what we can read about the enhanced for statement:
The enhanced for statement has the
form:
EnhancedForStatement:
for ( VariableModifiersopt Type Identifier: Expression) Statement
The Expression must either have type
Iterable
or else it must be of an
array type (§10.1), or a compile-time
error occurs.
The scope of a local variable declared
in the FormalParameter part of an
enhanced for
statement (§14.14) is
the contained Statement
The meaning of the enhanced for
statement is given by translation into
a basic for
statement.
If the type of Expression
is a
subtype of Iterable
, then let I
be
the type of the expression
Expression.iterator()
. The enhanced for
statement is equivalent
to a basic for
statement of the
form:
for (I #i = Expression.iterator(); #i.hasNext(); ) {
VariableModifiersopt Type Identifier = #i.next();
Statement
}
Where #i
is a compiler-generated
identifier that is distinct from any
other identifiers (compiler-generated
or otherwise) that are in scope (§6.3)
at the point where the enhanced for
statement occurs.
Otherwise, the Expression necessarily
has an array type, T[]
. Let L1 ... Lm
be the (possibly empty) sequence of
labels immediately preceding the
enhanced for
statement. Then the
meaning of the enhanced for statement
is given by the following basic for
statement:
T[] a = Expression;
L1: L2: ... Lm:
for (int i = 0; i < a.length; i++) {
VariableModifiersopt Type Identifier = a[i];
Statement
}
Where a and i are compiler-generated
identifiers that are distinct from any
other identifiers (compiler-generated
or otherwise) that are in scope at the
point where the enhanced for statement
occurs.
In your case, myMap.keySet()
returns a subtype of Iterable
so your enhanced for
statement is equivalent to the following basic for
statement:
for (Iterator<String> iterator = myMap.keySet().iterator(); iterator.hasNext();) {
String key = iterator.next();
System.out.println(key);
System.out.println(myMap.get(key));
}
And myMap.keySet()
is thus called only once.
Yes, it's called only once either way
I believe it's compiler optimized to run only once per loop entry.