一.问题发现
今天,在写完代码后用Find Bugs扫锚了一下,发现类中一处代码中有提示如下内容:
Map<String, EventChain> map = ContextHolder.getContext().getEventChains();
for (Iterator<String> iter = map.keySet().iterator(); iter.hasNext();) {
String key = iter.next();
EventChain eventChain = eventChains.get(key);
}
makes inefficient use of keySet iterator instead of entrySet iterator
意思是说用keySet 方式遍历Map的性能不如entrySet性能好.起初想不明白,索性仔细看下代码:
二.常用的遍历HashMap的两种方法
1.第一种方式
Iterator<String> keySetIterator = keySetMap.keySet().iterator();
while (keySetIterator.hasNext()) {
String key = keySetIterator.next();
String value = keySetMap.get(key);
}
2.第二种方式
Iterator<Entry<String, String>> entryKeyIterator = entrySetMap.entrySet()
.iterator();
while (entryKeyIterator.hasNext()) {
Entry<String, String> e = entryKeyIterator.next();
String value=e.getValue();
}
三.性能比较
到底第二种方式的性能比第一种方式的性能高多少呢,通过一个简单的测试类可以看一下,测试代码如下:
public class HashMapTest {
public static void main(String[] args) {
HashMap<String, String> keySetMap = new HashMap<String, String>();
HashMap<String, String> entrySetMap = new HashMap<String, String>();
for (int i = 0; i < 1000; i++) {
keySetMap.put("" + i, "keySet");
}
for (int i = 0; i < 1000; i++) {
entrySetMap.put("" + i, "entrySet");
}
long startTimeOne = System.currentTimeMillis();
Iterator<String> keySetIterator = keySetMap.keySet().iterator();
while (keySetIterator.hasNext()) {
String key = keySetIterator.next();
String value = keySetMap.get(key);
System.out.println(value);
}
System.out.println("keyset spent times:"
+ (System.currentTimeMillis() - startTimeOne));
long startTimeTwo = System.currentTimeMillis();
Iterator<Entry<String, String>> entryKeyIterator = entrySetMap
.entrySet().iterator();
while (entryKeyIterator.hasNext()) {
Entry<String, String> e = entryKeyIterator.next();
System.out.println(e.getValue());
}
System.out.println("entrySet spent times:"
+ (System.currentTimeMillis() - startTimeTwo));
}
}
通过测试发现,第二种方式的性能通常要比第一种方式高一倍.
四.原因分析:
通过查看源代码发现,调用这个方法keySetMap.keySet()会生成KeyIterator迭代器,其next方法只返回其key值.
private class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
而调用entrySetMap.entrySet()方法会生成EntryIterator 迭代器,其next方法返回一个Entry对象的一个实例,其中包含key和value.
private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
二者在此时的性能应该是相同的,但方式一再取得key所对应的value时,此时还要访问Map的这个方法,这时,方式一多遍历了一次table.
public V get(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
Entry<K,V> e = table[i];
while (true) {
if (e == null)
return null;
if (e.hash == hash && eq(k, e.key))
return e.value;
e = e.next;
}
}
这个方法就是二者性能差别的主要原因.
分享到:
相关推荐
除了使用EntrySet的迭代器,我们还可以使用KeySet的迭代器来遍历HashMap中的数据。其实现代码如下: ```java Iterator<Integer> iterator = coursesMap.keySet().iterator(); while (iterator.hasNext()) { Integer...
遍历HashMap是开发者经常遇到的任务,尤其在处理数据操作时。本篇文章将详细介绍如何使用简单的方法来遍历HashMap,通过实例代码帮助理解。 HashMap遍历的常用方法主要有三种:迭代器(Iterator)遍历、键集...
3. 通过Map.entrySet遍历key和value 这是一种使用增强for循环遍历键值对的方法。 ```java for (Map.Entry, String> entry : map.entrySet()) { System.out.println("key= " + entry.getKey() + " and value= " + ...
遍历HashMap是常见的操作,本文将介绍六种不同的方法来实现这一功能。 1. **方式一:使用KeySet方法** KeySet方法返回HashMap中所有键的Set视图。由于Set接口实现了Iterable接口,我们可以使用for-each循环来遍历...
本文将深入探讨`HashMap`的遍历方法,包括`keySet()`和`entrySet()`两种主要方式,并通过代码示例对比它们的性能差异。 #### 方法一:使用`keySet()`遍历 `keySet()`方法返回`HashMap`中的所有键的集合视图。通过...
可以通过2种方法遍历HashMap <br>Map map = new HashMap(); <br>for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { <br> Map.Entry entry = (Map.Entry) iter.next(); <br> Object ...
这是因为它们都使用了迭代器来遍历HashMap,时间复杂度相同。for each keySet的性能较差,是因为需要再次调用get方法来获取值,增加了时间复杂度。 源码分析 通过查看HashMap的源码,可以看到entrySet()和keySet()...
要解决HashMap遍历删除元素的问题,可以使用Iterator来遍历HashMap,并使用Iterator的remove方法来删除元素。这样可以避免ConcurrentModificationException异常。 小结 在遍历和删除HashMap和List的元素时,需要...
- 遍历HashMap时修改HashMap(添加、删除元素)可能会导致`ConcurrentModificationException`,因为迭代器无法检测到这种并发修改。 - 使用`keySet()`遍历并删除元素是安全的,但效率较低,因为这涉及到两次查找:...
通过entrySet()方法,我们可以遍历HashMap中的每个键值对(Entry)。这种方式不仅可以遍历到所有的键,还可以在单次遍历中直接获得与键相对应的值。对于需要同时处理键和值的场景,entrySet()是最为高效的方法。 **...
- 遍历HashMap时,由于内部实现细节,顺序可能不稳定,因为哈希函数可能会导致元素在数组中的顺序发生变化。如果需要稳定的遍历顺序,可以考虑使用`LinkedHashMap`,它在保持效率的同时,按照插入顺序或访问顺序遍历...
#### 遍历HashMap的方法 根据提供的内容,我们可以了解到遍历`HashMap`主要有两种方式:使用`keySet()`方法和使用`entrySet()`方法。 1. **使用keySet()方法** ```java Map map = new HashMap(); Iterator ...
在上述的`HashMapTest`类中,通过对比`keySet()`和`entrySet()`遍历HashMap的时间,我们可以看到`entrySet()`方法通常比`keySet()`方法更快。`keySet()`需要两次遍历:一次是转换为迭代器,另一次是从HashMap中根据...
6. **遍历HashMap**:有两种方式遍历HashMap,一是通过`entrySet()`获取键值对的迭代器,二是通过`keySet()`获取键的迭代器再获取对应的值。 ```java for (Map.Entry, String> entry : map.entrySet()) { System....
8. **迭代器**:HashMap提供了迭代器`keySet()`、`values()`和`entrySet()`,分别用于获取键集合、值集合和键值对集合的迭代器,方便遍历HashMap的所有元素。 9. ** equals() 和 hashCode()**:插入HashMap的键对象...
1、遍历Map.entrySet():它的每一个元素都是Map.Entry对象,这个对象中, 放着的就是Map中的某一对key-value; 2、遍历Map.keySet():它是Map中key值的集合,我们可以通过遍历这个集合来 读取Map中的元素; 3、...
这种方法相对于遍历`entrySet`来说性能更好(大约快10%),并且代码更简洁。但需要注意的是,这种方法只适用于只需要键或值的情况。 #### 方法三:使用Iterator遍历 在某些情况下,可能需要在遍历过程中修改`Map`...
2. **遍历HashMap** 使用`entrySet()`或`keySet()`的方式同样适用于遍历`HashMap`。 ```java HashMap, Object> hash = new HashMap(); hash.put(3, 3); hash.put(4, 4); hash.put(5, 5); hash.put(6, 6); ...