【数据结构和算法】HashMap之深入理解keySet、values、entrySet

  • Post author:
  • Post category:其他




深入理解keySet、values、entrySet

HashMap遍历的方式

    Map<String,String> map = new HashMap<>();
    //1、for循环遍历key
    System.out.println("通过Map.keySet遍历key和value:");
    for (String key: map.keySet()){
        System.out.println("key:"+key+" value:"+map.get(key));
    }
    //2、for循环遍历value
    System.out.println("通过Map.values()遍历所有的value,但ey");
    for(Object v:map.values())
    {
        System.out.println("value:"+v);
    }
    //3、for循环遍历entrySet
    System.out.println("通过Map.entrySet遍历key和value");
    for(Map.Entry<String, String> entry: map.entrySet())
    {
        System.out.println("Key: "+ entry.getKey()+ " Va"+entry.getValue());
    }
    //4、iterator
    System.out.println("通过Map.entrySet使用iterator遍历key和va");
    Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
    while(iterator.hasNext()){
        Map.Entry<String,String> entry = iterator.next();
        System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
    }

在遍历HashMap的时候:

    Set<String> setStrings = map.keySet()
    for (String key: setStrings){
        System.out.println("key:"+key+" value:"+map.get(key));
    }

但是keySet()是如何获取到HashMap的所有的key呢?

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

其中keySet、values是AbstractMap中声明的变量:

    transient Set<K>        keySet;
    transient Collection<V> values;

调用map.keySet()时,会判断ks是否为空,然后调用new KeySet(),keySet()方法返回一个引用,这个引用指向了HashMap的一个内部类KeySet类:

 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();
            }
        }
    }

此内部类继承了AbstractSet,此内部类初始化迭代器产生一个迭代器对象KeyIterator:

 final class KeyIterator extends HashIterator
        implements Iterator<K> {
        public final K next() { return nextNode().key; }
    }

在使用增强for循环遍历map.keySet()时,就是调用的KeyIterator.next()方法。

KeyIterator类继承了HashIterator迭代器:

abstract class HashIterator {
        Node<K,V> next;        // next entry to return
        Node<K,V> current;     // current entry
        int expectedModCount;  // for fast-fail
        int index;             // current slot

        HashIterator() {
            expectedModCount = modCount;
            Node<K,V>[] t = table;
            current = next = null;
            index = 0;
            if (t != null && size > 0) { // advance to first entry
                do {} while (index < t.length && (next = t[index++]) == null);
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Node<K,V> nextNode() {
            Node<K,V>[] t;
            Node<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }

        public final void remove() {
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            K key = p.key;
            removeNode(hash(key), key, null, false, false);
            expectedModCount = modCount;
        }
    }

HashIterator迭代器初始化拿到了next指向map中的第一个元素。当使用keySet集合遍历key时,其实是使用迭代器KeyIterator迭代每个节点的key。


问题:


在使用增强for循环遍历map.keySet()时,为什么调用的KeyIterator.next()?

对使用增强for循环反编译发现:

    Set<String> keys = map.keySet();
    Iterator iterator = keys.iterator()
    while(iterator.hasNext()) {
        String key = (String)iterator.next();
        System.out.println("key:" + key + " value:" (String)map.get(key));
    }

增强for循环根本还是iterator()

而KeySet定义的iterator()返回值为new KeyIterator(),因此在使用增强for循环遍历map.keySet()时,调用的是KeyIterator.next()。

总结:

  1. keySet和values和entrySet本质既然一样,就可以通过封装其相同的部分(也就是这里的HashIterator),再各自实现最重要的next方法。
  2. keySet()方法返回一个内部引用,并指向一个内部类对象,该内部类重写了迭代器方法,当在增强for循环时才调用,并从外部类的table中取值。



版权声明:本文为u011151359原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。