Java foreach 效率

问题描述 投票:0回答:6

我有这样的东西:

Map<String, String> myMap = ...;

for(String key : myMap.keySet()) {
   System.out.println(key);
   System.out.println(myMap.get(key)); 
}

那么

myMap.keySet()
foreach循环中被调用一次吗?我想是的,但我想确定一下。

我想知道以这种方式使用 foreach (

myMap.keySet()
) 是否会对性能产生影响或者等同于:

Set<String> keySet = myMap.keySet();
for (String key : keySet) {
   ...
}
java performance loops foreach premature-optimization
6个回答
64
投票

如果你想绝对确定,那么就两种方式编译它,然后反编译它并进行比较。 我用以下来源做到了这一点:

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

当我用 Jad 反编译类文件时,我得到:

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

这就是你的答案。 它以任一 for 循环形式调用一次。


35
投票

只调用一次。事实上,它使用迭代器来完成这个任务。

此外,就你的情况而言,我认为你应该使用

for (Map.Entry<String, String> entry : myMap.entrySet())
{
    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

避免每次都在地图中搜索。


9
投票

keySet()
只被调用一次。 “增强型 for 循环”基于
Iterable
接口,它使用该接口来获取
Iterator
,然后将其用于循环。甚至不可能以任何其他方式迭代
Set
,因为没有索引或任何可以用来获取单个元素的东西。

但是,您真正应该做的是完全放弃这种微观优化的担忧 - 如果您遇到真正的性能问题,则 99% 的可能性是您自己从未考虑过的问题。


7
投票

答案就在Java语言规范中,不需要反编译:)这就是我们可以读到的增强的for语句:

增强的 for 语句有 形式:

EnhancedForStatement:
        for ( VariableModifiersopt Type Identifier: Expression) Statement

表达式必须具有类型

Iterable
否则它必须是 数组类型(第 10.1 节),或编译时 发生错误。

声明的局部变量的作用域 在 FormalParameter 部分 增强的

for
语句(§14.14)是 所包含的声明

增强的意义

for
声明被翻译成 基本的
for
声明。

如果

Expression
的类型是
Iterable
的子类型,则令
I
为 表达式的类型 表情。
iterator()
。增强的
for
语句是等效的 的基本
for
陈述 形式:

for (I #i = Expression.iterator(); #i.hasNext(); ) {

        VariableModifiersopt Type Identifier = #i.next();
   Statement
}

其中

#i
是编译器生成的 不同于任何的标识符 其他标识符(编译器生成的 或其他)在范围内(§6.3) 在增强的点 出现语句。

否则,表达式必然 有一个数组类型,

T[]
。让
L1 ... Lm
是(可能是空的)序列 紧接在之前的标签 增强的
for
声明。然后 增强for语句的含义 由以下基本
for
给出 声明:

T[] a = Expression;
L1: L2: ... Lm:
for (int i = 0; i < a.length; i++) {
        VariableModifiersopt Type Identifier = a[i];
        Statement
}

其中 ai 是编译器生成的 不同于任何标识符的标识符 其他标识符(编译器生成的 或其他)在范围内 增强的 for 语句的点 发生。

在您的情况下,

myMap.keySet()
返回
Iterable
的子类型,因此您的增强型
for
语句相当于以下基本的
for
语句:

for (Iterator<String> iterator = myMap.keySet().iterator(); iterator.hasNext();) {
   String key = iterator.next();

   System.out.println(key);
   System.out.println(myMap.get(key)); 
}

并且

myMap.keySet()
因此仅被调用一次。


5
投票

是的,无论哪种方式都只调用一次


-3
投票

我相信它的编译器经过优化,每个循环条目仅运行一次。

© www.soinside.com 2019 - 2024. All rights reserved.