Java foreach efficiency
Solution 1
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.
Solution 2
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.
Solution 3
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.
Solution 4
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 StatementThe meaning of the enhanced
for
statement is given by translation into a basicfor
statement.If the type of
Expression
is a subtype ofIterable
, then letI
be the type of the expression Expression.iterator()
. The enhancedfor
statement is equivalent to a basicfor
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[]
. LetL1 ... Lm
be the (possibly empty) sequence of labels immediately preceding the enhancedfor
statement. Then the meaning of the enhanced for statement is given by the following basicfor
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.
Solution 5
Yes, it's called only once either way
Related videos on Youtube
Comments
-
Flueras Bogdan over 4 years
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) { ... }
-
Brett almost 15 years(The syntax of the enhanced for loop is a bit back to front.)
-
James McMahon almost 15 yearsI don't know if I would agree with calling this premature optimization. It is reasonable to want to understand what the compiler is doing with your code. We also have no idea at what point in his project (if he is even working on a project and not asking academically) he is asking this. It could be at the very end.
-
-
hhafez almost 15 years"what you really should do is abandon this kind of micro-optimization worries entirely" The concern he had would have been hardly micro-optimisation in general...
-
Michael Borgwardt almost 15 yearsYou can construct a malicious special case that results in a huge performance problem for pretty much anything, but that doesn't change the fact that it almost certainly wouldn't have been a problem either way - for one thing, keysets are cached in any Map implementation I've ever seen.
-
Hari Krishna Ganji about 11 yearsWoah! thats interesting... Nice. +1.
-
JavaTechnical almost 11 yearsMicro optimization - I care about it! They make a difference in complex programs for sure!
-
JavaTechnical almost 11 yearsWhat if myMap.entrySet() doesn't return a constant value, (say myMap is updated within the loop like adding a key-value pair)? Is it called only once? Wouldn't that produce weird results?
-
Michael Borgwardt almost 11 years@JavaTechnical: Sure, they make a difference: they make complex programs harder to maintain, and possibly even slower. You should definitely care about them, in the form of avoiding them.
-
Valentin Rocher almost 11 years@JavaTechnical : the thing is, you're not supposed to change the content of the map while updating, otherwise you get a ConcurrentModificationException. If you need to change the map while iterating, the only safe way to do it is via an Iterator.
-
Carcigenicate almost 9 yearsSorry for the necro comment, but something seems weird here. Why is a println statement in the end-section of the for-loop, but the call to next is in the body? Seems like a very odd set-up.
-
Daniel Brady almost 9 years@Carcigenicate I agree, it is an intriguing set-up. But logically it pans out: by the time the end-section of the for-loop is executed, the
next
method will already have been called on the iterator, and so everything is awesome. Working with raw iterators always felt a bit odd to me, since in most cases the actual 'iteration' happens explicitly at the beginning of the loop, and I'm used it happening (conceptually, at least) at the end of the previous loop.