java集合 - 地图中的keyset()与entrySet()

Posted

技术标签:

【中文标题】java集合 - 地图中的keyset()与entrySet()【英文标题】:java collections - keyset() vs entrySet() in map 【发布时间】:2012-02-16 06:15:49 【问题描述】:

我把一个字符串数组元素是一个映射,其中字符串数组的元素是键,单词的频率是值,例如:

String[] args = "if","it","is","to","be","it","is","up","me","to","delegate";

那么地图就会有类似[ if:1, it:2 .... ]的条目

Set<String> keys = m.keySet();
System.out.println("keyset of the map : "+keys);

打印所有键:"if","it","is","to","be","it","is","up","me","to","delegate"

Set<Map.Entry<String, Integer>> entrySet = m.entrySet();
Iterator<Map.Entry<String, Integer>> i = entrySet.iterator();
while(i.hasNext())
    Map.Entry<String, Integer> element = i.next();
    System.out.println("Key: "+element.getKey()+" ,value: "+element.getValue());

打印所有键值对:

使用条目集打印所有值:

Key: if ,value: 1
Key: it ,value: 2
Key: is ,value: 2
Key: to ,value: 2
Key: be ,value: 1
Key: up ,value: 1
Key: me ,value: 1
Key: delegate ,value: 1

但下面的代码块应该打印与上面完全相同的输出,但它没有:

Iterator<String> itr2 = keys.iterator();
while(itr2.hasNext())
    //System.out.println(itr1.next()+" ");
    //System.out.println(m.get(itr1.next())+" ");
    System.out.println("Key: "+itr2.next()+" ,value: "+m.get(itr2.next()));

打印出来:

Key: if ,value: 2
Key: is ,value: 2
Key: be ,value: 1
Key: me ,value: 1

但是如果我们在 while 循环中取消注释第 1 行,即

System.out.println(itr1.next()+" ");

并注释该行

System.out.println("Key: "+itr2.next()+" ,value: "+m.get(itr2.next()));

然后我们得到所有密钥:"if","it","is","to","be","it","is","up","me","to","delegate";

如果我们使用m.get()itr2.next(),那么迭代器不会有几个键!

【问题讨论】:

如果你要存储很多整数值,你应该查看 fastutil 库而不是 j.u 集合。 键集不会有重复,我猜它不应该将“it”字符串打印两次。在此调用 Set keys = m.keySet(); 之后您正在打印的内容System.out.println("地图键集:"+keys); 另见Performance considerations for keySet() and entrySet() of Map 【参考方案1】:

为简单起见,请注意,每次您执行itr2.next() 时,指针都会移动到下一个元素,即此处,如果您仔细注意,那么根据您编写的逻辑,输出是完全正常的。 这可能有助于您更好地理解:

While 循环的第一次迭代(指针在第一个元素之前): 键:如果,值:2 itr2.next()=if; m.get(itr2.next()=it)=&gt;2

While 循环的第二次迭代(指针在第三个元素之前): 键:是,值:2 itr2.next()=is; m.get(itr2.next()=to)=&gt;2

While 循环的第 3 次迭代(指针在第 5 个元素之前): 键:be ,值:1 itr2.next()="be"; m.get(itr2.next()="up")=&gt;"1"

While 循环的第 4 次迭代(指针在第 7 个元素之前): 键:我,值:1 itr2.next()="me"; m.get(itr2.next()="delegate")=&gt;"1"

键:如果,值:1 键:它,值:2 键:是,值:2 键:到,值:2 键:是,值:1 键:向上,值:1 键:我,值:1 键:委托,值:1

打印出来:

键:如果,值:2 键:是,值:2 键:是,值:1 键:我,值:1

【讨论】:

【参考方案2】:

遍历大地图entrySet()keySet() 好得多。查看this 教程他们如何在entrySet( 的帮助下优化对大对象的遍历以及它如何有助于性能调整。

【讨论】:

【参考方案3】:

Iterator 只会向前移动,如果它读取一次,就完成了。你的

m.get(itr2.next());

正在读取itr2.next(); 的下一个值,这就是为什么您缺少几个(实际上不是几个,每隔一个)键。

【讨论】:

【参考方案4】:

每次调用Iterator.next() 都会将迭代器移动到下一个元素。如果要在多个语句或表达式中使用当前元素,则必须将其存储在局部变量中。或者更好的是,为什么不简单地使用 for-each 循环?

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

此外,遍历 entrySet 更快,因为您不会为每个键查询两次映射。另外Map.Entry 实现通常实现toString() 方法,因此您不必手动打印键值对。

for (Entry<String, Integer> entry : map.entrySet()) 
    System.out.println(entry);

【讨论】:

感谢您提到这一点非常重要:使用条目集来避免调用get() 的不必要开销。 “因为你没有为每个键查询两次地图”为什么两次?我以为你在做 map.get(key) 时只查询一次? @HenryNguyen 我相信他的意思是你在使用迭代器获取密钥时查询它一次【参考方案5】:

每次调用 itr2.next() 时,都会得到一个不同的值。值不一样。您应该只在循环中调用一次。

Iterator<String> itr2 = keys.iterator();
    while(itr2.hasNext())
        String v = itr2.next();
        System.out.println("Key: "+v+" ,value: "+m.get(v));
    

【讨论】:

在 Effective Java 书中提到了同样的错误,这就是为什么使用 foreach 循环是首选的原因。 很幸运,您的地图中有偶数个条目,否则您会遇到 RuntimeException +1 对阿米尔的评论。除非您需要从集合中移除项目,否则直接使用 Iterator 只会引入出错的可能性。对于简单的读取循环,您应该使用 foreach 循环样式。

以上是关于java集合 - 地图中的keyset()与entrySet()的主要内容,如果未能解决你的问题,请参考以下文章

Map集合中value()方法与keySet()entrySet()区别

Java中的Map 笔记

java集合

如何通过获取map中的key来获得与key对应的value值,进行运算

java集合Map&HashMap

Java集合框架--Map接口概述 & Map接口使用 & entrySet和keySet()遍历map谁的效率更高?