Map 和 Set 【数据结构】

    • 搜索
  • 模型

  • Map 的使用

    • 常用方法
  • Set 的使用

    • 常见方法:
  • 题目练习

    • 只出现 1 次的数字
  • 复制带随机指针的链表

  • 宝石和石头

  • 旧键盘打字

  • 前 K 个高频单词

搜索


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

模型


一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为 Key-value 的键值对,所以模型会有两种

  1. 纯 key 模型 :即,我们 Set 要解决的事情,只需要判断关键字在不在集合中即可,没有关联的 Value —— 例:有一个英文词典,快速查找一个单词是否在词典中

  2. Key-Value 模型 :即,我们 Map 要解决的事情,需要根据指定 Key 找到关联的 Value

例:梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号

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

Map 的使用


在这里插入图片描述

Map.Entry<K, V>

Map.Entry<K, V> 是 Map 内部实现的用来存放 <key, value> 键值对映射关系的内部类,该内部类中主要提供了<key, value>的获取,value的设置以及Key的比较方式

| 方法 | 说明 |

| — | — |

| K getKey( ) | 返回 Entry 中的 key |

| V getValue( ) | 返回 Entry 中的 value |

| V setValue(V value) | 将键值对中的 value 替换为指定 value |

常用方法

| 方法 | 说明 |

| — | — |

| 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 keySet ( ) | 返回所有 key 的不重复集合 |

| Collection values ( ) | 返回所有 value 的可重复集合 |

| Set<Map.Entry <K, V> > entrySet( ) | 返回所有的 key-value 映射关系 |

| boolean containsKey (Object key) (高效) | 判断是否包含 key |

| boolean containsValue (Object value) (低效) | 判断是否包含 value |

HashMap 使用示例:

public static void main(String[] args) {

Map<Integer,String> map = new HashMap<>();

// put(key, value):插入key-value的键值对

map.put(1,“hello~”);

// key value 重复

map.put(1,“hello”);

map.put(6,“Java”);

map.put(3,“Cpp”);

// put(key,value): key 为null

map.put(null,“Python”);

// put(key,value): value 为null

map.put(2,null);

System.out.println(map.get(null));

System.out.println(map);

// 返回 key 对应的 value

System.out.println(map.get(6));

// 找不到 返回会null

System.out.println(map.get(10));

// 打印所有的 key

for (Integer key : map.keySet()) {

System.out.print(key + " ");

}

System.out.println();

// 打印所有的 value

for (String value : map.values()) {

System.out.print(value + " ");

}

System.out.println();

// 按照映射关系打印

for (Map.Entry<Integer,String> entry : map.entrySet()) {

System.out.println(entry);

}

// 是否包含 key

System.out.println(map.containsKey(6));

//是否包含 value

System.out.println(map.containsValue(“Java”));

}

输出结果:

在这里插入图片描述

TreeMap 使用示例:

1.插入键值对

public static void main(String[] args) {

Map<String,String> map = new TreeMap<>();

//插入键值对

map.put(“西游记”,“吴承恩”);

map.put(“红楼梦”,“曹雪芹”);

map.put(“狂人日记”,“鲁迅”);

// value 重复

map.put(“阿Q正传”,“鲁迅”);

// key 重复

map.put(“阿Q正传”,“key不能重复”);

System.out.println(map.get(“阿Q正传”));

}

输出结果:

在这里插入图片描述

如果key存在,会使用 value 替换原来 key 所对应的 value,返回旧 value

key 不能为null

key 为null,会抛出 NullPointerException 异常

public static void main(String[] args) {

Map<String,String> map = new TreeMap<>();

// key 不能为 null,否则抛出异常

map.put(null,“异常~”);

}

输出结果:

在这里插入图片描述

value 可以为null

public static void main(String[] args) {

Map<String,String> map = new TreeMap<>();

// value 可以为null

map.put(“value为null”,null);

System.out.println(map.get(“value为null”));

}

输出结果:

在这里插入图片描述

注意事项:

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

  • TreeMap 中存放键值对的 key 是唯一的,value 是可以重复的

  • 在 TreeMap 中插入键值对时,key 不能为空,否则就会抛 NullPointerException 异常,但是 value 可以为空

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

  • Map 中的 value 可以全部分离出来,存储在 Collection 的任何一个子集合中 (value可能有重复)

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

  • HashMap 和 TreeMap 的区别

| Map底层结构 | TreeMap | HashMap |

| — | — | — |

| 底层结构 | 红黑树 | 哈希桶 |

| 插入 / 删除 / 查找时间复杂度 | O(logN) | O(1) |

| 是否有序 | 关于 key 有序 | 无序 |

| 线程是否安全 | 不安全 | 不安全 |

| 插入 / 删除 / 查找区别 | 需要进行元素比较 | 通过哈希函数计算哈希地址 |

| 比较与重写 | key 必须能够比较,否则会抛出 ClassCastException 异常 | 自定义类型需要重写 equals 和 hashCode 方法 |

| 应用场景 | 需要 key 有序场景下 | key 是否有序不关心,需要更高的时间性能 |

Set 的使用


Set与Map主要的不同:

  • Set 是继承自 Collection 的接口类

  • Set 中只存储了 key

常见方法:

| 方法 | 说明 |

| — | — |

| 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 中,可以达到去重的效果 |

HashSet 代码示例:

public static void main(String[] args) {

//1.实例化 Set

Set set = new HashSet<>();

//2.插入元素 : add

set.add(“hello”);

set.add(“Java”);

set.add(“Cpp”);

// 插入null

set.add(null);

//3.判断某个值是否存在

System.out.println("Java: " + set.contains(“Java”));

System.out.println("Python: " + set.contains(“Python”));

System.out.println("Cpp: " + set.contains(“Cpp”));

System.out.println("null: " + set.contains(“null”));

//4.删除某个值

System.out.println(“删除Cpp…”);

set.remove(“Cpp”);

System.out.println("Cpp: " + set.contains(“Cpp”));

//删除null

System.out.println(“删除null…”);

System.out.println("null: " + set.contains(null));

//5.打印Set

// a) 直接打印

System.out.println(set);

// b) 循环遍历,使用迭代器

// 迭代器的泛型参数 要和 集合类中保存的元素参数类型一致

// 集合类内部自己实现自己版本的迭代器,不同的集合类 内部的迭代器类型不同,迭代方式也不同

System.out.print(“使用迭代器打印:”);

Iterator iterator = set.iterator();

while (iterator.hasNext()){

String next = iterator.next();

System.out.print(next + " ");

}

}

输出结果:

在这里插入图片描述

TreeSet 代码示例:

public static void main(String[] args) {

//1.实例化 Set

Set set = new TreeSet<>();

//2.插入元素

set.add(“Hello”);

set.add(“Java”);

set.add(“Cpp”);

// 不能插入null,会抛出 空指针异常

// set.add(null);

//3.判断某个值是否存在

System.out.println("Java: " + set.contains(“Java”));

System.out.println("Python: " + set.contains(“Python”));

System.out.println("Cpp: " + set.contains(“Cpp”));

// contains(key): key为null,抛出空指针异常

// System.out.println("null: " + set.contains(null));

//4.删除某个值

System.out.println(“删除Cpp…”);

set.remove(“Cpp”);

System.out.println("Cpp: " + set.contains(“Cpp”));

//删除null

System.out.println(“删除null…”);

// remove(key): key为null,抛出空指针异常

// set.remove(null);

// System.out.println("null: " + set.contains(null));

//5.打印 set

// a) 直接打印

System.out.println(set);

// b) 迭代器打印

System.out.print(“使用迭代器打印:”);

Iterator iterator = set.iterator();

while (iterator.hasNext()){

String next = iterator.next();

System.out.print(next + " ");

}

}

输出结果:

在这里插入图片描述

注意事项:

  • Set 是继承自 Collection 的一个接口类

  • Set 中只存储了 key,key 是唯一的,不能重复

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

  • Set 中的 Key 不能修改,如果要修改,先将原来的删除掉,然后再重新插入

  • TreeSet 和 HashSet 的区别

| Set | TreeSet | HashSet |

| — | — | — |

| 底层结构 | 红黑树 | 哈希桶 |

| 插入 / 删除 / 查找时间复杂度 | O(logN) | O(1) |

| 是否有序 | 关于 key 有序 | 不一定有序 |

| 线程是否安全 | 不安全 | 不安全 |

| 插入 / 删除 / 查找区别 | 按照红黑树的特性来进行插入和删除 | 1. 先计算 key 哈希地址;2. 然后进行插入和删除 |

| 比较与重写 | key 必须能够比较,否则会抛出 ClassCastException 异常 | 自定义类型需要重写 equals 和 hashCode 方法 |

| 应用场景 | 需要 key 有序场景下 | key是否有序不关心,需要更高的时间性能 |

题目练习


只出现 1 次的数字

题目: 在线OJ

在这里插入图片描述

思考:

方法一: 通过 Map 统计每个数字出现的次数,再遍历 Map 找到那个只出现一次的数字

  • 创建 Map<Integer,Integer>,key:当前出现的数字;value:该数字出现次数

  • 遍历统计次数即可~

方法二: 按位异或 A ^ B ^ A = BA ^ 0 = A,异或规则,相同为0,相异为1

代码实现:

  • 方法1

public int singleNumber(int[] nums) {

// key : 当前数字

// value : 数字出现次数

Map<Integer,Integer> map = new HashMap<>();

for (int x : nums) {

Integer value = map.get(x);

// 当前数字在 map 中不存在,新增一个键值对

if(value == null){

map.put(x,1);

}

//这个数字已经存在了

else{

map.put(x,value + 1);

}

}

// 遍历 map ,找到出现次数为1的数

for (Map.Entry<Integer,Integer> entry : map.entrySet()) {

// getValue 得到的是一个 Integer 包装类

// 使用 equals,相当于对 1 自动装箱

// 使用 == ,相当于对 Integer 自动拆箱

if(entry.getValue().equals(1)){

return entry.getKey();

}

}

//没找到

return 0;

}

  • 方法2

public int singleNumber(int[] nums) {

int result = 0;

for (int x : nums) {

result ^= x;

}

return result;

}

复制带随机指针的链表

题目: 在线OJ

在这里插入图片描述

思考:

  • 创建一个 Map <Node,Node>,key:旧链表的节点;value:新链表的节点(旧链表对应节点的拷贝)

  • Map 结构创建好了之后,遍历旧链表,取出节点在 Map 中找到对应的 value

例:新node1.next = map.get (旧node1.next);

新node1.random = map.get (旧node1.random);

代码实现:

public Node copyRandomList(Node head) {

//1.创建 map

Map<Node,Node> map = new HashMap<>();

//2.遍历旧链表,把旧链表的每个节点 依次插入到map中

// key: 旧链表节点

// value: 新链表节点

for (Node cur = head;cur != null;cur = cur.next){

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

public Node copyRandomList(Node head) {

//1.创建 map

Map<Node,Node> map = new HashMap<>();

//2.遍历旧链表,把旧链表的每个节点 依次插入到map中

// key: 旧链表节点

// value: 新链表节点

for (Node cur = head;cur != null;cur = cur.next){

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-rf7dvrt4-1715827090371)]

[外链图片转存中…(img-eQrMTqjs-1715827090372)]

[外链图片转存中…(img-G2quYIg2-1715827090372)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值