Java Map遍历方法(Map的Iterator原理)

        Map中存放数据的Key-Value实质上就是Node节点,而 底层是hash数组和链表(或树),不容易遍历。

一、containsKey() 和 get()查找元素

   这两个方法可以获得信息,但是依旧不能轻松遍历。
        containsKey()方法判断对应的key是否存在;
        get()方法得到对应key的键值对,依旧不是遍历。
1、containsKey() 和 get() 源码:
        可见二者 都是通过getNode()方法获取对应的结果。
        
2、getNode()方法:
            tab 是 table(哈希表)    first 哈希表找的链表或红黑树对应的头结点    e 代表当前节点    k 代表当前的 key
            第一个if,将哈希表空的、长度不够的、对应位置没存数据的 都过滤掉;
            第二个if,头结点就找到了 hash相等值相等 且 不空的 key或和当前节点 equals相等的节点,直接返回
            第三个if,上一个if头结点不匹配,没找到,就用 next 找
            第四个if,是红黑树就用红黑树的方法找
            第五个if,不是红黑树,就是链表,则找next即可。
            

二、keySet()、values()、entrySet()遍历

        上述 containsKey() 和 get()方法虽然获得了部分信息,但不容易遍历。
为了方便遍历,提供了以下几个方法:
    keySet()、values()、entrySet()用来方便遍历:
    可见三个方法都是新建一个对应的类的对象,然后返回该对象。这三个类都是HashMap的内部类。
①接下来介绍这三个类(KetSet、Values、EntrySet):
    由下面源码可知,这三个类中
        KeySet和EntrySet继承了AbstractSet类
        Values继承了AbstractCollection类
        而类AbstractSet继承了AbstractCollection类,AbstractCollection类又实现了Collection接口。
KeySet类:
    
Values类:
    
EntrySet类:
    
②介绍这三个类的父类:AbstractSet类和 AbstractCollection类(同时解释如何遍历):
    可见, AbstractSet类没有构造方法,而其父类AbstractCollection类构造方法如下,是一个空构造方法。
    因此,下面三个类的父类都没有属性和构造函数,其本类也没有属性和构造函数,所以这些类都没有属性,都是 没有内容的"空对象"
    
    
那为什么输出三个类都可以 sout输出 以及为什么可以通过下面三个类的对象 进行遍历 呢?
a.可以输出:
    相当于三个类都继承了AbstractCollection类,有两个是隔代继承,而 AbstractCollection类重写了toString()方法。
    toString()方法都是调用Iterator()方法,然后进行遍历。
    可是这是map,不是数组,理应没有 Iterator,因此肯定重写了:
        而在AbstractCollection类,该方法为空:
        
    
意味着子类重写了该方法( Iterator()方法 )
以子类HashMap为例:
    由上面可知,HashMap的内部类KetSet、Values、EntrySet三者,都继承了AbstractCollection类,因此子类中必定重写了该方法:
    三个类的源码在上面,在此处截取片段:
        可见三个方法都实现了Iterator()的重写,
        都是new一个对应的类,且对应的类都是HashMap的内部类:
            
        KeySet -> KeyIterator
        Values -> ValueIterator
        EntrySet -> EntryIterator
    
    
    
由下图可知,三个类都继承了HashIterator类:
    且其next()方法最后都是返回nextNode()的值
    
HashIterator类:
    由下面源码可知,该类主要有两个方法,nextNode()和remove()方法
        nextNode()使得其正确指向下一个节点。
    
nextNode():
        可见,由于加进来的顺序是通过hash进行确定的,而遍历却是依次遍历table的每一个链表(树),
    所以取出来的顺序和加入的顺序大概率不相同。
    
    加入顺序:
    
    map中table:
    
    set中顺序(即去除==取出顺序):
        因为其是按顺序依次遍历table的每一个链表(树),所以其取出顺序应与table表中顺序一致
    
由此解决了  KeySet、EntrySet和 Values三个类为何能正常输出:
    首先, KeySet、EntrySet和Values间接或直接 继承了AbstractCollection类
-> AbstractCollection类重写了toString()方法
-> toString()方法调用Iterator()
->动态绑定到具体实现类,即HashMap的内部类:KeySet、Values、EntrySet
->三个类的Iterator()方法返回对应的三个类的对象:KeyIterator、ValueIterator、EntryIterator
->调用三个类的next()方法,其实是调用父类的nextNode()方法
->这三个类都继承自同一个类:HashIterator类
->该类主要有两个方法,nextNode()和remove()方法
b.可以遍历:
    可以遍历原因跟上面一样。
    
  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值