大家好,
请问有人能够告诉我使用keySet()和get()方法之间的性能差异吗?网站:CodeRanch提供了一个简要概述,介绍了在使用这两种方法时所需的内部调用。但如果有人能够提供关于使用keySet()和get()方法时流程的确切细节,那就太好了。这将帮助我更好地理解性能问题。
大家好,
请问有人能够告诉我使用keySet()和get()方法之间的性能差异吗?网站:CodeRanch提供了一个简要概述,介绍了在使用这两种方法时所需的内部调用。但如果有人能够提供关于使用keySet()和get()方法时流程的确切细节,那就太好了。这将帮助我更好地理解性能问题。
使用entrySet比keySet更好的最常见的情况是,当您要遍历Map中的所有键值对时。
这样更有效率:
for (Map.Entry entry : map.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
}
比:
for (Object key : map.keySet()) {
Object value = map.get(key);
}
第二种情况下,对于keySet中的每个键,都需要调用map.get()
方法。在HashMap的情况下,这要求评估key对象的hashCode()
和equals()
方法以查找关联的值。而在第一种情况下,这个额外的工作是被省略掉了。
编辑:如果考虑到TreeMap,情况甚至更糟。在TreeMap中,调用get方法的时间复杂度是O(log(n)),也就是说比较器可能需要运行log2(n)次(n为Map的大小)才能找到关联的值。
有些Map实现在调用hashCode()
和equals()
之前会进行内部优化来检查对象的身份。
Map
类型。但由于JavaRanch线程谈论了HashMap
,我假设您指的是该实现。同时假设您也在谈论来自Sun/Oracle的标准API实现。LinkedHashMap
。从文档中可以得知:
迭代
LinkedHashMap
的集合视图需要与地图大小成比例的时间,而不管其容量如何。迭代HashMap
可能更昂贵,需要与其容量成比例的时间。
此实现的源代码可用。实现基本上只返回一个新的HashMap.EntrySet
。一个类看起来像这样:
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator(); // returns a HashIterator...
}
// ...
}
一个 HashIterator
的外观如下所示
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry<K,V> current; // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null);
}
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null);
}
current = e;
return e;
}
// ...
}
所以,这就是在迭代entrySet
时会发生什么的代码。它遍历整个数组,长度与映射的容量相同。
首先需要获取键的集合。这需要花费与映射的容量成比例的时间(而不是LinkedHashMap
的大小)。完成此操作后,您需要为每个键调用get()
。当然,在平均情况下,使用良好的hashCode实现可以使其具有恒定的时间复杂度。但是,它不可避免地需要大量的hashCode()
和equals()
调用,这显然比执行entry.value()
调用需要更长的时间。
entrySet()
, keySet()
, 和 values()
三种方法并提供了相关建议,适用场景不同使用时需要注意。
显然,只要您不需要通过Map.get()
方法获取值,使用keySet()
会更快(除了更方便)。
get()
操作是一个O(log(n))
的操作。 - ILMTitan