Java集合(八)Map接口

news2025/1/11 11:33:48

我们来查看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 +
                        '}';
            }
        }
}

运行界面如下所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/193979.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Issue—C#—ssl相关错误

报错&#xff1a;.net 6 : {"MessageTemplate": "An unhandled exception has occurred while executing the request.""Exception": "System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exc…

函数(基础)

目录 一、字符串函数 &#xff08;一&#xff09;常用的字符串函数 &#xff08;二&#xff09;举例 1. concat&#xff08;拼接&#xff09; 2. Lower(大写变小写) 3. upper(小写变大写) 4. lpad(左填充) 5. rpad(右填充) 6. trim(去掉前后空格) 7. ltrim(去掉左边…

Linux-远程管理命令

1.关机/重启序号命令对应英文作用01shutdown 选项 时间shutdown关机/重新启动1.1shutdownshutdown命令可以安全关闭或者重新启动系统选项含义-r重新启动提示&#xff1a;不指定选项和参数&#xff0c;默认表示1分钟之后关闭电脑远程维护服务器时&#xff0c;最好不要关闭系统&a…

Centos7 U盘 安装

目录 一 描述环境 二 下载镜像 2.1 阿里云下载地址: 2.2 Centos 下载&#xff1a; 三 制作U盘启动盘 3.1 下载软碟通 最新UltraISO官方免费下载 - UltraISO软碟通中文官方网站下载https://cn.ultraiso.net/xiazai.html 3.2 傻瓜安装即可&#xff08;不赘述&#xff09; 3.3…

怎么移除css的hover事件

移除css hover事件的方法&#xff1a;1、&#xff1b;通过“$("a").hover(function(){ alert(mouseover); }, function(){ alert(mouseout); })”方法绑定hover事件&#xff1b;2、通过“$(a).off(mouseenter).unbind(mouseleave);”方法取消绑定的hover事件即可。 j…

Filesystem closed报错问题处理

使用HDFS的时候 final Configuration conf new Configuration(); final FileSystem fs FileSystem.get(URI.create(hdfsFile), conf); final Path path new Path(hdfsFile); if (fs.exists(path)) {final FSDataInputStream is fs.open(path);final FileStatus stat fs.g…

一文细说OpenCL框架

说明&#xff1a; 子曾经曰过&#xff1a;不懂Middleware的系统软件工程师&#xff0c;不是一个好码农&#xff1b; 1. 介绍 OpenCL(Open Computing Language&#xff0c;开放计算语言&#xff09;&#xff1a;从软件视角看&#xff0c;它是用于异构平台编程的框架&#xff1b…

华为OD测试岗面经,一周走完面试流程

一周走完面试流程&#xff0c;10.18 机考&#xff0c;机试210 第一题:【最大N个数与最小N个数的和】 第二题&#xff1a;拼接URL 第三题&#xff1a;跳格子 性格测试:题目比较多&#xff0c;有一百多道&#xff0c;在三个选项中选出一个最符合的和一个最不符合的。答题的时候以…

C++:string模拟实现(下)

目录 一.引言 二.string类的容量操作接口 三.string类的字符串修改操作接口 1.两个插入字符的重载函数&#xff1a; 2.在string字符串末尾追加内容的接口 3.在指定位置pos删除n个字符的接口 四.string类字符串的字符和子串查找接口 五.全局定义的string类字符串比较运算…

SSM项目-小说网站

目录 设计目标 需求分析 网站主页 用户注册 1、需求分析 2、数据库设计 3、生成验证码 4、数据加密 1、MD5 2、BCrypt加密 5、数据交换格式 用户登录 找回密码 新用户注册 邮件发送 检测登录状态 书架功能 查看书架 添加书籍进入书架 删除书架上的书籍 获…

kafka在zookeeper中存储结构

1、存储结构图 2、ZooKeeper命令 ZooKeeper -server host:port cmd args stat path [watch] set path data [version] ls path [watch] delquota [-n|-b] path ls2 path [watch] setAcl path acl setquota -n|-b val…

Verticle-align

1.verticle-align的官方解释及所产生的疑问 1.1 vertical-align的官方解释 vertical-align会影响 行内块级元素 在一个 行盒 中垂直方向的位置 【这里有重点词汇&#xff0c;一个行盒&#xff0c;行内块元素&#xff0c;为什么不包括块元素呢&#xff0c;因为块元素是独占一行…

TOP10:餐饮店设计排行榜(2023年最新排名)

随着我国经济不断的高速发展&#xff0c;自13年以来&#xff0c;大众化餐饮市场呈现良好发展趋势&#xff0c;已由13年的2.64万亿增长到3.96万亿&#xff0c;增长率为10.7%&#xff0c;预计2017年到2022年增长速度为9.9%&#xff0c;达到6.28万亿。其中中餐主题餐饮占比维持在8…

JVM 基础 - Java 类加载机制

Java 类加载机制类加载器的分类类加载机制类加载器的分类 如果有必要&#xff0c;我们还可以加入自定义的类加载器。因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件&#xff0c;因此如果编写了自己的ClassLoader&#xff0c;便可以做到如下几点&…

django-rest-framework框架总结之View视图之APIView、GenericAPIView、视图集ViewSet

APIView APIView 是 REST framework 提供的所有视图的基类&#xff0c;继承自Django的View父类。支持认证、限流、授权等功能。 rest_framework.views.APIViewAPIView 与 View 的不同之处在于&#xff1a; 传入到视图方法中的是 REST framework 的 Request 对象&#xff0c;…

Linux gcc和gdb的使用

gcc/g编译器的使用 gcc如何使用 语法&#xff1a; gcc [选项] 编译文件 功能&#xff1a; 用于编译C语言程序&#xff0c;编译C程序使用g。 选项&#xff1a; 指令说明-E只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面-S编译到汇编语言不进行汇编和链接…

python设计模式-单例模式,工厂模式

单例模式 单例模式将类的实例化限制为一个对象。 它是一种创建模式&#xff0c;只涉及创建方法和指定对象的一个类。 它提供了创建实例的全局访问点。 如何实现一个单例类&#xff1f; 下面的程序演示了单例类的实现&#xff0c;并多次打印创建的实例。 class Singleton:_…

动态规划(详细解释)

日升时奋斗&#xff0c;日落时自省 目录 1、Fibonacci 2、字符串分割 3、三角矩阵 4、路径总数 5、最小路径和 6、背包问题 7、回文串分割 8、编辑距离 9、不同子序列 10、总结 DP定义&#xff1a; 动态规划是分治思想的延伸&#xff0c;通俗一点来说就是大事化小&a…

高密度 ARM 服务器如何引领“数智时代”发展,打通“智变质变”正循环

并行计算 | 多样性计算 | ARM架构 深度学习 | 高性能计算 | ARM服务器 如今随着算力、高性能计算的快速发展&#xff0c;数字经济已经成为全球经济增长的主引擎。数字经济的快速发展&#xff0c;使得深度学习、数据分析、数据挖掘等技术迅猛发展起来。伴随国家政策东数西算的…

无痕埋点在Android中的实现

无痕埋点在Android中的实现 目标 解决手动打点效率低下问题自动化埋点 本篇技术实现主要是运行是代理&#xff0c;不涉及到插桩技术&#xff0c;不引入插件&#xff0c;对业务影响点最小 技术难点 1. 如何拦截到所有的view的点击事件 view有个setAccessibilityDelegate方…