Java集合(八)Map接口

我们来查看Map接口:

里面的k-v和set集合的不同之处在于:key仍然是一个对象,但是对于set来说value是一个常量,

private static final Object PRESENT = new Object();

set里面的value放的是 静态性质的PRESENT。

而map的value是自己传进去的。map接口是一个双列集合,set接口是一个单列集合。

Map接口实现类的特点:

(1)Map与Collection并列存在,用于保存具有映射关系的数据:Key-Value(双列元素)

(2)Map中的Key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中

我们发现table表是HashMap$Node这样子的一个数组。 

(3)Map中的key不允许重复,原因和hashSet一样。

(4)Map中的value可以重复

(5)Map的key可以为Null,value也可以为null,注意key为null,只能有一个,value为null,可以多个。

(6)常用string类作为Map的key,而只要object的子类都可以作为key。

(7)key和value之间存在单向一对一关系,即通过指定的Key总能找到对应的value. 

我们的代码设计如下所示:

package com.rgf.map;

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

@SuppressWarnings({"all"})
public class Map_ {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("no1","rgf");//k-v
        map.put("no2","张无忌");
        map.put("no1","张三丰");//当有相同的key值时,会替换掉上面的value
        map.put("no3","张三丰");
        map.put(null,null);
        map.put(null,"abc");//等价替换
        map.put("no4",null);//k-v
        map.put("not5",null);//k-v
        map.put(1,"赵敏");//k-v
        map.put(new Object(),"金毛狮王");//k-v
        //通过get方法,传入key,会返回对应的value
        System.out.println(map.get(1));
        System.out.println("map="+map);



    }
}

运行界面如下所示:

(8)Map存放数据的key-value示意图,一对k-v是放在一个HashMap$Node中,又因为Node实现了Entry接口,有些书上也说一对k-v就是一个Entry。(key放在set集合里面,values放在Collection接口下面的实现子类里面的)但是,真正的Key是放在HashMap$Node这个类型里面的。而我们的set和collection集合只是指向了他而已。就是简单的引用。

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

 其中newNode返回了hashMap里面的Node类型。

Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
        return new Node<>(hash, key, value, next);
    }

 Node为HashMap里面的静态内部类:

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

 其中Node也实现了Entry接口

Entry这个集合中包含2类,一类是Set型的KeySet,用来存放key值,另外一类是存放value值,然后他们里面各自的值其实存放的是地址,一个指向真正Node节点中的key和value的地址。而真正存放Key和value的值是HashMap$Node。

我们来通过源码进行较深入的理解:

1.k-v最后是存放在HashMap$Node node=new Node<>(hash, key, value, next)这个类型里面的 2.k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素的类型就是Entry, // 而一个Entry对象就包含了key,value,EntrySet<Entry<K,V>>,即:transient Set<Map.Entry<K,V>> entrySet() ,有一个EntrySet集合,EntrySet集合里面放的是Entry这样子的数据类型,Entry这样子的数据类型包含key和value;

3.entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node,Node实现了Map.Entry接口,这是因为HashMap$Node implements Map.Entrty,HashMap$Node实现了这个接口Map.Entrty。 当有一个类,实现了一个接口,这个类的对象实例就可以赋给我们的接口类型。

我们进行总结就是EntrySet内部类存储了node节点的引用,返回一个集合,方便遍历。 

我们进入如下源码所示:

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

 (4)这样当把HashMap$Node对象存放到entrySet方便我们的遍历,因为Map.Entry提供了两个非常重要的方法

interface Entry<K,V> {
        /**
         * Returns the key corresponding to this entry.
         *
         * @return the key corresponding to this entry
         * @throws IllegalStateException implementations may, but are not
         *         required to, throw this exception if the entry has been
         *         removed from the backing map.
         */
        K getKey();

        /**
         * Returns the value corresponding to this entry.  If the mapping
         * has been removed from the backing map (by the iterator's
         * <tt>remove</tt> operation), the results of this call are undefined.
         *
         * @return the value corresponding to this entry
         * @throws IllegalStateException implementations may, but are not
         *         required to, throw this exception if the entry has been
         *         removed from the backing map.
         */
        V getValue();

我们设计的代码如下所示:

package com.rgf.map;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@SuppressWarnings({"all"})
public class MapSource_ {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("no1","沃尔"); //k-v
        map.put("no2","张无忌"); //k-v
        //1.k-v最后是存放在HashMap$Node  node=new Node<>(hash, key, value, next)这个类型里面的
        //2.k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素的类型就是Entry,
        // 而一个Entry对象就包含了key,value,EntrySet<Entry<K,V>>,即:transient Set<Map.Entry<K,V>> entrySet()
        //有一个EntrySet集合,EntrySet集合里面放的是Entry这样子的数据类型,Entry这样子的数据类型包含key和value;
        //3.entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node,Node实现了Map.Entry接口
        //这是因为HashMap$Node  implements Map.Entrty,HashMap$Node实现了这个接口Map.Entrty。
        //当有一个类,实现了一个接口,这个类的对象实例就可以赋给我们的接口类型。
        //4.这样当把HashMap$Node对象存放到entrySet方便我们的遍历,因为Map.Entry提供了两个非常重要的方法
        //getKey()和getValue()方法
        Set set = map.entrySet();
        System.out.println(set.getClass());//输出EntrySet类型
        for (Object obj :set) {
            //System.out.println(obj.getClass()); //输出HashMap$Node类型
            //为了从HashMap$Node取出k-v,先做一个向下转型
           // System.out.println("==============");
            Map.Entry entry=(Map.Entry)  obj;
            System.out.println(entry.getKey()+"-"+entry.getValue());
        }


    }
}

运行界面如下所示:

我们进行Debug来进行更好的理解:

 我们entrySet里面的no2和no1其实是指向table表里面的那两个结点。

 我们发现这两个表的位置是相同的。

 我们这里有一个table表,是以数组+链表+红黑树的来组织我们这个Node,但是为了方便管理,我们在底层做了一个控制,我们把每一个Node封装成一个entry,然后把多个entry放到entrySet这个集合里面去,便于管理,除此之外,还提供了一个叫keyset,如果我们想指向得到这个keyset的话,可以直接把key里面的对象封装到set这样子一个集合。通过这样可以单独的取出key里面的对象。

我们继续进行设计代码:

package com.rgf.map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@SuppressWarnings({"all"})
public class MapSource_ {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("no1","沃尔"); //k-v
        map.put("no2","张无忌"); //k-v
        //1.k-v最后是存放在HashMap$Node  node=new Node<>(hash, key, value, next)这个类型里面的
        //2.k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素的类型就是Entry,
        // 而一个Entry对象就包含了key,value,EntrySet<Entry<K,V>>,即:transient Set<Map.Entry<K,V>> entrySet()
        //有一个EntrySet集合,EntrySet集合里面放的是Entry这样子的数据类型,Entry这样子的数据类型包含key和value;
        //3.entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node,Node实现了Map.Entry接口
        //这是因为HashMap$Node  implements Map.Entrty,HashMap$Node实现了这个接口Map.Entrty。
        //当有一个类,实现了一个接口,这个类的对象实例就可以赋给我们的接口类型。
        //4.这样当把HashMap$Node对象存放到entrySet方便我们的遍历,因为Map.Entry提供了两个非常重要的方法
        //getKey()和getValue()方法
        Set set = map.entrySet();
        System.out.println(set.getClass());//输出EntrySet类型
        for (Object obj :set) {
            //System.out.println(obj.getClass()); //输出HashMap$Node类型
            //为了从HashMap$Node取出k-v,先做一个向下转型
           // System.out.println("==============");
            Map.Entry entry=(Map.Entry)  obj;
            System.out.println(entry.getKey()+"-"+entry.getValue());
        }
        Set set1 = map.keySet();
        System.out.println(set1.getClass());
        for (Object o :set1) {
            System.out.println("key="+o);

        }
        Collection values = map.values();
        for (Object o1 :values) {
            System.out.println("values="+o1);

        }

        System.out.println(values.getClass());
    }
}

运行界面如下所示:

 我们可以查看values的源码:

final class Values extends AbstractCollection<V> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<V> iterator()     { return new ValueIterator(); }
        public final boolean contains(Object o) { return containsValue(o); }
        public final Spliterator<V> spliterator() {
            return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super V> action) {
   
   

也可以查看values的源码:

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) {
   
   

Map接口和常用方法:

Map体系的继承图:

 Map接口常用方法:

我们所设计的代码如下所示:

package com.rgf.map;

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

@SuppressWarnings({"all"})
public class MapMethod {
    public static void main(String[] args) {
        //演示map接口常用方法
        Map map = new HashMap();
        map.put("邓超",new Book("",100));
        map.put("邓超","孙俪");//替换
        map.put("王宝强","马蓉");
        map.put("宋喆","马蓉");
        map.put("沃尔",null);
        map.put(null,"刘亦菲");
        map.put("鹿晗","关晓彤");
        System.out.println("map="+map);
        //remove:根据键删除映射关系
        map.remove(null);
        System.out.println("map="+map);
        //get:根据键获取值
        Object val = map.get("鹿晗");
        System.out.println("val="+val);
        //size:获取元素个数
        System.out.println("k-v="+map.size());
        //isEmpty:判断个数是否为0
        System.out.println(map.isEmpty());//F
        //clear:清除k-v
        map.clear();
        System.out.println("map="+map);
        map.put("rgf","ypl");
        //containsKey:查找键是否存在
        System.out.println("结果="+map.containsKey("rgf"));//F


    }
}
class Book{
    private String name;
    private  int num;

    public Book(String name, int num) {
        this.name = name;
        this.num = num;
    }
}

运行界面如下所示:

 Map接口遍历方法:

(1)containsKey:查找键是否存在

(2)KeySet:获取所有的键

(3)entrySet:获取所有关系k-v

(4)values:获取所有的值

我们设计代码如下所示:

package com.rgf.map;

import java.util.*;

@SuppressWarnings({"all"})
public class MapFor {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("邓超","孙俪");//替换
        map.put("王宝强","马蓉");
        map.put("宋喆","马蓉");
        map.put("沃尔",null);
        map.put(null,"刘亦菲");
        map.put("鹿晗","关晓彤");
        //第一组:先取出所有的Key,通过key取出对应的Value
        Set keyset = map.keySet();
        //(1)增强for循环
        System.out.println("--------keyset第一种方式:增强for循环-------");
        for (Object key :keyset) {
            System.out.println(key+"-"+map.get(key));
        }
        //(2)iterator迭代器
        System.out.println("-------keyset第二种方式:迭代器-------");
        Iterator iterator = keyset.iterator();
        while (iterator.hasNext()) {
            Object key =  iterator.next();
            System.out.println(key+"-"+map.get(key));
        }
        //第二组:把所有的values取出
        Collection values = map.values();
        //这里可以使用collection使用的遍历方法
        //(1)增强for循环
        System.out.println("-------values第一种方式:增强for循环-----");
        for (Object value :values) {
            System.out.println("values="+value);
        }
        //(2)iterator迭代器遍历
        System.out.println("-----values第二种方式:iterator迭代器遍历");
        Iterator iterator1 = values.iterator();
        while (iterator1.hasNext()) {
            Object value = iterator1.next();
            System.out.println("values=" + value);
        }
       //第三组:通过EntrySet来获取k-v
        Set entrySet = map.entrySet(); //EntrySet<Entry<K,V>>
//(1)增强for
        System.out.println("-------entrySet增强for循环----");
        for (Object entry :entrySet) {
            //将entry转成Map.Entry
            Map.Entry m =(Map.Entry) entry;
            System.out.println(m.getKey()+"-"+m.getValue());
        }
        //(2)迭代器
        System.out.println("-------entrySet迭代器------");
        Iterator iterator2 = entrySet.iterator();
        while (iterator2.hasNext()) {
            Object entry= iterator2.next();
            System.out.println(entry.getClass());//HashMap$Node--->实现了Map.Entry(getKey,getValue)
            //向下转型,应该转成HashMap$Node,但是因为他没有提供相应的方法,就直接转成Map.Entry
            Map.Entry  m=(Map.Entry) entry;
            System.out.println(m.getKey()+"-"+m.getValue());
        }


    }


    }

运行界面如下所示:

 实例练习:

使用HashMap添加三个员工对象,要求:

键:员工id

值:员工对象

并遍历显示工资>18000的员工(遍历方式至少两种)

员工类:姓名、工资、员工id

我们设计的代码如下所示:

package com.rgf.map;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@SuppressWarnings({"all"})
public class MapExercise {
    public static void main(String[] args) {
        Map map = new HashMap();
        Object rgf = map.put(1, new staff("rgf", 50000, 1));
        Object ypl = map.put(2, new staff("ypl", 60000, 2));
        Object love = map.put(3, new staff("love", 9999, 3));
        Set keySet = map.keySet();

        //第一种遍历:Keyset方式:增强for循环
        System.out.println("=====第一种遍历方式:keyset增强for循环=====");
        for (Object key : keySet) {
            //先获取value
            staff emp = (staff) map.get(key);
            if (emp.getWages() > 18000) {
                System.out.println(emp);
            }
        }
        System.out.println("=====第二种遍历方式:entryset迭代器方式");
            //第二种遍历:entryset:迭代器方式
            Set set = map.entrySet();
            Iterator iterator = set.iterator();
            while (iterator.hasNext()) {
                Object entry = iterator.next();
                Map.Entry entry1 = (Map.Entry) entry;
                staff value = (staff) entry1.getValue();
                if (value.getWages() > 18000) {
                    System.out.println(value);
                }
            }
        

    }
        static class staff {

        private String name;
        private int wages;
        private int id;

        public staff(String name, int wages,int id) {
            this.name = name;
            this.wages = wages;
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getWages() {
            return wages;
        }

        public void setWages(int wages) {
            this.wages = wages;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

            @Override
            public String toString() {
                return "staff{" +
                        "name='" + name + '\'' +
                        ", wages=" + wages +
                        ", id=" + id +
                        '}';
            }
        }
}

运行界面如下所示:

猜你喜欢

转载自blog.csdn.net/weixin_59448049/article/details/127183127