学习笔记-Map集合的遍历

Map集合的两种遍历方式

通过查看Map集合的API发现没有iterator方法,那么双列集合如何迭代呢?
一、Map集合的遍历之键找值
基本思路:
* 先获取所有键的集合
* 遍历键的集合,获取到每一个键
* 根据键找值
注意--这里我们查看API可以发现Map接口中有一个方法为:
Set<K> keySet():获取集合中所有键的集合
可以看到keySet方法返回了一个Set集合,而Set集合中有迭代器iterator,这样就可以运用Set集合的特性来达到对Map集合的遍历
示例
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Map_Iterator {
	public static void main(String[] args) {
		Map<String, Integer> map = new HashMap<>();
		map.put("张三", 23);
		map.put("李四", 24);
		map.put("王五", 25);
		map.put("赵六", 26);

		Set<String> keySet = map.keySet(); 		   // 获取所有键的集合
		Iterator<String> it = keySet.iterator();   // 获取迭代器
		while (it.hasNext()) { 					   // 判断集合中是否有元素
			String key = it.next();  			   // 获取每一个键
			Integer value = map.get(key); 		   // 根据键获取值
			System.out.println(key + "=" + value);
		}

	}

}
上面也可以使用增强for循环遍历
import java.util.HashMap;
import java.util.Map;

public class Map_Iterator {
	public static void main(String[] args) {
		Map<String, Integer> map = new HashMap<>();
		map.put("张三", 23);
		map.put("李四", 24);
		map.put("王五", 25);
		map.put("赵六", 26);

		for(String key : map.keySet()) {			//map.keySet()是所有键的集合
			System.out.println(key + "=" + map.get(key));
			//这里的map.get方法是HashMap中的get
		}

	}

}
二、Map集合的遍历之键值对对象找键和值
键值对对象找键和值的思路:
* 获取所有键值对对象的集合
* 遍历键值对对象的集合,获取到每一个键值对对象
* 根据键值对对象找键和值
注意--这里我们查看API可以发现Map接口中有一个方法为:
Set<Map.Entry<K,V>> entrySet():返回此映射中包含的映射关系的Set视图
Map.Entry(注意这个.)说明Entry是Map的内部接口,将键和值封装成了Entry对象,并存储在Set集合中
示例
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Map_Iterator2{
	public static void main(String[] args) {
		Map<String, Integer> map = new HashMap<>();
		map.put("张三", 23);
		map.put("李四", 24);
		map.put("王五", 25);
		map.put("赵六", 26);
		
		//Map.Entry说明Entry是Map的内部接口,将键和值封装成了Entry对象,并存储在Set集合中
		Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
		//获取每一个对象
		Iterator<Map.Entry<String, Integer>> it = entrySet.iterator();
		while(it.hasNext()) {
			//获取每一个Entry对象
			Map.Entry<String, Integer> en = it.next();	//父类引用指向子类对象					
			//Entry<String, Integer> en = it.next();	//这样写等同于上面,这里的Entry也是Map中的Entry接口
			String key = en.getKey();					//根据键值对对象获取键
			Integer value = en.getValue();				//根据键值对对象获取值
			System.out.println(key + "=" + value);
		}
		
	}

}
产生的疑问:?
上面代码中Map.Entry<String, Integer> en = it.next();	是说父类引用指向子类对象,为什么?

接下来我们来分析一下上述程序的具体实现过程:
先看一下HashMap的底层的一些变量:

      transient Node<K,V>[] table;        //存储数据的Node数组
      
      transient Set<java.util.Map.Entry<K,V>> entrySet;
      
      transient int size;          //map中存放数据的个数,不等于table.length
      
      transient int modCount;         //修改的次数,防止
      
      int threshold;            //临界值
      
      final float loadFactor;        //扩展因子,一般情况下threshold=table.length*loadFactor;
      
//构造一个空的HashMap时,只有loadFactor被赋值为默认的0.75。代码如下:
       public HashMapMmc(){
          this.loadFactor=DEFAULT_LOAD_FACTOR;
       }

第一步Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
上面这一步map调用的entrySet() 方法其实是HashMap中重写了Map接口entrySet() 方法的entrySet().源码如下:

 transient Set<Map.Entry<K,V>> 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>> {
	...

从源码可以看出这个临时变量entrySet是等于null的,也就是说每次都是new EntrySet();接下来看下面的EntrySet类:

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

程序走到了:Iterator<Map.Entry<String, Integer>> it = entrySet.iterator();
而我们可以从源码看到,EntrySet类中重写了iterator方法。返回的是一个new EntryIterator(),接下来看EntryIterator

 final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { 
        return nextNode(); 
        }
 }

可以看到EntryIterator类继承了HashIterator,而且EntryIterator中有一个next()方法,返回一个nextNode(); 这个返回值其实是在HashIterator中;再看它的源码:

    transient Node<K,V>[] table;
    transient int modCount;
    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的变量,这个变量是在table数组中取得,索引是从0递增的,即先入先出原则。构造初期会从0开始找有值的索引位置,找到后将这个Node赋值给next;然后要遍历的时候是调用nextNode()方法,这个方法是先判断next.next是否为空,如果为空继续往上找有值的索引位置,如果不为空就找next.next。这样就能都遍历出来了,是从索引0table.length去一个个寻找遍历的。

此时程序经历了while(it.hasNext()) { Map.Entry<String, Integer> en = it.next();已经遍历完了HashMap集合;这里的 it.next() 返回了 nextNode(); 方法,而nextNode()返回了一个e;Node<K,V> e = next;NodeHashMap集合的一个静态内部类,它实现了Map.Entry接口;
源码如下:

    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

所以程序中String key = en.getKey(); Integer value = en.getValue();调用的就是Node类中的方法;所以就可以说Map.Entry<String, Integer> en = it.next(); 是父类引用指向子类对象.

上述遍历代码也可以用增强for循环实现

代码如下:

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

public class Map_Iterator2{
	public static void main(String[] args) {
		Map<String, Integer> map = new HashMap<>();
		map.put("张三", 23);
		map.put("李四", 24);
		map.put("王五", 25);
		map.put("赵六", 26);

		for (Map.Entry<String, Integer> en : map.entrySet()) {
			System.out.println(en.getKey() + "=" + en.getValue());
		}
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值