Map和Set的详解

news2025/1/22 21:36:58

  Map和Set是一种专门用来搜素的容器或者数据结构,其搜索的效率与其具体的实例化子类有关,是一种适合动态查找的集合容器

一、模型

       一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称为Key-Value的键值对 

因此模型会有两种:

   1、纯Key模型

   2、Key-Value模型

其中Map中存储的是Key-Value的键值对,Set中只存储了Key

二、Map的使用

   由此可知:Map是一个接口类,该类没有继承Collection,该类中存储的是<K,V>结构的键值对,并且K是唯一的,不能重复

  1、Map.Entry<K,V>的说明

          Map.Entry<K,V>是Map内部实现的用来存放<Key,Value>键值对映射关系的内部类,该类主要提供了<Key,Value>的获取

方法解释
K.getKey()返回entry中的key
V.getValue()返回entry中的value
V.setValue(V value)将键值对中的value替换为指定value

注: Map.Entry<K,V>并没有提供设置Key的方法

 2、Map的常用方法

注:

    (1) Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap

    (2) Map中存放键值对的Key是唯一的,value是可以重复的

    (3)Map的Key可以全部分离出来,存储到Set中进行访问(因为Key不能重复)

    (4)Map的value可以全部分离出来,存储到Collection中的任意一个子集合中进行访问(因为value不能重复)

    (5)Map中键值对中的Key不能直接修改,value可以修改,如果要修改key,只能先将key删除掉,然后再进行重新插入

    (6)TreeMap和HashMap的区别

Map底层结构TreeMapHashMap
底层结构红黑树哈希桶
插入/删除/查找时间复杂度O(log2^N)O(1)
是否有序关于key有序无序
线程安全不安全不安全
插入/删除/查找区别需要进行元素比较通过哈希函数计算哈希地址
比较与重写key必须能够比较,否则会抛出ClassCastException异常自定义类型需要重写equals和hashCode方法
应用场景需要key有序场景下key是否有序不关心,需要更高的时间性能

 3、HashMap的使用案例

public class TestHashMap {
    public static void main(String[] args) {
        Map<Integer,String> hashMap = new HashMap<>();
        hashMap.put(1,"hello");
        hashMap.put(3,"world");
        hashMap.put(20,"world");
        hashMap.put(4,"world");
        hashMap.put(5,"world");
        System.out.println(hashMap);//输出是无序的
        System.out.println(hashMap.getOrDefault(100,"啥也没有"));

        //按照指定的key获取value
        String v1 = hashMap.get(1);
        String v5 = hashMap.get(5);
        System.out.println(v1);
        System.out.println(v5);
    }
}

 4、TreeMap的使用案例

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

public class TestTreeMap {
    //TreeMap的演示,是有序的
    public static void main(String[] args) {
        TreeMap<Integer,String> treeMap = new TreeMap<>();
        treeMap.put(1,"hello");
        treeMap.put(3,"world");
        treeMap.put(2,"world");
        treeMap.put(4,"world");
        treeMap.put(5,"world");
        System.out.println(treeMap);

        //按照指定的key获取value
        String v1 = treeMap.get(1);
        String v5 = treeMap.get(5);
        System.out.println(v1);
        System.out.println(v5);

        String v4 = treeMap.getOrDefault(4,"空值");
        String v10 = treeMap.getOrDefault(10,"空值");
        System.out.println(v4);
        System.out.println(v10);

        //删除指定的元素
        treeMap.remove(5);
        System.out.println(treeMap);

        //获取所有的key的不重复集合
        Set<Integer> keySet = treeMap.keySet();
        System.out.println(keySet);
        //获取所有的value
        Collection<String> values = treeMap.values();
        System.out.println(values);
        //是否包含key,是否包含value
        System.out.println(treeMap.containsKey(1));
        System.out.println(treeMap.containsValue("hello"));

        //遍历
        fun1(treeMap);
        System.out.println("方法二");
        fun2(treeMap);

    }
    //根据所有的key去获取值,方法一,不常用
    private static void fun1(Map<Integer, String> map) {
        Set<Integer> keySet = map.keySet();
        for (Integer key : keySet) {
            System.out.println("key = " + key + " value = " + map.get(key));
        }
    }

    //方法二
    private static void fun2(Map<Integer, String> map) {
        Set<Map.Entry<Integer,String>> entries = map.entrySet();
        for (Map.Entry<Integer,String> entry : entries) {
            System.out.println("key = " + entry.getKey() + " value = " + entry.getValue());
        }
    }
}

 

三、Set的说明

        Set是继承自Collection的接口类,Set只存储了key

 1、常见方法说明

注:

     (1)Set是继承Collection的一个接口类

     (2) Set只存储了key,并且要求key唯一

     (3)Set底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的

     (4)Set最大的功能就是对集合中的元素进行去重

     (5)实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序

     (6)Set的key不能修改,如果要修改,先把原来的删除,再重新插入

     (7)Set不能插入null的key

     (8)TreeSet和HashSet的区别

Set底层结构TreeSetHashSet
底层结构红黑树哈希桶
插入/删除/查找时间复杂度O(log2^N)O(1)
是否有序关于key有序不一定有序
线程安全不安全不安全
插入/删除/查找区别按照红黑树的特性来进行插入和删除先计算key的哈希地址,然后进行插入和删除
比较与重写key必须能够比较,否则会抛出ClassCastException异常自定义类型需要重写equals和hashCode方法
应用场景需要key有序场景下key是否有序不关心,需要更高的时间性能

三、搜索树

     二叉搜索树又被称为二叉排序树,它或者是一颗空树,或者是具有以下性质的二叉树

    若它的左子树不为空,那么左子树上所有的节点的值都小于根节点的值

    若它的右子树不为空,那么右子树上所有的节点的值都大于根节点的值

    它的左右子树也分别为二叉搜索树

 1、删除操作

       设待删除的结点为cur,双亲结点为parent

        (1)cur.left == null

                    cur是root,则root == cur.right

                    cur不是root,cur是parent.left,则parent.left == cur.right

                    cur不是root,cur是parent.right,则parent.right == cur.right

        (2)cur.right == null

                    cur是root,则root == cur.left

                    cur不是root,cur是parent.left,则parent.left == cur.left

                    cur不是root,cur是parent.right,则parent.right == cur.left

        (3)cur.left != null && cur.right != null

                   需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题

  2、实现

public class BinarySearchTree {
    private static class TreeNode {
        TreeNode left;
        TreeNode right;
        int val;

        public TreeNode(int val) {
            this.val = val;
        }
    }
    public TreeNode root;

    /**
     * 查找指定的值是否存在
     * @param val
     * @return
     */
     public boolean search(int val) {

         return false;
     }

    /**
     * 插入一个元素
     * @param val 要插入的值
     * @return
     */
     public boolean insert(int val) {
         TreeNode node = new TreeNode(val);
         if (root == null) {
             root = node;
             return true;
         }
         TreeNode cur = root;
         TreeNode pre = null;
         while (cur != null) {
             if (val == cur.val) {
                 return false;
             }
             pre = cur;
             if (val < cur.val) {
                 cur = cur.left;

             } else {
                 cur = cur.right;
             }
         }
         if (val < pre.val) {
             pre.left = node;
         } else {
             pre.right = node;
         }
         return true;
     }

     public boolean remove(int val) {
         if (root == null) {
             return false;
         }
         TreeNode cur = root;
         TreeNode parent = null;
         while (cur != null) {
             if (cur.val == val) {
                 removeNode(parent,cur);
                 return true;
             }
             parent = cur;
             if (cur.val > val) {
                 cur = cur.left;
             } else {
                 cur = cur.right;
             }
         }
         return false;
     }

    private void removeNode(TreeNode parent,TreeNode cur) {
         if (cur.left == null) {
             if (cur == root) {
                 root = cur.right;
             } else if (cur == parent.left) {
                 //当前节点是父节点的左孩子节点时
                 parent.left = cur.right;
             } else {
                 //当前节点是父节点的右孩子节点时
                 parent.right = cur.right;
             }
         } else if (cur.right == null) {
             if (cur == root) {
                 root = cur.left;
             } else if (cur == parent.left) {
                 //当前节点是父节点的左孩子节点时
                 parent.left = cur.left;
             } else {
                 //当前节点是父节点的右孩子节点时
                 parent.right = cur.left;
             }

         } else {
             TreeNode target = cur.right;
             TreeNode parentTarget = cur;
             while (target.left != null) {
                 parentTarget = target;
                 target = target.left;
             }
             //到达叶子节点
             cur.val = target.val;
             //删除target节点
             if (target == parentTarget.left) {
                 parentTarget.left = target.right;
             } else {
                 parentTarget.right = target.right;
             }
         }

     }

    public String inorder(TreeNode node) {
         StringBuilder sb = new StringBuilder();
         if (node == null) {
             return sb.toString();
         }
         String left  =inorder(node.left);
         sb.append(left);
         sb.append(node.val + " ");
         String right = inorder(node.right);
         sb.append(right);
         return sb.toString();
     }
     public boolean search1(int val) {
         if (root == null) {
             return false;
         }
         TreeNode cur = root;
         while (cur != null) {
             if (val == cur.val) {
                 return true;
             }
             if (val < cur.val) {
                 cur = cur.left;
             } else {
                 cur = cur.right;
             }
         }
         return false;
     }
}

四、练习题加深理解

import java.util.*;

public class Exe_01 {
    public static void main(String[] args) {
        //生成十万个元素的数组
        int capacity = 10000;
        int[] arr = new int[capacity];
        Random random = new Random();
        for (int i = 0;i < arr.length;i++) {
            int value = random.nextInt(capacity);
            arr[i] = value;
        }
        fun1(arr);
        fun2(arr);
        fun3(arr);
    }
    //去除十万个元素中重复的元素
    public static void fun1(int[] arr) {
        Set<Integer> set = new HashSet<>();
        for (int i = 0;i < arr.length;i++) {
            set.add(arr[i]);
        }
        System.out.println("去重后元素个数" + set.size());
    }

    //查找十万个元素中第一次重复的元素
    public static void fun2(int[] arr) {
        Set<Integer> set = new HashSet<>();
        for (int i = 0;i < arr.length;i++) {
            if (!set.contains(arr[i])) {
                set.add(arr[i]);
            } else {
                System.out.println("第一个重复的元素" + arr[i]);
                return;
            }
        }

    }
    //统计十万个元素中,每个元素出现的次数
    public static void fun3(int[] arr) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0;i < arr.length;i++) {
            int cnt = map.getOrDefault(arr[i],0);
            map.put(arr[i],cnt + 1);
        }
    }
}

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

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

相关文章

Teams Tab App 的 manifest 分析

上一篇文章我们深入来分析了 tab app 的代码&#xff0c;这篇文章我们研究一下 manifest。 Teams 的 manifest 实际上是一个很复杂的 json&#xff0c;里面的配置非常多&#xff0c;之前的文章陆陆续续的讲过一些配置&#xff0c;我们在这篇文章里来看看关于 tabs 的一些配置&…

链表之删除单链表中的重复节点

文章目录删除单链表中的重复节点题目描述解题思路代码实现删除单链表中的重复节点 力扣链接 题目描述 编写代码&#xff0c;移除未排序链表中的重复节点。保留最开始出现的节点。 示例1:输入&#xff1a;[1, 2, 3, 3, 2, 1]输出&#xff1a;[1, 2, 3]示例2:输入&#xff1a;…

WEB网站安全检测系统设计与实现

目 录 1 引言 1 2 Web服务器所受的威胁及防御 1 2.1 缓冲区溢出 1 2.2 SQL注入攻击 1 2.3 基于脚本的DDos攻击 2 2.4 其他的不安全因素 3 3 Web的木马检测系统的设计 4 3.1 体系结构 4 3.2 处理流程 5 3.3 对客户端访问的响应 7 3.4 策略引擎的设计 8 3.4.1 策略的属性 8 3.4.2…

9、软件包管理

文章目录9、软件包管理9.1 RPM9.1.1 RPM 概述9.1.2 RPM 查询命令&#xff08;rpm -qa&#xff09;9.1.3 RPM 卸载命令&#xff08;rpm -e&#xff09;9.1.4 RPM 安装命令&#xff08;rpm -ivh&#xff09;9.2 YUM 仓库配置9.2.1 YUM 概述9.2.2 YUM 的常用命令9.2.3 修改网络 YU…

Linux系统中基本的启动方式

大家好&#xff0c; 今天主要和大家聊一聊&#xff0c;Linux系统的启动方式有哪些&#xff1f; 目录 第一&#xff1a;启动方式基本简介 第二​&#xff1a;启动模式的选择 第一&#xff1a;启动方式基本简介 Linux系统支持多种启动方式&#xff0c;可以从SD/EMMC、NAND Flas…

Jumperserver堡垒机管理服务器实战

一、 Jumpserver堡垒机简介 1、跳板机简介 跳板机就是一台服务器,开发或运维人员在维护过程中首先要统一登录到这台服务器,然后再登录到目标设备进行维护和操作。 跳板机缺点:没有实现对运维人员操作行为的控制和审计,使用跳板机的过程中还是会出现误操作、违规操作导致的…

Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码

前言 vue-admin-template是一个最基础的后台管理模板&#xff0c;只包含了一个后台需要最基础的东西&#xff0c;如果clone的是它的master分支&#xff0c;是没有权限管理的&#xff0c;只有完整版vue-element-admin有这个功能&#xff0c;但是为了小小的一个权限管理而用比较…

java框架 Spring之 AOP 面向切面编程 切入点表达式 AOP通知类型 Spring事务

AOP(Aspect Oriented Programming)面向切面编程&#xff0c;一种编程范式&#xff0c;指导开发者如何组织程序结构 作用&#xff1a;在不惊动原始设计的基础上为其进行功能增强 Spring理念&#xff1a;无入侵式/无侵入式 我们在不修改源代码的时候&#xff0c;为了执行另外的…

idea如何快速找到项目中对应的类(包括源码)

文章目录1. 前言2. 先说结论3. idea的全局搜索功能 MethodValidation4. 搜索spring源码(例子)1. 前言 最近在看某些功能的时候&#xff0c;会去看对应的源码&#xff0c;而有时候只知道类名&#xff0c;不知道从哪里进入源码&#xff0c;因此就比较好奇&#xff0c;idea的全局…

JAVA类加载器

JAVA是一种解释型语言&#xff0c;也就是一种边解释边执行的语言。JAVA所有源代码在执行之前&#xff0c;先要被编译成class文件&#xff0c;然后类加载器加载解析class文件&#xff0c;最后才执行。 JVM自带了几个类型的类加载器&#xff0c;JVM使用分层的软件架构方式设计类…

蜂鸟E203学习笔记(二)--蜂鸟E203总体框架

蜂鸟E203总体框架 蜂鸟E203处理器系统如下图所示 一、蜂鸟E203处理器核设计总览和顶层 1.1 蜂鸟E203处理器核的设计理念 模块化和可重用性&#xff1a;将处理器分成几个主体模块&#xff0c;每个单元之间的接口简单清晰。面积最小化&#xff1a;追求低功耗和小面积&#x…

DS18B20

一、DS18B20初始化时序图 &#xff08;1&#xff09; 先将数据线置高电平“1”。 &#xff08;2&#xff09; 延时&#xff08;该时间要求的不是很严格&#xff0c;但是尽可能的短一点&#xff09;。 &#xff08;3&#xff09; 数据线拉到低电平“0”。 &#xff08;4&#xf…

【配送路径规划】基于matlab遗传算法求解静态外卖骑手路径规划问题【含Matlab源码 2248期】

⛄一、遗传算法求解静态外卖骑手路径规划问题 1 模型假设 外卖配送的实际运行是一个复杂的过程, 受诸多因素影响, 为了建立调度模型, 本文做如下假设。 (1) 外卖配送更多的是服务特殊群体, 所以本文认为外卖配送是一种预约型配送, 即在进行调度安排前, 己经获取了所有顾客的地…

Java并发-生产者消费者实现

生产者与消费者模型介绍 定义&#xff1a; 生产者消费者模式是一个十分经典的多线程并发协作的模式。 意义&#xff1a;弄懂生产者消费者问题能够让我们对并发编程的理解加深。 介绍&#xff1a;所谓生产者 - 消费者问题&#xff0c;实际上主要是包含了两类线程&#xff0c;…

Python面向对象编程之对象行为与特殊方法

面向对象编程之对象行为与特殊方法 python中的对象通常根据它们的行为和实现的功能进行分类。例如&#xff0c;所有序列类型都分在一组&#xff0c;如字符串&#xff0c;列表和元组&#xff0c;就是因为它们都支持一组相同的序列操作&#xff0c;如s[n], len[s]等。 所有基本…

【学习总结】LSD-SLAM配置与运行记录

今天安装测试了LSD-SLAM&#xff0c;记录配置中遇到的问题。 LSD-SLAM论文 LSD-SLAM: Large-Scale Direct Monocular SLAM, J. Engel, T. Schps, D. Cremers, ECCV 14 Semi-Dense Visual Odometry for a Monocular Camera, J. Engel, J. Sturm, D. Cremers, ICCV 13 配置环境…

SpringBoot实现多数据源(四)【集成多个 Mybatis 框架】

上一篇文章《SpringBoot实现多数据源&#xff08;三&#xff09;【AOP 自定义注解】》 四、集成多个 Mybatis 框架 实现步骤 创建一个 dynamic_mybatis 的springboot项目&#xff0c;导入依赖 pom.xml <dependencies><!--jdbc--><dependency><groupId…

Fiddler抓取手机app包

文章目录1. 配置fiddler1.1 下载fiddler1.2 配置fiddler2. 安装证书2.1 查询主机ip2.2下载证书2.3 手机安装证书2.4 查询安装的证书3. 手机设置代理4. 测试是否抓包成功大前提&#xff1a;手机和Fiddler所在的主机在同一网段&#xff0c;且能够互相访问 1. 配置fiddler 1.1 下…

第三十八篇 Vue中封装Swiper组件 2.0

上一篇内容讲到封装Swiper组件的一个过程&#xff0c;如果是静态的数据封装组件初始化在mounted当中并无多大影响&#xff0c;但是这样封装的组件复用性较低或者可能只使用一次&#xff0c;那么在动态使用通过ajax请求数据需要面临的是swiper初始化过早的问题&#xff0c;在mou…

【车间调度】基于matlab混合蛙跳算法 (SFLA) 求解简单调度问题【含Matlab源码 2247期】

⛄一、车间调度简介 在传统的SFLA中&#xff0c;每一个青蛙的位置代表一个解&#xff0c;若干个青蛙组成的种群代表一个解的集合&#xff0c;种群被划分为不同的组&#xff0c;即模因组&#xff0c;对每个模因组执行搜索过程&#xff0c;当达到终止条件后&#xff0c;重新将模…