collection工具箱几乎为每一个集合类都提供了迭代器iterator供使用者对数据结构进行迭代访问;jdk 8下的用法如下:
//keyset iterator
Iterator keySelfIterator=map.keySet().iterator();
while (keySelfIterator.hasNext())
{
Integer key=(Integer)keySelfIterator.next();
String value=map.get(key);
}
//entryset iterator
Iterator entrysetIterator=map.entrySet().iterator();
while (entrysetIterator.hasNext())
{
Integer key=(Integer)entrysetIterator.next();
String value=map.get(key);
}
这样就可以顺序访问元素,为什么在这里要强调下jdk的版本呢,简单介绍下,在上古时代你是可以这样用的,
for(Map.Entry<Integer, String> entry : map.entrySet()){
Integer key = entry.getKey();
if(key % 2 == 0){
System.out.println("To delete key " + key);
map.remove(key);
System.out.println("The key " + + key + " was deleted");
}
}
这里你会得到无情的报错ConcurrentModificationException;大家也很容易想到原因,map.remove元素的变化导致了异常,那么是什么样的原因导致了这个呢?本着给博客凑字数的心态我去查了其他博客和源码,
hashmap的remove
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
hashmap.hashiterator的remove方法
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
不知道有没有感受到赤果果的嘲讽,关键处expectedModCount = modCount;这里每次remove时,或者对map做put等操作时,mod跟expectedModCount都被做处理了,而hashmap并没有。
我们继续讲,那么keyset跟entryset用哪个好呢,以我目前的知识储备只能告诉你,用entryset,entryset,entryset.因为速度相差的实在是有点多;
public static void main(String[] args) {
Map map = new HashMap();
initMap(map);
keySetTravel(map);
entrySetTravel(map);
}
private static void initMap(Map map) {
for (int i = 0; i < 1000; i++) {
map.put(i, new Point(0, i));
}
}
public static void keySetTravel(Map map) {
long startTime = System.currentTimeMillis();
Set keySet = map.keySet();
for (Integer key : keySet) {
System.out.println(key + ":" + map.get(key));
}
System.out.println("keySetTravel:" + (System.currentTimeMillis() - startTime));
}
有兴趣的可以跑跑,简单的很, 为什么差别这么大呢?这两种遍历方式代码几乎完全雷同,只是在获取Value对象的时候不一样。entrySet遍历方式获取Value对象是直接从Entry对象中直接获得,时间复杂度T(n)=o(1);keySet遍历获取Value对象则要从Map中重新获取,keySet遍历Map方式比entrySet遍历Map方式多了一次循环,多遍历了一次table,当Map的size越大时,遍历的效率差别就越大。