HashMap的keySet()和entrySet()实现原理

借鉴两篇文章:

https://www.cnblogs.com/dsj2016/p/5551059.html

https://blog.csdn.net/u013370551/article/details/77113154

keySet()  entrySet() 可以获得hashmap的key集合和键值对集合。keySet是父类AbstractMap的属性,entrySet是本身的属性,最开始以为是在put方法放元素时 往两个set集合放元素,却发现根本没有,那到底是怎么获得key和entry有哪些元素呢?

keySet()方法返回一个引用,这个引用指向了HashMap的一个内部类KeySet类,此内部类继承了AbstractSet,此内部类初始化迭代器产生一个迭代器对象KeyIterator,它继承了HashIterator迭代器,HashIterator迭代器初始化拿到了next指向map中的第一个元素。当使用keySet集合遍历key时,其实是使用迭代器KeyIterator迭代每个节点的key。

entrySet()方法也是这个道理,values()方法也是

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

 

    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);//拿到next
            }
        }

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

 

以下转载:

 

首先看keySet()的使用:

        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("a", 1);
        map.put("b", 2);
        map.put("c", 3);
        Set<String> ks = map.keySet();
        for (String s: ks){
            System.out.println(s);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

再看HashMap中keySet()方法:

    public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }
  • 1
  • 2
  • 3
  • 4

而且keySet成员初始为null,且并没有在构造函数中初始化过

transient volatile Set<K> keySet = null;
  • 1

所以初次调用keySet()方法时会new KeySet(),而KeySet()是一个内部类

    private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return newKeyIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这个时候我们只是新建了一个KeySet 内部类对象,并没有调用其他方法,而且内部类KeySet 的父类无参构造函数也并没有做啥,那么问题来了,我们是怎么获取的HashMap中的key值的,之前对这个问题一直没明白。 
其实,Set ks = map.keySet(); 这里的 ks 就仅仅只是一个Set引用,指向HashMap内部类KeySet的一个实例,重点在于该实例拥有自己的迭代器,当我们在使用增强for循环时才会调用该迭代器,也才会输出我们想要的东西。 
现在看看迭代器是如何工作的:

private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return newKeyIterator();
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5

newKeyIterator()是指向外部类的一个函数

Iterator<K> newKeyIterator()   {
    return new KeyIterator();
}
  • 1
  • 2
  • 3

现在又指向创建的另一个内部类对象KeyIterator(),该类继承HashIterator也是HashMap的内部类

    private final class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5

当我们在增强for循环时会调用该next()方法,它指向的是nextEntry().getKey(),Entry中不仅存放了key,value,也存放了next,指向下一个Entry对象,我们知道,HashMap的数据层实现是数组+链表,nextEntry会先遍历链表,然后再继续遍历下一个数组位置的链表,直至全部遍历完成,其部分源码如下:

if ((next = e.next) == null) {
    Entry[] t = table;
    while (index < t.length && (next = t[index++]) == null)
        ;
}
  • 1
  • 2
  • 3
  • 4
  • 5

总结:keySet()方法返回一个内部引用,并指向一个内部类对象,该内部类重写了迭代器方法,当在增强for循环时才调用,并从外部类的table中取值。

 

 

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值