Java Map的遍历方式 - 每天五分钟搞定Java面试

本文介绍了四种遍历Map的方法:entrySet(), keySet()与values(), Iterator, 以及 Java 8 中新增的 forEach()方法,并从源码层面分析了这些方法的性能差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本篇文章首先讲一下Map的几种遍历写法,然后从源码角度比较不同写法的性能差别。

方法一 entrySet()

public static void func1(Map<Integer,Integer> map) {
        for(Map.Entry<Integer,Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        }
    }

大多情况下采用上面这种方式,因为通过遍历Entry,可以同时获取键值。

方法二 keySet()、values()

public static void func2(Map<Integer,Integer> map) {
        for (Integer key : map.keySet()){
            System.out.println(key);
            System.out.println(map.get(key));
        }
    }

keySet()方法是对Map的key值进行遍历,我们可以通过找到的key值,使用get(key)方法获取对应的value。也可以使用values()方法来获取所有的value,然后遍历value。具体代码如下:

public static void func3(Map<Integer,Integer> map) {
        for(Integer value : map.values()) {
            System.out.println(value);
        }
    }

方法三 Iterator

public static void fun4(Map<Integer,Integer> map) {
        Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
        while(entries.hasNext()){
            Map.Entry<Integer, Integer> entry = entries.next();
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        }
    }

这种方法与第一种方法相比,显得有些冗余。多设置一个Iterator变量,然后通过它来进行遍历。但是这种写法有一个好处,就是在遍历的过程中可以对Iterator进行remove的操作,删除调用next()方法返回的元素。这种操作在其他两种方法中是不可行的。for-each下是不可以对集合元素进行删除操作的。不然会报错。

public static void main(String[] args){
        Map<Integer,Integer> map = new HashMap<>();
        map.put(1,1);
        map.put(2,2);
        map.put(3,3);
        map.put(4,4);
        //func1(map);
        func4(map);
    }
public static void func4(Map<Integer,Integer> map) {
        System.out.println(map.size()); //4
        Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
        Map.Entry<Integer, Integer> entry = entries.next(); //1->1
        entries.remove();
        System.out.println(map.size());//3
        for(Integer value : map.values()) {
            System.out.println(value); // 2,3,4
        }
        System.out.println(entry.getKey()); //1
        }

方法四

Java8中,Map接口中提供了默认方法 forEach,可以用于map的遍历。关于Java8的语言新特性-接口默认方法可以参见前面的文章。

public static void func5(Map<Integer,Integer> map) {
        map.forEach((k,v)->{
            System.out.println(k);
            System.out.println(v);
        });
    }

从源码可以看出Map.forEach()方法本质上还是entrySet()方法

default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }

下面我们从HashMap的源码分析三种遍历方式的性能

entrySet()

HashMap中entrySet()方法如下,重写Map中的entrySet()方法

public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es;
        return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
    }
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator();
        }
        public final boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Node<K,V> candidate = getNode(hash(key), key);
            return candidate != null && candidate.equals(e);
        }
        public final boolean remove(Object o) {
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
                Object key = e.getKey();
                Object value = e.getValue();
                return removeNode(hash(key), key, value, true, true) != null;
            }
            return false;
        }
        public final Spliterator<Map.Entry<K,V>> spliterator() {
            return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

keySet()

public Set<K> keySet() {
        Set<K> ks;
        return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
    }

    final class KeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<K> iterator()     { return new KeyIterator(); }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super K> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.key);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

上面的方法二使用keySet()方法得到key的所有值,然后再使用get(key)获取对应的value,这样的方式比entrySet()方法一次性获取key和value耗时要增加一倍,相当于遍历两次map集合,一次遍历获得key,一次遍历获得value。

总结

JDK8之前,可以使用keySet或者entrySet来遍历HashMap,JDK8中引入了map.foreach来进行遍历。
原因:keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value。而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高。如果是JDK8,使用Map.foreach方法。

参考:https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/iterate-through-a-hashmap.md
http://www.importnew.com/26298.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值