如何有效地遍历 Java Map 中的每个条目?

How do I efficiently iterate over each entry in a Java Map?

提问人:iMack 提问时间:9/6/2008 最后编辑:Barry ChapmaniMack 更新时间:7/5/2023 访问量:3298293

问:

如果我有一个用 Java 实现接口的对象,并且我希望遍历其中包含的每一对,那么浏览地图的最有效方法是什么?Map

元素的顺序是否取决于我为接口提供的特定映射实现?

Java 字典 集合 迭代

评论

48赞 Nitin Mahesh 7/26/2015
在 Java 8 中使用 Lambda 表达式:stackoverflow.com/a/25616206/1503859
5赞 akhil_mittal 10/10/2018
Java 8:stackoverflow.com/questions/46898/...

答:

266赞 pkaeding 9/6/2008 #1

是的,顺序取决于具体的 Map 实现。

@ScArcher2 具有更优雅的 Java 1.5 语法。在 1.4 中,我会做这样的事情:

Iterator entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Entry thisEntry = (Entry) entries.next();
  Object key = thisEntry.getKey();
  Object value = thisEntry.getValue();
  // ...
}

评论

45赞 jai 10/20/2009
更喜欢for循环而不是while..for(迭代器条目 = myMap.entrySet().iterator(); entries.hasNext();{...}使用此语法,“entries”范围仅缩减为 for 循环。
8赞 pkaeding 1/10/2012
@jpredham 你是对的,使用构造 as 不允许您修改集合,但@HanuAthena提到的示例应该有效,因为它为您提供了 in 范围。(除非我错过了什么......forfor (Entry e : myMap.entrySet)Iterator
1赞 JohnK 1/10/2015
IntelliJ 在以下方面给了我错误:无法识别。这是其他东西的伪代码吗?Entry thisEntry = (Entry) entries.next();Entry
1赞 pkaeding 1/14/2015
@JohnK尝试导入 .java.util.Map.Entry
1赞 1/21/2017
如果您有整数键和 String 键,则此解决方案将不起作用。
5866赞 ScArcher2 9/6/2008 #2
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}

在 Java 10+ 上:

for (var entry : map.entrySet()) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}

评论

95赞 ScArcher2 3/22/2010
如果你这样做,那么它将不起作用,因为 Entry 是 Map 中的嵌套类。java.sun.com/javase/6/docs/api/java/util/Map.html
293赞 jjujuma 4/30/2010
你可以把导入写成“import java.util.Map.Entry”,它就可以工作了。
63赞 assylias 10/8/2012
@Pureferret 您可能想要使用迭代器的唯一原因是您是否需要调用其方法。如果是这种情况,另一个答案会告诉你如何去做。否则,如上面的答案所示的增强循环是要走的路。remove
110赞 Josiah Yoder 12/5/2014
我相信 Map.Entry 形式比将内部类导入当前命名空间更清晰。
47赞 dguay 10/13/2016
请注意,如果只想循环访问值或键,则可以使用 或。map.values()map.keySet()
41赞 Leigh Caldwell 9/6/2008 #3

从理论上讲,最有效的方法将取决于 Map 的实现。执行此操作的官方方法是调用 ,它返回一组 ,每个 包含一个键和一个值 ( 和 )。map.entrySet()Map.Entryentry.getKey()entry.getValue()

在特殊实现中,使用 还是其他方式可能会有所不同。但我想不出为什么有人会这样写。最有可能的是,它对你所做的事情的性能没有影响。map.keySet()map.entrySet()

是的,顺序将取决于实现 - 以及(可能)插入顺序和其他难以控制的因素。

[编辑]我最初写的,但当然是答案。valueSet()entrySet()

151赞 Tom Hawtin - tackline 9/6/2008 #4

遍历地图的典型代码是:

Map<String,Thing> map = ...;
for (Map.Entry<String,Thing> entry : map.entrySet()) {
    String key = entry.getKey();
    Thing thing = entry.getValue();
    ...
}

HashMap是规范映射实现,并且不做保证(或者,如果没有对其执行更改操作,则不应更改顺序)。 将根据键的自然顺序返回条目,如果提供,则返回 。 将按插入顺序或访问顺序返回条目,具体取决于其构造方式。 按键的自然顺序返回条目。SortedMapComparatorLinkedHashMapEnumMap

(更新:我认为这不再是真的。请注意,迭代器当前有一个特殊的实现,它为 !但是,每当新的迭代器前进时,都会更新。IdentityHashMapentrySetMap.EntryentrySetMap.Entry

评论

6赞 Premraj 3/10/2011
EnumMap 和 IdentityHashMap 也具有这种特殊行为
1赞 jpaugh 1/27/2016
“LinkedHashMap 将返回 [...] 中的条目访问顺序 [...]“ ...所以你按照你访问元素的顺序来访问它们?要么是同义词,要么是可以使用题外话的有趣的东西。;-)
5赞 Tom Hawtin - tackline 1/27/2016
@jpaugh 仅直接访问计数。通过 、 、 等的,不修改顺序。LinkedHashMapiteratorspliteratorentrySet
1赞 Peter Mortensen 2/7/2018
1. 虽然如果?2. 最后一段可能会从复习中受益。
109赞 serg10 9/6/2008 #5

这是一个由两部分组成的问题:

如何遍历地图的条目 - @ScArcher2完美地回答了这个问题。

迭代的顺序是什么——如果你只是使用 ,那么严格来说,没有排序保证。因此,您不应该真正依赖任何实现给出的顺序。但是,SortedMap 接口扩展并提供了您正在寻找的内容 - 实现将提供一致的排序顺序。MapMap

NavigableMap 是另一个有用的扩展 - 这是一个附加方法,用于按条目在键集中的有序位置查找条目。因此,这有可能首先消除迭代的需要 - 您也许能够在使用 、 、 或 方法之后找到特定的方法。该方法甚至为您提供了反转遍历顺序的显式方法。SortedMapentryhigherEntrylowerEntryceilingEntryfloorEntrydescendingMap

64赞 ckpwong 9/6/2008 #6

仅供参考,如果您只对地图的键/值感兴趣,也可以使用 and,而对其他键/值不感兴趣。map.keySet()map.values()

63赞 Chris Dail 9/8/2008 #7

正确的方法是使用公认的答案,因为它是最有效的。我发现下面的代码看起来更干净一些。

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

评论

15赞 Jeff Olson 11/7/2009
这不是最好的方法,使用 entrySet() 的效率要高得多。Findbugs 将标记此代码(请参阅 findbugs.sourceforge.net/...)
6赞 kritzikratzi 10/8/2012
@JeffOlson嗯,不是真的。map lookup 为 O(1),因此两个循环的行为方式相同。诚然,在微基准测试中它会稍微慢一些,但我有时也会这样做,因为我讨厌一遍又一遍地编写类型参数。此外,这很可能永远不会成为您的性能瓶颈,因此,如果它使代码更具可读性,那就去做吧。
4赞 kritzikratzi 10/9/2012
更详细地说:几乎是大 O 符号的定义。你是对的,因为它运行得有点慢,但就复杂性而言,它们是相同的。O(1) = 2*O(1)
3赞 Holger 11/9/2016
@Jeff Olson:当只有一个恒定因素时,“大O”复杂性不会改变的评论是正确的。不过,对我来说,手术需要一小时还是两个小时很重要。更重要的是,必须强调的是,因子不是 ,因为迭代 根本就不进行查找;它只是所有条目的线性遍历。相比之下,遍历每个键并执行每个键的查找时,每个键都会进行一次查找,因此我们在这里谈论的是零查找与 n 个查找,n 是 . 所以这个因素远远超出了......2entrySet()keySet()Map2
3赞 Holger 11/9/2016
@kornero:值得注意的是,您不需要密钥具有相同的哈希码即可发生冲突;当相同时已经发生了碰撞。从 Java 8 开始,具有相同但不同或回退到只有具有相同哈希代码且不强加复杂性的键的项的复杂性。但是,查找的复杂性可能比实际情况更复杂的说法仍然成立。hashcode % capacityhashcode % capacityhashcodeComparableO(log n)ComparableO(n)O(1)
133赞 serg 8/19/2009 #8

使用迭代器和泛型的示例:

Iterator<Map.Entry<String, String>> entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Map.Entry<String, String> entry = entries.next();
  String key = entry.getKey();
  String value = entry.getValue();
  // ...
}

评论

15赞 Steve Kuo 2/18/2012
您应该放入 for 循环来限制其范围。Iterator
1赞 StudioWorks 2/4/2015
@SteveKuo “限制其范围”是什么意思?
16赞 ComFreek 3/14/2015
@StudioWorks .通过使用该结构,我们将(变量的可见性)的范围限制为 for 循环。for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }entries
30赞 abods 2/3/2010 #9

在 Java 1.4 中尝试以下操作:

for ( Iterator entries = myMap.entrySet().iterator(); entries.hasNext();) {

  Entry entry = (Entry) entries.next();

  System.out.println(entry.getKey() + "/" + entry.getValue());

  //...
}
21赞 Fathah Rehman P 5/17/2012 #10
public class abcd {
    public static void main(String[] args)
    {
        Map<Integer, String> testMap = new HashMap<Integer, String>();
        testMap.put(10, "a");
        testMap.put(20, "b");
        testMap.put(30, "c");
        testMap.put(40, "d");

        for (Integer key : testMap.keySet()) {
            String value = testMap.get(key);
            System.out.println(value);
        }
    }
}

public class abcd {
    public static void main(String[] args)
    {
        Map<Integer, String> testMap = new HashMap<Integer, String>();
        testMap.put(10, "a");
        testMap.put(20, "b");
        testMap.put(30, "c");
        testMap.put(40, "d");

        for (Entry<Integer, String> entry : testMap.entrySet()) {
            Integer key = entry.getKey();
            String value = entry.getValue();
        }
    }
}
44赞 Donald Raab 12/19/2012 #11

使用 Eclipse Collections,您将在 MapIterable 接口上使用该方法,该方法由 and 接口及其实现继承。forEachKeyValueMutableMapImmutableMap

MutableMap<Integer, String> map = 
    Maps.mutable.of(1, "One", 2, "Two", 3, "Three");

MutableBag<String> result = Bags.mutable.empty();
map.forEachKeyValue((key, value) -> result.add(key + value));

MutableBag<String> expected = Bags.mutable.of("1One", "2Two", "3Three");
Assertions.assertEquals(expected, result);

与 Eclipse Collections (EC) 实现一起使用比使用更有效的原因是 EC 实现不存储对象。与 EC 实现一起使用会导致动态生成对象。该方法能够避免创建对象,因为它可以直接导航实现的内部结构。在这种情况下,使用内部迭代器比使用外部迭代器有好处。forEachKeyValueMapentrySetMapMap.EntryentrySetMapMap.EntryforEachKeyValueMap.EntryMap

注意:我是Eclipse Collections的提交者。

13赞 Suresh Atta 3/22/2013 #12

是的,正如许多人所同意的那样,这是迭代 .Map

但是如果地图是.别忘了输入 .check。NullPointerExceptionnullnull

                                                 |
                                                 |
                                         - - - -
                                       |
                                       |
for (Map.Entry<String, Object> entry : map.entrySet()) {
    String key = entry.getKey();
    Object value = entry.getValue();
}
14赞 Learner 7/1/2013 #13

您可以使用泛型来做到这一点:

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
    Map.Entry<Integer, Integer> entry = entries.next();
    System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
344赞 The Coordinator 10/21/2013 #14

在 Java 8 中,您可以使用新的 lambda 功能干净快速地完成它:

 Map<String,String> map = new HashMap<>();
 map.put("SomeKey", "SomeValue");
 map.forEach( (k,v) -> [do something with key and value] );

 // such as
 map.forEach( (k,v) -> System.out.println("Key: " + k + ": Value: " + v));

和的类型将由编译器推断,无需再使用。kvMap.Entry

简单易行!

评论

12赞 Vitalii Fedorenko 6/28/2014
根据您要对地图执行的操作,您还可以对 docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html 返回的条目使用流 APImap.entrySet().stream()
3赞 Chris 4/21/2017
如果您想从 forEach() 中引用在 lambda 表达式外部声明的非最终变量,这将不起作用......
11赞 The Coordinator 4/22/2017
@Chris正确。如果您尝试有效地使用 lambda 外部的非最终变量,它将不起作用。
22赞 dmunozfer 11/29/2013 #15

如果您有一个通用的非类型化 Map,则可以使用:

Map map = new HashMap();
for (Map.Entry entry : ((Set<Map.Entry>) map.entrySet())) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}
6赞 J.B.Vala 1/3/2014 #16

有几种方法可以迭代地图。请参考以下代码。

当您使用接口迭代映射时,必须使用 或 .IteratorEntry<K,V>entrySet()

它看起来像这样:

    import java.util.*;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;

    public class IteratMapDemo {

        public static void main(String arg[]) {
            Map<String, String> mapOne = new HashMap<String, String>();
            mapOne.put("1", "January");
            mapOne.put("2", "February");
            mapOne.put("3", "March");
            mapOne.put("4", "April");
            mapOne.put("5", "May");
            mapOne.put("6", "June");
            mapOne.put("7", "July");
            mapOne.put("8", "August");
            mapOne.put("9", "September");
            mapOne.put("10", "Octomber");
            mapOne.put("11", "November");
            mapOne.put("12", "December");

            Iterator it = mapOne.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry me = (Map.Entry) it.next();
                // System.out.println("Get Key through While loop = " + me.getKey());
            }

            for (Map.Entry<String, String> entry:mapOne.entrySet()) {
                // System.out.println(entry.getKey() + "=" + entry.getValue());
            }

            for (Object key : mapOne.keySet()) {
                System.out.println("Key: " + key.toString() + " Value: " +
                                   mapOne.get(key));
            }
        }
    }

评论

0赞 edin-m 11/29/2019
keySet() 很慢
31赞 Rupesh Yadav 1/29/2014 #17

在地图中,可以迭代和/或和/或取决于自己感兴趣的in_例如:keysvaluesboth (e.g., entrySet)

  1. 遍历映射的键 -> keySet():

     Map<String, Object> map = ...;
    
     for (String key : map.keySet()) {
         //your Business logic...
     }
    
  2. 遍历映射的 values -> values():

     for (Object value : map.values()) {
         //your Business logic...
     }
    
  3. 遍历映射的 -> entrySet():

     for (Map.Entry<String, Object> entry : map.entrySet()) {
         String key = entry.getKey();
         Object value = entry.getValue();
         //your Business logic...
     }
    

此外,有 3 种不同的方法可以遍历 HashMap。它们如下:

//1.
for (Map.Entry entry : hm.entrySet()) {
    System.out.print("key,val: ");
    System.out.println(entry.getKey() + "," + entry.getValue());
}

//2.
Iterator iter = hm.keySet().iterator();
while(iter.hasNext()) {
    Integer key = (Integer)iter.next();
    String val = (String)hm.get(key);
    System.out.println("key,val: " + key + "," + val);
}

//3.
Iterator it = hm.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry entry = (Map.Entry) it.next();
    Integer key = (Integer)entry.getKey();
    String val = (String)entry.getValue();
    System.out.println("key,val: " + key + "," + val);
}
17赞 Fadid 3/20/2014 #18
    Iterator iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry element = (Map.Entry) it.next();
        LOGGER.debug("Key: " + element.getKey());
        LOGGER.debug("value: " + element.getValue());    
    }
36赞 Georgios Syngouroglou 8/4/2014 #19

爪哇 8:

您可以使用 lambda 表达式:

myMap.entrySet().stream().forEach((entry) -> {
    Object currentKey = entry.getKey();
    Object currentValue = entry.getValue();
});

有关详细信息,请按照操作。

评论

0赞 humblerookie 8/22/2014
@injecteer:似乎是 lambda 表达式的动机
10赞 Holger 11/9/2016
如果您只想遍历地图,则不需要流。 更简洁。myMap.forEach( (currentKey,currentValue) -> /* action */ );
2赞 fechidal89 8/22/2014 #20

我用以下代码将地图的数据复制到另一个地图:

    HashMap product = (HashMap) shopping_truck.get(i);
    HashMap tmp = new HashMap();
    for (Iterator it = product.entrySet().iterator(); it.hasNext();) {
        Map.Entry thisEntry = (Map.Entry) it.next();
        tmp.put(thisEntry.getKey(), thisEntry.getValue());
    }
3赞 JohnK 1/10/2015 #21

它并不能完全回答 OP 的问题,但可能对找到此页面的其他人有用:

如果只需要值而不需要键,则可以执行以下操作:

Map<Ktype, Vtype> myMap = [...];
for (Vtype v: myMap.values()) {
  System.out.println("value: " + v);
}

Ktype,是伪代码。Vtype

7赞 Mr. Polywhirl 5/13/2015 #22

这是一个通用的类型安全方法,可以调用它来转储任何给定的 .Map

import java.util.Iterator;
import java.util.Map;

public class MapUtils {
    static interface ItemCallback<K, V> {
        void handler(K key, V value, Map<K, V> map);
    }

    public static <K, V> void forEach(Map<K, V> map, ItemCallback<K, V> callback) {
        Iterator<Map.Entry<K, V>> it = map.entrySet().iterator();

        while (it.hasNext()) {
            Map.Entry<K, V> entry = it.next();

            callback.handler(entry.getKey(), entry.getValue(), map);
        }
    }

    public static <K, V> void printMap(Map<K, V> map) {
        forEach(map, new ItemCallback<K, V>() {
            @Override
            public void handler(K key, V value, Map<K, V> map) {
                System.out.println(key + " = " + value);
            }
        });
    }
}

以下是其使用示例。请注意,该方法推断出 的类型。Map

import java.util.*;

public class MapPrinter {
    public static void main(String[] args) {
        List<Map<?, ?>> maps = new ArrayList<Map<?, ?>>() {
            private static final long serialVersionUID = 1L;
            {
                add(new LinkedHashMap<String, Integer>() {
                    private static final long serialVersionUID = 1L;
                    {
                        put("One", 0);
                        put("Two", 1);
                        put("Three", 3);
                    }
                });

                add(new LinkedHashMap<String, Object>() {
                    private static final long serialVersionUID = 1L;
                    {
                        put("Object", new Object());
                        put("Integer", new Integer(0));
                        put("Double", new Double(0.0));
                    }
                });
            }
        };

        for (Map<?, ?> map : maps) {
            MapUtils.printMap(map);
            System.out.println();
        }
    }
}

输出

One = 0
Two = 1
Three = 3

Object = java.lang.Object@15db9742
Integer = 0
Double = 0.0
2赞 Joshua Michael Calafell 5/31/2015 #23

我相信这是最简单的方法......

/* For example, this could be a map object */
Map<String, Integer> MAP = new Map<>();

// Do something like put keys/value pairs into the map, etc...
MAP.put("Denver", 35);
MAP.put("Patriots", 14);

/* Then, simply use a for each loop like this to iterate */
for (Object o : MAP.entrySet()) {
    Map.Entry pair = (Map.Entry) o;
    // Do whatever with the pair here (i.e. pair.getKey(), or pair.getValue();
}
6赞 tomaj 8/30/2015 #24

如果遍历 的原因在于对值执行操作并写入生成的 。我建议使用 Google Guava 类中的 -methods。MapMaptransformMaps

import com.google.common.collect.Maps;

将 添加到导入后,您可以在地图上使用 和,如下所示:MapsMaps.transformValuesMaps.transformEntries

public void transformMap() {
    Map<String, Integer> map = new HashMap<>();
    map.put("a", 2);
    map.put("b", 4);

    Map<String, Integer> result = Maps.transformValues(map, num -> num * 2);
    result.forEach((key, val) -> print(key, Integer.toString(val)));
    // key=a,value=4
    // key=b,value=8

    Map<String, String> result2 = Maps.transformEntries(map, (key, value) -> value + "[" + key + "]");
    result2.forEach(this::print);
    // key=a,value=2[a]
    // key=b,value=4[b]
}

private void print(String key, String val) {
    System.out.println("key=" + key + ",value=" + val);
}
43赞 akhil_mittal 9/2/2015 #25

爪哇 8

我们有接受 lambda 表达式的方法。我们也有 API。考虑一张地图:forEach

Map<String,String> sample = new HashMap<>();
sample.put("A","Apple");
sample.put("B", "Ball");

遍历键:

sample.keySet().forEach((k) -> System.out.println(k));

遍历值:

sample.values().forEach((v) -> System.out.println(v));

遍历条目(使用 forEach 和 Streams):

sample.forEach((k,v) -> System.out.println(k + ":" + v)); 
sample.entrySet().stream().forEach((entry) -> {
            Object currentKey = entry.getKey();
            Object currentValue = entry.getValue();
            System.out.println(currentKey + ":" + currentValue);
        });

流的优点是,如果我们愿意,它们可以很容易地并行化。我们只需要用 in 代替上面的。parallelStream()stream()

forEachOrderedforEach 有流?不遵循相遇顺序(如果已定义),并且本质上是非确定性的,其中 as 是。所以不保证订单会被保留。另请查看内容以了解更多信息。forEachforEachOrderedforEach

39赞 Nitin Mahesh 10/6/2015 #26

Lambda表达式 Java 8

在 Java 1.8 (Java 8) 中,通过使用 Aggregate 操作(Stream 操作)中的 forEach 方法,这变得容易得多,该方法看起来类似于 Iterable Interface 中的迭代器。

只需将以下语句复制粘贴到您的代码中,并将 HashMap 变量从 hm 重命名为 HashMap 变量即可打印出键值对。

HashMap<Integer,Integer> hm = new HashMap<Integer, Integer>();
/*
 *     Logic to put the Key,Value pair in your HashMap hm
 */

// Print the key value pair in one line.

hm.forEach((k, v) -> System.out.println("key: " + k + " value:" + v));

// Just copy and paste above line to your code.

下面是我尝试使用 Lambda 表达式的示例代码。这东西太酷了。必须尝试。

HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();
    Random rand = new Random(47);
    int i = 0;
    while(i < 5) {
        i++;
        int key = rand.nextInt(20);
        int value = rand.nextInt(50);
        System.out.println("Inserting key: " + key + " Value: " + value);
        Integer imap = hm.put(key, value);
        if( imap == null) {
            System.out.println("Inserted");
        } else {
            System.out.println("Replaced with " + imap);
        }               
    }

    hm.forEach((k, v) -> System.out.println("key: " + k + " value:" + v));
    
Output:

Inserting key: 18 Value: 5
Inserted
Inserting key: 13 Value: 11
Inserted
Inserting key: 1 Value: 29
Inserted
Inserting key: 8 Value: 0
Inserted
Inserting key: 2 Value: 7
Inserted
key: 1 value:29
key: 18 value:5
key: 2 value:7
key: 8 value:0
key: 13 value:11

也可以使用 Spliterator 来实现相同的目的。

Spliterator sit = hm.entrySet().spliterator();

更新


包括指向 Oracle Docs 的文档链接。 有关 Lambda 的更多信息,请转到此链接,并且必须阅读聚合操作,对于 Spliterator,请转到此链接

3赞 Syd Lambert 10/28/2015 #27

如果要按照添加元素的顺序遍历映射,请使用 as 而不是 。LinkedHashMapMap

这种方法过去对我有用:

    LinkedHashMap<String, Integer> test = new LinkedHashMap();
    
    test.put("foo", 69);
    test.put("bar", 1337);

    for (int i = 0; i < test.size(); i++) {
        System.out.println(test.get(test.keySet().toArray()[i]));
    }

输出:

69
1337
1586赞 Slava Vedenin 2/23/2016 #28

为了总结其他答案并将它们与我所知道的结合起来,我找到了 10 种主要方法可以做到这一点(见下文)。此外,我还编写了一些性能测试(请参阅下面的结果)。例如,如果我们想找到一个映射的所有键和值的总和,我们可以这样写:

  1. 使用迭代器Map.Entry

    long i = 0;
    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Integer, Integer> pair = it.next();
        i += pair.getKey() + pair.getValue();
    }
    
  2. 使用 foreachMap.Entry

    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
    
  3. 在 Java 8 中使用 forEach

    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
    
  4. 使用 keySetforeach

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
    
  5. 使用 keySet迭代器

    long i = 0;
    Iterator<Integer> itr2 = map.keySet().iterator();
    while (itr2.hasNext()) {
        Integer key = itr2.next();
        i += key + map.get(key);
    }
    
  6. 使用 forMap.Entry

    long i = 0;
    for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
        Map.Entry<Integer, Integer> entry = entries.next();
        i += entry.getKey() + entry.getValue();
    }
    
  7. 使用 Java 8 Stream API

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
    
  8. 并行使用 Java 8 Stream API

    final long[] i = {0};
    map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
    
  9. 使用 IterableMapApache Collections

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
        i += it.next() + it.getValue();
    }
    
  10. 使用 MutableMap of Eclipse (CS) 集合

    final long[] i = {0};
    mutableMap.forEachKeyValue((key, value) -> {
        i[0] += key + value;
    });
    

性能测试(模式 = AverageTime,系统 = Windows 8.1 64 位,Intel i7-4790 3.60 GHz,16 GB)

  1. 对于小地图(100 个元素),得分 0.308 是最好的

    Benchmark                          Mode  Cnt  Score    Error  Units
    test3_UsingForEachAndJava8         avgt  10   0.308 ±  0.021  µs/op
    test10_UsingEclipseMap             avgt  10   0.309 ±  0.009  µs/op
    test1_UsingWhileAndMapEntry        avgt  10   0.380 ±  0.014  µs/op
    test6_UsingForAndIterator          avgt  10   0.387 ±  0.016  µs/op
    test2_UsingForEachAndMapEntry      avgt  10   0.391 ±  0.023  µs/op
    test7_UsingJava8StreamApi          avgt  10   0.510 ±  0.014  µs/op
    test9_UsingApacheIterableMap       avgt  10   0.524 ±  0.008  µs/op
    test4_UsingKeySetAndForEach        avgt  10   0.816 ±  0.026  µs/op
    test5_UsingKeySetAndIterator       avgt  10   0.863 ±  0.025  µs/op
    test8_UsingJava8StreamApiParallel  avgt  10   5.552 ±  0.185  µs/op
    
  2. 对于包含 10000 个元素的地图,得分 37.606 是最好的

    Benchmark                           Mode   Cnt  Score      Error   Units
    test10_UsingEclipseMap              avgt   10    37.606 ±   0.790  µs/op
    test3_UsingForEachAndJava8          avgt   10    50.368 ±   0.887  µs/op
    test6_UsingForAndIterator           avgt   10    50.332 ±   0.507  µs/op
    test2_UsingForEachAndMapEntry       avgt   10    51.406 ±   1.032  µs/op
    test1_UsingWhileAndMapEntry         avgt   10    52.538 ±   2.431  µs/op
    test7_UsingJava8StreamApi           avgt   10    54.464 ±   0.712  µs/op
    test4_UsingKeySetAndForEach         avgt   10    79.016 ±  25.345  µs/op
    test5_UsingKeySetAndIterator        avgt   10    91.105 ±  10.220  µs/op
    test8_UsingJava8StreamApiParallel   avgt   10   112.511 ±   0.365  µs/op
    test9_UsingApacheIterableMap        avgt   10   125.714 ±   1.935  µs/op
    
  3. 对于包含 100000 个元素的地图,得分 1184.767 是最好的

    Benchmark                          Mode   Cnt  Score        Error    Units
    test1_UsingWhileAndMapEntry        avgt   10   1184.767 ±   332.968  µs/op
    test10_UsingEclipseMap             avgt   10   1191.735 ±   304.273  µs/op
    test2_UsingForEachAndMapEntry      avgt   10   1205.815 ±   366.043  µs/op
    test6_UsingForAndIterator          avgt   10   1206.873 ±   367.272  µs/op
    test8_UsingJava8StreamApiParallel  avgt   10   1485.895 ±   233.143  µs/op
    test5_UsingKeySetAndIterator       avgt   10   1540.281 ±   357.497  µs/op
    test4_UsingKeySetAndForEach        avgt   10   1593.342 ±   294.417  µs/op
    test3_UsingForEachAndJava8         avgt   10   1666.296 ±   126.443  µs/op
    test7_UsingJava8StreamApi          avgt   10   1706.676 ±   436.867  µs/op
    test9_UsingApacheIterableMap       avgt   10   3289.866 ±  1445.564  µs/op
    

图形(性能测试取决于地图大小)

Enter image description here

表格(性能测试取决于地图大小)

          100     600      1100     1600     2100
test10    0.333    1.631    2.752    5.937    8.024
test3     0.309    1.971    4.147    8.147   10.473
test6     0.372    2.190    4.470    8.322   10.531
test1     0.405    2.237    4.616    8.645   10.707
test2     0.376    2.267    4.809    8.403   10.910
test7     0.473    2.448    5.668    9.790   12.125
test9     0.565    2.830    5.952   13.220   16.965
test4     0.808    5.012    8.813   13.939   17.407
test5     0.810    5.104    8.533   14.064   17.422
test8     5.173   12.499   17.351   24.671   30.403

所有测试都在 GitHub 上。

评论

11赞 GPI 5/12/2016
@Viacheslav : 非常好的答案。只是想知道 Java8 API 是如何在您的基准测试中通过捕获 lambda 来阻碍的......(例如 捕获多头,这可能比说的要慢。当然,你不能总是避免捕获状态,但这可能是对工作台的合理补充。long sum = 0; map.forEach( /* accumulate in variable sum*/);sumstream.mapToInt(/*whatever*/).sum
65赞 Holger 3/18/2017
@ZhekaKozlov:看看大得惊人的误差值。考虑测试结果 的测试结果意味着在从 到 的区间内有结果,因此最快的结果 () 的范围从 到 ,而第二慢的结果 () 在 和 之间运行,因此结果仍然有明显的重叠。现在看最慢的结果,这意味着在 和 之间发散,您知道这些测试结果毫无意义。x±ex-ex+e1184.767±332.96885215181706.676±436.867127021443289.866±1445.56418444735
8赞 Thierry 11/8/2017
比较 3 种主要实现:HashMap、LinkedHashMap 和 TreeMap 怎么样?
25赞 ErikE 7/15/2018
#1 和 #6 完全相同。使用 vs. 循环并不是一种不同的迭代技术。令我惊讶的是,在你的测试中,它们之间有如此大的差异——这表明这些测试没有正确地与与你打算测试的事物无关的外部因素隔离开来。whilefor
9赞 Todd Sewell 1/2/2019
#8是一个可怕的例子,因为在添加到 时现在有一个竞争条件。paralleli
13赞 Sajad NasiriNezhad 4/13/2016 #29
           //Functional Oprations
            Map<String, String> mapString = new HashMap<>();
            mapString.entrySet().stream().map((entry) -> {
                String mapKey = entry.getKey();
                return entry;
            }).forEach((entry) -> {
                String mapValue = entry.getValue();
            });

            //Intrator
            Map<String, String> mapString = new HashMap<>();
            for (Iterator<Map.Entry<String, String>> it = mapString.entrySet().iterator(); it.hasNext();) {
                Map.Entry<String, String> entry = it.next();
                String mapKey = entry.getKey();
                String mapValue = entry.getValue();
            }

            //Simple for loop
            Map<String, String> mapString = new HashMap<>();
            for (Map.Entry<String, String> entry : mapString.entrySet()) {
                String mapKey = entry.getKey();
                String mapValue = entry.getValue();

            }
27赞 Witold Kaczurba 10/26/2016 #30

排序将始终取决于特定的映射实现。 使用 Java 8,您可以使用以下任一方法:

map.forEach((k,v) -> { System.out.println(k + ":" + v); });

艺术

map.entrySet().forEach((e) -> {
            System.out.println(e.getKey() + " : " + e.getValue());
        });

结果将是相同的(相同的顺序)。由映射支持的 entrySet,因此您获得相同的顺序。第二个很方便,因为它允许您使用 lambda,例如,如果您只想打印大于 5 的 Integer 对象:

map.entrySet()
    .stream()
    .filter(e-> e.getValue() > 5)
    .forEach(System.out::println);

下面的代码显示了通过 LinkedHashMap 和普通 HashMap 进行的迭代(示例)。您将看到顺序的差异:

public class HMIteration {


    public static void main(String[] args) {
        Map<Object, Object> linkedHashMap = new LinkedHashMap<>();
        Map<Object, Object> hashMap = new HashMap<>();

        for (int i=10; i>=0; i--) {
            linkedHashMap.put(i, i);
            hashMap.put(i, i);
        }

        System.out.println("LinkedHashMap (1): ");
        linkedHashMap.forEach((k,v) -> { System.out.print(k + " (#="+k.hashCode() + "):" + v + ", "); });

        System.out.println("\nLinkedHashMap (2): ");

        linkedHashMap.entrySet().forEach((e) -> {
            System.out.print(e.getKey() + " : " + e.getValue() + ", ");
        });


        System.out.println("\n\nHashMap (1): ");
        hashMap.forEach((k,v) -> { System.out.print(k + " (#:"+k.hashCode() + "):" + v + ", "); });

        System.out.println("\nHashMap (2): ");

        hashMap.entrySet().forEach((e) -> {
            System.out.print(e.getKey() + " : " + e.getValue() + ", ");
        });
    }
}

输出:

LinkedHashMap (1):
10 (#=10):10, 9 (#=9):9, 8 (#=8):8, 7 (#=7):7, 6 (#=6):6, 5 (#=5):5, 4 (#=4):4, 3 (#=3):3, 2 (#=2):2, 1 (#=1):1, 0 (#=0):0,
LinkedHashMap (2):
10 : 10, 9 : 9, 8 : 8, 7 : 7, 6 : 6, 5 : 5, 4 : 4, 3 : 3, 2 : 2, 1 : 1, 0 : 0,
HashMap (1):
0 (#:0):0, 1 (#:1):1, 2 (#:2):2, 3 (#:3):3, 4 (#:4):4, 5 (#:5):5, 6 (#:6):6, 7 (#:7):7, 8 (#:8):8, 9 (#:9):9, 10 (#:10):10,
HashMap (2):
0 : 0, 1 : 1, 2 : 2, 3 : 3, 4 : 4, 5 : 5, 6 : 6, 7 : 7, 8 : 8, 9 : 9, 10 : 10,
9赞 Rupendra Sharma 4/6/2017 #31
package com.test;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Test {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("ram", "ayodhya");
        map.put("krishan", "mathura");
        map.put("shiv", "kailash");

        System.out.println("********* Keys *********");
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.println(key);
        }

        System.out.println("********* Values *********");
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }

        System.out.println("***** Keys and Values (Using for each loop) *****");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + "\t Value: "
                    + entry.getValue());
        }

        System.out.println("***** Keys and Values (Using while loop) *****");
        Iterator<Entry<String, String>> entries = map.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry<String, String>) entries
                    .next();
            System.out.println("Key: " + entry.getKey() + "\t Value: "
                    + entry.getValue());
        }

        System.out
                .println("** Keys and Values (Using java 8 using lambdas )***");
        map.forEach((k, v) -> System.out
                .println("Key: " + k + "\t value: " + v));
    }
}
11赞 shivampip 11/5/2017 #32

迭代 Map 非常容易。

for (Object key : map.keySet()) {
   Object value = map.get(key);
   // Do your stuff
}

例如,你有一个Map<String, int> data;

for (Object key : data.keySet()) {
  int value = data.get(key);
}

评论

2赞 michaeak 12/12/2018
嗯,这不必要地慢,因为首先要获取密钥,然后才能获取条目。备选方法:获取 entrySets,然后为每个 entryset 提供键和值
11赞 Utpal Kumar 11/13/2017 #33

有很多方法可以做到这一点。以下是几个简单的步骤:

假设你有一张地图,如下所示:

Map<String, Integer> m = new HashMap<String, Integer>();

然后,您可以执行如下操作来迭代地图元素。

// ********** Using an iterator ****************
Iterator<Entry<String, Integer>> me = m.entrySet().iterator();
while(me.hasNext()){
    Entry<String, Integer> pair = me.next();
    System.out.println(pair.getKey() + ":" + pair.getValue());
}

// *********** Using foreach ************************
for(Entry<String, Integer> me : m.entrySet()){
    System.out.println(me.getKey() + " : " + me.getValue());
}

// *********** Using keySet *****************************
for(String s : m.keySet()){
    System.out.println(s + " : " + m.get(s));
}

// *********** Using keySet and iterator *****************
Iterator<String> me = m.keySet().iterator();
while(me.hasNext()){
    String key = me.next();
    System.out.println(key + " : " + m.get(key));
}
14赞 ABHAY JOHRI 4/18/2018 #34

使用 Java 8:

map.entrySet().forEach(entry -> System.out.println(entry.getValue()));
25赞 bluehallu 4/19/2018 #35

Java 8 最紧凑:

map.entrySet().forEach(System.out::println);
57赞 Taras Melnyk 5/3/2018 #36

Java 8 中,您可以使用 forEach 和 lambda 表达式迭代 Map,

map.forEach((k, v) -> System.out.println((k + ":" + v)));
15赞 anandchaugule 11/27/2018 #37

基于 Map 的有效迭代解决方案是从 Java 5 到 Java 7 的循环。在这里:for

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

从 Java 8 开始,您可以使用 lambda 表达式来遍历 Map。这是一个增强的forEach

phnMap.forEach((k,v) -> System.out.println("Key: " + k + " Value: " + v));

如果你想为 lambda 编写一个条件,你可以这样写:

phnMap.forEach((k,v) -> {
    System.out.println("Key: " + k + " Value: " + v);
    if ("abc".equals(k)) {
        System.out.println("Hello abc");
    }
});
6赞 user1098063 6/6/2019 #38

我喜欢连接一个计数器,然后保存计数器的最终值;

int counter = 0;
HashMap<String, String> m = new HashMap<String, String>();
for (int i = 0; i < items.length; i++)
{
    m.put("firstname" + i, items.get(i).getFirstName());
    counter = i;
}

m.put("recordCount", String.valueOf(counter));

然后,当您要检索时:

int recordCount = Integer.parseInf(m.get("recordCount"));
for (int i = 0; i < recordCount; i++)
{
    System.out.println("First Name :" + m.get("firstname" + i));
}
6赞 Lova Chittumuri 8/2/2019 #39

使用 Java 7

Map<String,String> sampleMap = new HashMap<>();
for (sampleMap.Entry<String,String> entry : sampleMap.entrySet()) {
    String key = entry.getKey();
    String value = entry.getValue();

    /* your Code as per the Business Justification  */

}

使用 Java 8

Map<String,String> sampleMap = new HashMap<>();

sampleMap.forEach((k, v) -> System.out.println("Key is :  " + k + " Value is :  " + v));
24赞 Basil Bourque 10/27/2019 #40

如果我有一个在 Java 中实现 Map 接口的对象,并且我希望遍历其中包含的每一对,那么遍历映射的最有效方法是什么?

如果循环键的效率是应用的首要任务,请选择按所需顺序维护键的实现。Map

元素的顺序是否取决于我为接口提供的特定映射实现?

是的,当然可以。

  • 有些实现承诺一定的迭代顺序,有些则不然。Map
  • 不同的实现保持键值对的不同顺序。Map

请参阅我创建的这个表格,其中总结了与 Java 11 捆绑在一起的各种实现。具体而言,请注意迭代顺序列。单击/点击以缩放。Map

Table of map implementations in Java 11, comparing their features

您可以看到有四种 Map 实现保持顺序

  • TreeMap
  • ConcurrentSkipListMap
  • LinkedHashMap
  • EnumMap

NavigableMap接口

其中两个实现了 NavigableMap 接口:& 。TreeMapConcurrentSkipListMap

较旧的 SortedMap 接口实际上已被较新的 NavigableMap 接口所取代。但您可能会发现第三方实现仅实现了较旧的接口。

自然秩序

如果您想要一个按键的“自然顺序”排列其对的 Map,请使用 TreeMapConcurrentSkipListMap。术语“自然秩序”是指实现可的键的类。compareTo 方法返回的值用于排序中的比较。

定制订单

如果要为用于维护排序顺序的键指定自定义排序例程,请传递适合键类的 Comparator 实现。使用 TreeMapConcurrentSkipListMap,将 .Comparator

原始广告订单

如果希望地图对保持插入地图时的原始顺序,请使用 LinkedHashMap

枚举定义顺序

如果使用枚举(如 DayOfWeekMonth)作为键,请使用 EnumMap 类。该类不仅经过高度优化,使用非常少的内存并且运行速度非常快,而且它按照枚举定义的顺序维护您的对。例如,在迭代时将首先找到 的键,而 的键将是最后一个。DayOfWeekDayOfWeek.MONDAYDayOfWeek.SUNDAY

其他注意事项

在选择实现时,还要考虑:Map

  • NULL。某些实现禁止/接受 NULL 作为键和/或值。
  • 并发。如果要跨线程操作映射,则必须使用支持并发的实现。或者用 Collections::synchronizedMap 包装地图(不太可取)。

这两个注意事项都包含在上图中。

评论

1赞 filpa 2/17/2020
对一个答案的迟到评论,也迟到了(但信息量很大)。+1 来自我的提及,因为这是我第一次听说它。在很多情况下,这可能会派上用场。EnumMap
8赞 Ali Akram 1/6/2020 #41

Map.forEach

简单地使用 Map::forEach 将键和值都传递给 BiConsumer 怎么样?

map.forEach((k,v) -> {
    System.out.println(k + "->" + v);
});

评论

0赞 Basil Bourque 1/6/2020
这在 Lova Chittumuri 的回答中有所涉及。在维亚切斯拉夫·韦德宁 (Viacheslav Vedenin) 的高度赞成的答案中也作为项目 #3 进行了介绍。
0赞 Mahbubur Rahman Khan 3/6/2020
但仅在 API 标签 24 中可用
8赞 Younes El Ouarti 6/15/2020 #42

从 Java 10 开始,您可以使用局部变量推理(又名“var”)来使许多已经可用的答案不那么臃肿。例如:

for (var entry : map.entrySet()) {
    System.out.println(entry.getKey() + " : " + entry.getValue());
}
3赞 Dubstep 9/16/2020 #43
Map<String, String> map = 
for (Map.Entry<String, String> entry : map.entrySet()) {
    MapKey = entry.getKey() 
    MapValue = entry.getValue();
}
5赞 Badri Paudel 1/12/2021 #44

您可以搜索密钥,在密钥的帮助下,您可以找到映射的关联值,因为映射具有唯一的密钥,请参阅此处此处复制密钥时会发生什么。

演示地图:

    Map<String, String> map = new HashMap();
    map.put("name", "Name");
    map.put("age", "23");
    map.put("address", "NP");
    map.put("faculty", "BE");
    map.put("major", "CS");
    map.put("head", "MDK");

要仅获取密钥,您可以使用如下方法:map.keySet();

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

若要仅获取值,可以使用如下方法:map.values();

    for (String value : map.values()) {
        System.out.println(value);
    }

若要同时获取键及其值,您仍然可以使用并获取其相应的值,如下所示:map.keySet();

    // this prints the key + value pair
    for (String k : map.keySet()) {
        System.out.println(k + " " + map.get(k) + " ");
    }

map.get(key)给出该键指向的值。

25赞 anand krish 9/29/2021 #45

这些都是迭代 HashMap 的可能方法。

HashMap<Integer,String> map = new HashMap<Integer,String>();
    map.put(1, "David"); // Adding elements to Map
    map.put(2, "John");
    map.put(4, "Samyuktha");
    map.put(3, "jasmin");
    System.out.println("Iterating Hashmap...");

    // way 1 (java 8 Method)
    map.forEach((key, value) -> {
        System.out.println(key + " : " + value);
    });

    // way 2 (java 7 Method)
    for (Map.Entry me : map.entrySet()) {
        System.out.println(me.getKey() + " : " + me.getValue());
    }

    // way 3 (java 6 Method)
    for (Integer key : map.keySet()) {
        System.out.println(map.get(key));
    }

    // way 4 (Legacy way to iterate HashMap)
    Iterator iterator = map.entrySet().iterator(); // map.keySet().iterator()
    while (iterator.hasNext())
    {
        Map.Entry me = (Map.Entry)iterator.next();
        System.out.println(me.getKey() + " : " + me.getValue());
    }   
}