您的位置:首页 > 其它

keySet 与entrySet 遍历HashMap性能差别

2015-07-20 15:22 381 查看
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;

}

}

这个方法就是二者性能差别的主要原因.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: