当前位置: 首页 > news >正文

数据结构 Map与Set

前言

Map与Set是数据结构中重要的两个容器,它们是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关

对于搜索方式,我们可以将它们分为两种,一种静态搜索,即:数据集合一旦确定后,后续只进行 “查找” 操作,几乎不做插入、删除元素的修改。简单说就是 “数据不动,只查不改”,典型的例子是查找一本固定的字典(字典内容不会临时增减),而另一种是动态搜索,它指的是数据集合在查找过程中,可能随时进行插入、删除元素的修改。简单说就是 “边查边改,数据动态变化”,典型的例子是我们手机中的通讯录,它不仅要查联系人电话,还会随时新增朋友、删除旧联系人。

Map与Set就是一种适合动态查找的集合容器。

在Java的集合框架中:

Map并不属于Collection这个体系中,它是独立于Collection的另一个根接口体系,而Set属于Collection这个体系。

1.纯Key模型Key-Value模型

一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以模型会有两种:纯Key模型Key-Value模型,Map与Set与它们密不可分。

1.纯Key模型,就好像:

  • 在一本英文词典中,快速查找一个单词是否在词典中
  • 快速查找某个名字在不在通讯录中

2.Key-Value模型,就好像:

  • 统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数
  • 梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号

简单来说,纯Key模型可以理解为一个鞋柜,它存放的是一双双鞋子,可以根据鞋子的号码直接找到这双鞋,而Key-Value模型可以理解为一个衣柜,存放的是一套衣服,包括上衣和裤子,可以根据上衣找到与它配对的裤子。

Map中存储的就是key-value的键值对,Set中只存储了Key。下图更加形象:

2.Map

2.1 关于Map的说明

Map是一个接口类,该类没有继承自Collection,该类中存储的是结构的键值对,并且K一定是唯一的,不能重复。那么它是如何储存键值对的呢?它是这样储存的:在Map这个接口中,定义了一个内部类 Map.Entry<K,V>,它是Map内部实现的用来存放键值对映射关系的内部类,它主要提供了键值对<K,V>的获取方法、V的设置方法和K的比较方式,如下表所示:

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

需要注意的是:Map.Entry<K,V>并没有提供设置K的方法!

在Java的集合框架中可以看到,Map底下有两个实现了它的类,分别是TreeMap和HashMap,这两个类最根本的区别是它们的底层结构不同,TreeMap的底层结构是红黑树(红黑树可以理解为是二叉搜索树的升级版,它能够避免二叉搜索树的极端情况出现,从而避免性能下降的问题),HashMap的底层结构是哈希桶(在 JDK 7 及以前,HashMap的底层数据结构是数组 + 链表 ;在 JDK 8 及以后,HashMap的底层数据结构是数组 + 链表 + 红黑树

如果不知道二叉搜索树和哈希桶,请跳转:

数据结构 实现二叉搜索树与哈希表-CSDN博客

TreeMap和HashMap的具体区别如下:

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

2.2 Map的常用方法及说明

方法解释
V get(Object key)返回 key 对应的 value
V getOrDefault(Object key, V defaultValue)返回 key 对应的 value,key 不存在,返回默认值
V put(K key, V value)设置 key 对应的 value
V remove(Object key)删除 key 对应的映射关系
Set<K> keySet()返回所有 key 的不重复集合
Collection<V> values()返回所有 value 的可重复集合
Set<Map.Entry<K,V>> entrySet()返回所有的 key-value 映射关系
boolean containsKey(Object key)判断是否包含 key
boolean containsValue(Object value)判断是否包含 value

【注意事项】

  1. Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap。
  2. Map中存放键值对的Key是唯一的,value是可以重复的。
  3. TreeMap中插入键值对时,key不能为空,否则就会抛NullPointerException异常,value可以为空。但是HashMap的key和value都可以为空。
  4. Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)
  5. Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。
  6. Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入。

2.3 使用案例

这里拿TreeMap来进行举例:

import java.util.*;public class Test {public static void main(String[] args) {Map<String,String> map = new TreeMap<>();// put(key, value):插入key-value的键值对// 如果key不存在,会将key-value的键值对插入到map中,返回nullmap.put("林冲", "豹子头");map.put("鲁智深", "花和尚");map.put("武松", "行者");map.put("宋江", "及时雨");String str = map.put("李逵", "黑旋风");System.out.println(str);System.out.println(map.size());System.out.println(map);System.out.println("====================");// put(key,value): 注意key不能为空,但是value可以为空// key如果为空,会抛出空指针异常//map.put(null, "花名");str = map.put("无名", null);System.out.println(map.size());System.out.println("====================");// put(key, value):// 如果key存在,会使用value替换原来key所对应的value,返回旧valuestr = map.put("李逵", "铁牛");System.out.println(str);System.out.println(map);System.out.println("====================");// get(key): 返回key所对应的value// 如果key存在,返回key所对应的value// 如果key不存在,返回nullSystem.out.println(map.get("鲁智深"));System.out.println(map.get("史进"));System.out.println("====================");//getOrDefault(): 如果key存在,返回与key所对应的value,如果key不存在,返回一个默认值System.out.println(map.getOrDefault("李逵", "铁牛"));System.out.println(map.getOrDefault("史进", "九纹龙"));System.out.println(map.size());System.out.println("====================");//containKey(key):检测key是否包含在Map中,时间复杂度:O(logN)// 按照红黑树的性质来进行查找// 找到返回true,否则返回falseSystem.out.println(map.containsKey("林冲"));System.out.println(map.containsKey("史进"));System.out.println("====================");// containValue(value): 检测value是否包含在Map中,时间复杂度: O(N)// 找到返回true,否则返回falseSystem.out.println(map.containsValue("豹子头"));System.out.println(map.containsValue("九纹龙"));System.out.println("====================");// 打印所有的key,使用foreach打印Set<String> keys = map.keySet();for (String s1:keys) {System.out.println(s1);}System.out.println("====================");// 打印所有的Value,使用foreach打印Collection<String> values = map.values();for (String s2:values) {System.out.println(s2);}System.out.println("====================");// 打印所有的键值对,使用foreach打印Set<Map.Entry<String, String>> key_value = map.entrySet();for (Map.Entry<String, String> s3:key_value) {System.out.println(s3);}}
}//运行结果null
5
{宋江=及时雨, 李逵=黑旋风, 林冲=豹子头, 武松=行者, 鲁智深=花和尚}
====================
6
====================
黑旋风
{宋江=及时雨, 无名=null, 李逵=铁牛, 林冲=豹子头, 武松=行者, 鲁智深=花和尚}
====================
花和尚
null
====================
铁牛
九纹龙
6
====================
true
false
====================
true
false
====================
宋江
无名
李逵
林冲
武松
鲁智深
====================
及时雨
null
铁牛
豹子头
行者
花和尚
====================
宋江=及时雨
无名=null
李逵=铁牛
林冲=豹子头
武松=行者
鲁智深=花和尚

可以发现Map接口的实现类重写了toString()方法,同理Set接口的实现类也重写了toString()方法。

3.Set

3.1 关于Set的说明

当我们充分了解了Map接口,那么对于Set接口,也就很容易理解了。通过Java的集合框架可以知道,Set接口同Map接口一样,它也有两个实现类,分别是TreeSet和HashSet,它们的底层结构与TreeMap和HashMap相似。需要注意的是,Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key

3.2 Set的常用方法及说明

方法解释
boolean add(E e)添加元素,但重复元素不会被添加成功
void clear()清空集合
boolean contains(Object o)判断 o 是否在集合中
Iterator iterator()返回迭代器
boolean remove(Object o)删除集合中的 o
int size()返回set中元素的个数
boolean isEmpty()检测set是否为空,空返回true,否则返回false
Object[] toArray()将set中的元素转换为数组返回
boolean containsAll(Collection <?> c)集合c中的元素是否在set中全部存在,是返回true,否则返回 false
boolean addAll(Collection <? extends E> c)将集合c中的元素添加到set中,可以达到去重的效果

【注意事项】

  1. Set是继承自Collection的一个接口类。
  2. Set中只存储了key,并且要求key一定要唯一
  3. TreeSet的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的。
  4. Set最大的功能就是对集合中的元素进行去重。
  5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。
  6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入。
  7. TreeSet中不能插入null的key,HashSet可以。

3.3 使用案例

这里拿TreeSet来进行举例:

import java.util.*;public class Test {public static void main(String[] args) {Set<String> set = new TreeSet<>();// add(key): 如果key不存在,则插入,返回ture// 如果key存在,返回falseboolean isIn = set.add("apple");set.add("orange");set.add("peach");set.add("banana");boolean flag = set.add("apple");System.out.println(flag);System.out.println(set.size());System.out.println(set);// add(key): key如果是空,抛出空指针异常// set.add(null);System.out.println("=================");// contains(key): 如果key存在,返回true,否则返回falseSystem.out.println(set.contains("apple"));System.out.println(set.contains("watermelen"));System.out.println("=================");// remove(key): key存在,删除成功返回true//              key不存在,删除失败返回false//              key为空,抛出空指针异常set.remove("apple");System.out.println(set);System.out.println("=================");//可以通过迭代器去遍历Iterator<String> it = set.iterator();while (it.hasNext()){System.out.println(it.next());}System.out.println("=================");//对一个集合进行去重,这个集合容器要实现Collection接口List<String> list = new ArrayList<>();list.add("a");list.add("a");list.add("hello");list.add("world");list.add("you");list.add("i");list.add("you");System.out.println(list);Set<String> set1 = new TreeSet<>();set1.addAll(list);System.out.println(set1);}
}//运行结果false
4
[apple, banana, orange, peach]
=================
true
false
=================
[banana, orange, peach]
=================
banana
orange
peach
=================
[a, a, hello, world, you, i, you]
[a, hello, i, world, you]

4.一些关于Java的Map与Set的知识

  1. Java中HashMap与HashSet会在冲突链表(哈希桶内的链表)长度大于一定阈值后,将链表转变为搜索树(红黑树)。
  2. java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key 的 equals 方法。所以如果要用自定义类作为 HashMap 的 key 或者 HashSet 的值,必须覆写 hashCode 和 equals 方法,而且要做到 equals 相等的对象,hashCode 一定是一致的。

到此,关于数据结构中的Map与Set内容到此完结!谢谢您的阅读,如有不对,还请指出。

http://www.dtcms.com/a/499364.html

相关文章:

  • 2025网络架构
  • C++编程学习(第37天)
  • 手机壳在线设计网站网站建设座谈会上的发言
  • 北京北排建设公司招标网站电子商务网站建设规划方案论文
  • hot100练习-10
  • WebSocket实现网站点赞通知
  • NotoSansCJK和SourceHanSansSC两款字体区别浅谈
  • 串口屏学习
  • Conda 环境激活失败或 PATH 优先级被主 Anaconda 覆盖
  • 什么建网站免费做网站代码审计哪个工具比较好
  • AI协同编程架构师:驾驭智能体社会的元语言创造者
  • 肇庆建网站服务腾讯建站平台官网
  • 聊聊 Unity(小白专享、C# 小程序 之 播放器)
  • [MLflow] CI/CD | 测试自动格式化工作流 | Ruff格式化器 | 预提交钩子配置
  • 数据结构——二十四、图(王道408)
  • C#获取钉钉平台考勤记录
  • Java JVM “调优” 面试清单(含超通俗生活案例与深度理解)
  • opencv gpu cuda python c++版本测试代码
  • 建设旅游门户网站安徽网站建设推荐 晨飞网络
  • 鸿蒙Next Wear Engine Kit:打造无缝连接的穿戴应用体验
  • 哪里有免费的网站推广软件网站源码上传完后怎么做
  • 快手测开面试题总结合并版(按分类标注序号+出现频率)
  • P1005 [NOIP 2007 提高组] 矩阵取数游戏
  • JAVA面试复习笔记(待完善)
  • 七、WEB APIs(二)
  • LLMs-from-scratch :多种字节对编码(BPE)对比
  • 济南哪里有网站建设公司网站类网站开发源代码
  • 做笔记的网站源码wordpress手机版论坛
  • 网站推广有哪些举措域名需要跟网站名称一致么
  • 具身神经-机器人通讯架构与实现系列