[Java]Mapのループ

2016年8月15日月曜日

C# Java

JavaでMapをループする時に、なんとなしにkeySetを使ったりしていた。

MapIterate1.java
import java.util.HashMap;
import java.util.Map;

class MapIterate1 {
    public static void main(String ... args) {
        Map<String, String> map = new HashMap<>();
        map.put("banana", "yellow");
        map.put("apple", "red");
        map.put("orange", "orange");

        for (String key : map.keySet()) {
            System.out.println(key + ":" + map.get(key));
        }
    }
}

けれど、パフォーマスを考慮するとentrySetを使ってループしたほうがいいみたい。

MapIterate2.java
import java.util.HashMap;
import java.util.Map;

class MapIterate2 {
    public static void main(String ... args) {
        Map<String, String> map = new HashMap<>();
        map.put("banana", "yellow");
        map.put("apple", "red");
        map.put("orange", "orange");

        for (Map.Entry<String, String> elem : map.entrySet()) {
            System.out.println(elem.getKey() + ":" + elem.getValue());
        }
    }
}

FindBugs Bug Descriptionsにも以下の記載がある。

http://findbugs.sourceforge.net/bugDescriptions_ja.html#WMI_WRONG_MAP_ITERATOR
WMI: entrySet イテレータではなく効率が悪い keySet イテレータを使用している (WMI_WRONG_MAP_ITERATOR)
このメソッドは、keySet イテレータから取り出されたキーを使用して、マップエントリの値にアクセスしています。 Map の entrySet イテレータを使用したほうが Map.get(key) ルックアップを回避するのでより効率的です。

ちなみにC#だとDictionaryが持っているGetEnumeratorメソッドがそのままforeachに対応しているから、以下のように書ける。
using System;
using System.Collections.Generic;

class Program
{
    public static void Main(string[] args)
    {
        var dict = new Dictionary<string, string>()
        {
            { "banana", "yellow" },
            { "apple", "red" },
            { "orange", "orange" }
        };

        foreach (var elem in dict)
        {
            Console.WriteLine(elem.Key + ":" + elem.Value);
        }

    }
}