Map和Set(哈希表)

目录

map:

map说明:

Map.Entry的说明:,v>

Map 的常用方法:

演示:

注意:

TreeMap和HashMap的区别

 Set:

常见方法说明:

注意:

TreeSet和HashSet的区别 

哈希表:

冲突:

冲突-避免:

冲突-避免-负载因子调节:

冲突-解决:

冲突-解决-闭散列:

冲突-解决-开散列/哈希桶:

结语:


map:

map说明:

Map是一个接口类,该类没有继承自Collection,该类中存储的是结构的键值对,并且K一定是唯一的,不能重复。

Map.Entry<K,V>的说明:

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

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

注意:Map.Entry并没有提供设置Key的方法.

Map 的常用方法:

如下图所示:

Map底层可以用Hashmap和Treemap实现,由于Hashmap的效率比较高故下面我用Hashmap来进行演示。

演示:

import java.util.Map;
import java.util.HashMap;
import java.util.Set;
public class Hashmap {public static void main(String[] args) {Map<String,Integer> map = new HashMap<>();map.put("aaa",3);map.put("bbb",3);System.out.println(map.get("aaa"));Set<Map.Entry<String,Integer>> set = map.entrySet();for(Map.Entry<String,Integer> entry:set){System.out.println("Key :"+entry.getKey() + " Value :" + entry.getValue());}}
}

entrySet是比较重要的故进行演示。

效果如下:

这里采用foreach进行遍历,可以不用直到Set的长度。

注意:

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删除掉,然后再来进行 重新插入。

TreeMap和HashMap的区别

 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 c)将集合c中的元素添加到set中,可以达到去重的效果

演示:

public class Test1 {public static void main(String[] args) {Set<String> set = new HashSet<>();set.add("aaa");set.add("bbb");System.out.println(set.size());System.out.println(set.isEmpty());set.clear();}
}

效果如下:

注意:

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可以。

TreeSet和HashSet的区别 

哈希表:

概念:

顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键 码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(logn ),搜索的效率取决于搜索过程中 元素的比较次数。

理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函 数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快 找到该元素。

插入元素:

根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放。

搜索元素:

对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。

该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)

冲突:

对于两个数据元素的关键字和 (i != j),有 != ,但有:Hash( ) == Hash( ),即:不同关键字通过相同哈 希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。 把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。

例如:

下图的4和7就是发生了冲突。

冲突-避免:

 首先,我们需要明确一点,由于我们哈希表底层数组的容量往往是小于实际要存储的关键字的数量的,这就导致一 个问题,冲突的发生是必然的,但我们能做的应该是尽量的降低冲突率。

函数设计:

哈希函数设计原则:

(1)哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1 之间。

(2)哈希函数计算出来的地址能均匀分布在整个空间中。

(3)哈希函数应该比较简单。

常见哈希函数:

(1)直接定制法:

取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B 优点:简单、均匀缺点:需要事先知道关 键字的分布情况使用场景:适合查找比较小且连续的情况。

(2)除留余数法:

设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数: Hash(key) = key% p(p<=m),将关键码转换成哈希地址。

冲突-避免-负载因子调节:

已知哈希表中已有的关键字个数是不可变的,那我们能调整的就只有哈希表中的数组的大小。

冲突-解决:

解决哈希冲突两种常见的方法是:闭散列和开散列。

冲突-解决-闭散列:

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以 把key存放到冲突位置中的“下一个” 空位置中去。

寻找方法:

(1)线性探测

线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

注意:这里的删除都是伪删除。

(2) 二次探测:

找下一个空位置的方法为: = ( + )% m, 或者: = ( - )% m。其中:i = 1,2,3…, 是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置, m是表的大小。

冲突-解决-开散列/哈希桶:

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子 集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

简单来说就是数组加链表。

如图:

开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜索了。

源代码的实现:


public class HashBucket {static class Node{private int key;private int value;Node next;public Node(int key,int value){this.key = key;this.value = value;}}private Node[] array;private int size;public HashBucket(){array = new Node[8];size = 0;}private static final double LOAD_FACTOR = 0.75;public int put(int key,int value){int index = key % array.length;Node cur = array[index];for(;cur != null; cur = cur.next){if(cur.key == key){int oldValue = cur.value;cur.value = value;return oldValue;}}Node node = new Node(key,value);node.next = array[index];array[index] = node;size++;if(loadFactor() >= LOAD_FACTOR){resize();}return -1;}//重新哈希private void resize(){Node[] newArray = new Node[array.length * 2];for(int i = 0;i < array.length; i++){Node next;for(Node cur = array[i]; cur != null; cur = next){next = cur.next;int index = cur.key % newArray.length;cur.next = newArray[index];newArray[index] = cur;}}array = newArray;}private double loadFactor(){return size * 1.0 / array.length;}public int get(int key){int index = key % array.length;for(Node cur = array[index]; cur != null; cur = cur.next){if(key == cur.key){return cur.value;}}return -1;}}

性能分析:

虽然哈希表一直在和冲突做斗争,但在实际使用过程中,我们认为哈希表的冲突率是不高的,冲突个数是可控的, 也就是每个桶中的链表的长度是一个常数,所以,通常意义下,我们认为哈希表的插入/删除/查找时间复杂度是 O(1) 。

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

                                                 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用LORA微调RoBERTa

模型微调是指在一个已经训练好的模型的基础上&#xff0c;针对特定任务或者特定数据集进行再次训练以提高性能的过程。微调可以在使其适应特定任务时产生显着的结果。 RoBERTa&#xff08;Robustly optimized BERT approach&#xff09;是由Facebook AI提出的一种基于Transfor…

【王道数据结构】【chapter5树与二叉树】【P158t8】

设树B是一颗采用链式结构存储的二叉树&#xff0c;编写一个把树B种所有节点的左、右子树进行交换的函数。 #include <iostream> #include <stack> typedef struct treenode{char data;struct treenode *left;struct treenode *right; }treenode,*ptreenode;ptreeno…

docker磁盘不足!已解决~

目录 &#x1f35f;1.查看docker镜像目录 &#x1f9c2;2.停止docker服务 &#x1f953;3.创建新的目录 &#x1f32d;4.迁移目录 &#x1f37f;5.编辑迁移的目录 &#x1f95e;6.重新加载docker &#x1f354;7.检擦docker新目录 &#x1f373;8.删掉旧目录 1.查看doc…

Linux系统之部署File Browser文件管理系统

Linux系统之部署File Browser文件管理系统 一、File Browser介绍1.1 File Browser简介1.2 File Browser功能1.3 File Browser使用场景 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、安装File Browser4…

11-OpenFeign-实现负载均衡策略

2021.0.1版本使用 spring-cloud-loadbalancer 1、默认开启负载均衡策略 使用default RoundRobinLoadBalancer策略 无需yaml文件配置&#xff0c;openfeignclient配置 RandomLoadBalancer &#xff1a;基于随机访问的负载均衡策略NacosLoadBalancer&#xff1a;基于Nacos权重…

Java:什么是向上转型与向下转型(详细图解)

目录 一、什么是向上转型 1、概念 2、代码示例 3、向上转型的优缺点 二、什么是向下转型 1、向下转型的概念 ​编辑 2、代码示例 三、向下转型的缺点及 instanceof 的使用 1、向下转型的缺点 2、instanceof的使用 一、什么是向上转型 1、概念 向上转型就是创建一个…

【MySQL进阶之路】通过实操理解 explain 执行计划

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

重复导航到当前位置引起的。Vue Router 提供了一种机制,阻止重复导航到相同的路由路径。

代码&#xff1a; <!-- 侧边栏 --><el-col :span"12" :style"{ width: 200px }"><el-menu default-active"first" class"el-menu-vertical-demo" select"handleMenuSelect"><el-menu-item index"…

Python算法题集_LRU 缓存

Python算法题集_LRU 缓存 题146&#xff1a;LRU 缓存1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【队列字典】2) 改进版一【有序字典】3) 改进版二【双向链表字典】 4. 最优算法 本文为Python算法题集之一的代码示例 题146&#xff1a;LRU …

Java面试、进阶、实践一网打尽(由电子工业出版社出版)

Java面试、进阶、实践一网打尽 准备好应对Java开发的新挑战吗&#xff1f;我们为您精选了五本核心书籍&#xff0c;一站式满足您在Java面试准备、技能进阶和实战应用的需求。 这套书籍包括《Offer来了&#xff1a;Java面试核心知识点精讲&#xff08;第2版&#xff09;》、《…

电脑数据误删如何恢复?9 个Windows 数据恢复方案

无论您是由于软件或硬件故障、网络犯罪还是意外删除而丢失数据&#xff0c;数据丢失都会带来压力和令人不快。 如今的企业通常将其重要数据存储在云或硬盘上。但在执行其中任何一项操作之前&#xff0c;您很有可能会丢失数据。 数据丢失的主要原因是意外删除&#xff0c;任何…

python算法之 Dijkstra 算法

文章目录 基本思想&#xff1a;步骤&#xff1a;复杂度&#xff1a;注意事项&#xff1a;代码实现K 站中转内最便宜的航班 Dijkstra 算法是一种用于解决单源最短路径问题的经典算法。该问题的目标是找到从图中的一个固定顶点&#xff08;称为源点&#xff09;到图中所有其他顶点…