`

keySet 与entrySet 遍历HashMap性能差别

阅读更多
keySet 与entrySet 遍历HashMap性能差别
博客分类: Java

一.问题发现
今天,在写完代码后用Find Bugs扫锚了一下,发现类中一处代码中有提示如下内容:
        Java代码 
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);  
         } 

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.第一种方式
Java代码 
Iterator<String> keySetIterator = keySetMap.keySet().iterator();  
        while (keySetIterator.hasNext()) {  
            String key = keySetIterator.next();  
            String value = keySetMap.get(key);  
              
        } 

Iterator<String> keySetIterator = keySetMap.keySet().iterator();
  while (keySetIterator.hasNext()) {
   String key = keySetIterator.next();
   String value = keySetMap.get(key);
  
  }


2.第二种方式
Java代码 
Iterator<Entry<String, String>> entryKeyIterator = entrySetMap.entrySet()  
                .iterator();  
        while (entryKeyIterator.hasNext()) {  
            Entry<String, String> e = entryKeyIterator.next();  
            String value=e.getValue();  
        } 

Iterator<Entry<String, String>> entryKeyIterator = entrySetMap.entrySet()
    .iterator();
  while (entryKeyIterator.hasNext()) {
   Entry<String, String> e = entryKeyIterator.next();
   String value=e.getValue();
  }

三.性能比较

    到底第二种方式的性能比第一种方式的性能高多少呢,通过一个简单的测试类可以看一下,测试代码如下:
Java代码 
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));  
 
    }  


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值.
Java代码 
private class KeyIterator extends HashIterator<K> {  
       public K next() {  
           return nextEntry().getKey();  
       }  
   } 

private class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }
而调用entrySetMap.entrySet()方法会生成EntryIterator 迭代器,其next方法返回一个Entry对象的一个实例,其中包含key和value.
Java代码 
private class EntryIterator extends HashIterator<Map.Entry<K,V>> {  
       public Map.Entry<K,V> next() {  
           return nextEntry();  
       }  


private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
        public Map.Entry<K,V> next() {
            return nextEntry();
        }
  }

二者在此时的性能应该是相同的,但方式一再取得key所对应的value时,此时还要访问Map的这个方法,这时,方式一多遍历了一次table.

Java代码 
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;  
        }  
    } 

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

这个方法就是二者性能差别的主要原因.

分享到:
评论

相关推荐

    Java5种遍历HashMap数据的写法

    除了使用EntrySet的迭代器,我们还可以使用KeySet的迭代器来遍历HashMap中的数据。其实现代码如下: ```java Iterator&lt;Integer&gt; iterator = coursesMap.keySet().iterator(); while (iterator.hasNext()) { Integer...

    java遍历HashMap简单的方法

    遍历HashMap是开发者经常遇到的任务,尤其在处理数据操作时。本篇文章将详细介绍如何使用简单的方法来遍历HashMap,通过实例代码帮助理解。 HashMap遍历的常用方法主要有三种:迭代器(Iterator)遍历、键集...

    基于HashMap遍历和使用方法(详解)

    3. 通过Map.entrySet遍历key和value 这是一种使用增强for循环遍历键值对的方法。 ```java for (Map.Entry, String&gt; entry : map.entrySet()) { System.out.println("key= " + entry.getKey() + " and value= " + ...

    使用多种方式实现遍历HashMap的方法

    遍历HashMap是常见的操作,本文将介绍六种不同的方法来实现这一功能。 1. **方式一:使用KeySet方法** KeySet方法返回HashMap中所有键的Set视图。由于Set接口实现了Iterable接口,我们可以使用for-each循环来遍历...

    HashMap遍历

    本文将深入探讨`HashMap`的遍历方法,包括`keySet()`和`entrySet()`两种主要方式,并通过代码示例对比它们的性能差异。 #### 方法一:使用`keySet()`遍历 `keySet()`方法返回`HashMap`中的所有键的集合视图。通过...

    怎样遍历一个HashMap?

    可以通过2种方法遍历HashMap &lt;br&gt;Map map = new HashMap(); &lt;br&gt;for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { &lt;br&gt; Map.Entry entry = (Map.Entry) iter.next(); &lt;br&gt; Object ...

    Java HashMap三种循环遍历方式及其性能对比实例分析

    这是因为它们都使用了迭代器来遍历HashMap,时间复杂度相同。for each keySet的性能较差,是因为需要再次调用get方法来获取值,增加了时间复杂度。 源码分析 通过查看HashMap的源码,可以看到entrySet()和keySet()...

    HashMap和List遍历方法及如何遍历删除元素总结

    要解决HashMap遍历删除元素的问题,可以使用Iterator来遍历HashMap,并使用Iterator的remove方法来删除元素。这样可以避免ConcurrentModificationException异常。 小结 在遍历和删除HashMap和List的元素时,需要...

    Java 实例 - HashMap遍历源代码-详细教程.zip

    - 遍历HashMap时修改HashMap(添加、删除元素)可能会导致`ConcurrentModificationException`,因为迭代器无法检测到这种并发修改。 - 使用`keySet()`遍历并删除元素是安全的,但效率较低,因为这涉及到两次查找:...

    java哈希遍历_哈希遍历_

    - 遍历HashMap时,由于内部实现细节,顺序可能不稳定,因为哈希函数可能会导致元素在数组中的顺序发生变化。如果需要稳定的遍历顺序,可以考虑使用`LinkedHashMap`,它在保持效率的同时,按照插入顺序或访问顺序遍历...

    如何得到hashmap的索引

    #### 遍历HashMap的方法 根据提供的内容,我们可以了解到遍历`HashMap`主要有两种方式:使用`keySet()`方法和使用`entrySet()`方法。 1. **使用keySet()方法** ```java Map map = new HashMap(); Iterator ...

    JAVA遍历Map所有元素.doc

    在上述的`HashMapTest`类中,通过对比`keySet()`和`entrySet()`遍历HashMap的时间,我们可以看到`entrySet()`方法通常比`keySet()`方法更快。`keySet()`需要两次遍历:一次是转换为迭代器,另一次是从HashMap中根据...

    Java HashMap的三种遍历方法及优缺点含示例

    entrySet()方法在遍历过程中提供键值对的完整信息,适用于需要同时处理键和值的情况;keySet()方法适用于只关心键而不关心键对应的值的情况,而values()方法则适用于只关心值,不需要键的情况。 在实际开发中,...

    hashmap使用实例

    6. **遍历HashMap**:有两种方式遍历HashMap,一是通过`entrySet()`获取键值对的迭代器,二是通过`keySet()`获取键的迭代器再获取对应的值。 ```java for (Map.Entry, String&gt; entry : map.entrySet()) { System....

    HashMap的数据结构

    8. **迭代器**:HashMap提供了迭代器`keySet()`、`values()`和`entrySet()`,分别用于获取键集合、值集合和键值对集合的迭代器,方便遍历HashMap的所有元素。 9. ** equals() 和 hashCode()**:插入HashMap的键对象...

    java中Map集合的常用遍历方法及HashMap的应用实例

    1、遍历Map.entrySet():它的每一个元素都是Map.Entry对象,这个对象中, 放着的就是Map中的某一对key-value; 2、遍历Map.keySet():它是Map中key值的集合,我们可以通过遍历这个集合来 读取Map中的元素; 3、...

    map遍历的四种方式及性能比较

    这种方法相对于遍历`entrySet`来说性能更好(大约快10%),并且代码更简洁。但需要注意的是,这种方法只适用于只需要键或值的情况。 #### 方法三:使用Iterator遍历 在某些情况下,可能需要在遍历过程中修改`Map`...

    java Map 遍历方法

    2. **遍历HashMap** 使用`entrySet()`或`keySet()`的方式同样适用于遍历`HashMap`。 ```java HashMap, Object&gt; hash = new HashMap(); hash.put(3, 3); hash.put(4, 4); hash.put(5, 5); hash.put(6, 6); ...

Global site tag (gtag.js) - Google Analytics