首先列举几种HashMap的遍历方法
1.迭代器entrySet
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
}
2.迭代器keySet
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object val = map.get(key);
}
3.for循环entrySet
Map<String, String> map = new HashMap<String, String>();
for (Entry<String, String> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
4.for选好keySet
Map<String, String> map = new HashMap<String, String>();
for (String key : map.keySet()) {
map.get(key);
}
这样写是想凸显出遍历过程中entrySet和keySet的地位,以便后面分析不同的遍历方法之间性能差异。从上面的例子我们发现无论是for循环还是迭代器遍历,时间复杂度都是o(n).换句话说具体使用for循环还是迭代器影响不大。具体的时间复杂度与使用的是keySet还是entrySet以及我们使用的是key还是value有关。
从代码上看entrySet的执行步骤可以分为,取entrySet,取entry,取key或者Value,keySet的执行步骤可以分为取keySet,取key,取value。这样对比就不难发现如果我们只是使用key,那么keySet做遍历,执行的步骤更少;如果取value,那么entrySet的执行步骤更少。
实际上keySet取value的需要通过Map的get方法获取value,要先计算key的hash值,然后再通过hash值取value,这个我们可以通过HashMap的源码看出来。
/**
* Gets the value mapped to the key specified.
*
* @param key the key
* @return the mapped value, null if no match
*/
public Object get(Object key) {
purgeBeforeRead();
Entry entry = getEntry(key);
if (entry == null) {
return null;
}
return entry.getValue();
}
/**
* Gets the entry mapped to the key specified.
* <p>
* This method exists for subclasses that may need to perform a multi-step
* process accessing the entry. The public methods in this class don't use this
* method to gain a small performance boost.
*
* @param key the key
* @return the entry, null if no match
*/
protected HashEntry getEntry(Object key) {
key = convertKey(key);
int hashCode = hash(key);
HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index
while (entry != null) {
if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
return entry;
}
entry = entry.next;
}
return null;
}
总结一下:凡是之间问性能优劣的都是耍流氓,凡是之间回答性能优劣的都是耍臭流氓。除了很明显的差异外,性能无疑不与使用场景密切相关。