HashMap面试题

1.hashMap底层实现

hashMap的实现我们是要分jdk 1.7及以下版本,jdk1.8及以上版本

jdk 1.7 实现是用数组+链表

jdk1.8 实现是用数组+链表+红黑树, 链表长度大于8(TREEIFY_THRESHOLD)时,会把链表转换为红黑树,红黑树节点个数小于6(UNTREEIFY_THRESHOLD)时才转化为链表,防止频繁的转化

hashMap的一些常量

// 默认初始容量

DEFAULT_INITIAL_CAPACITY 16

// 数组默认最大的容量

DEFAULT_INITIAL_CAPACITY

当元素的总个数>当前数组的长度 * 负载因子。数组会进行扩容,扩容为原来的两倍

// 最大加载因子

DEFAULT_LOAD_FACTOR = 0.75f;

链表树化阙值: 默认值为 8 。表示在一个node(Table)节点下的值的个数大于8时候,会将链表转换成为红黑树。

TREEIFY_THRESHOLD = 8;

表示在进行扩容期间,单个Node节点下的红黑树节点的个数小于6时候,会将红黑树转化成为链表。

UNTREEIFY_THRESHOLD = 6;

最小树化阈值,当Table所有元素超过改值,才会进行树化

MIN_TREEIFY_CAPACITY = 64;

hash 带初始容量,加载因子

public HashMap(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);
}

使用默认加载 因子

public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

hash 带初始容量

public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

转入一个map对象,的构造器

public HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);
}

为什么建议设置HashMap的容量?

HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容条件就是当HashMap中的元素个数超过临界值时就会自动扩容(threshold = loadFactor * capacity)。

如果我们没有设置初始容量大小,随着元素的不断增加,HashMap会发生多次扩容。而HashMap每次扩容都需要重建hash表,非常影响性能。所以建议开发者在创建HashMap的时候指定初始化容量。

扩容过程?

下面HashMap的put方法, 首先hashMap 会通过hash 算法,将key转换一个hash值(如果key 为null ,hash值就为0,如果不为null,先获取key的hashCode值,然后将他将他与 与自己 hashCode值右移16位进行 异或运算。),然后进入putVal方法

   public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

putVal方法

putVal 首先判断当前hash数组是否有值,当前table如果没有值,就会进行扩容执行 resize()方法

判断当前节点是否有值,如果有值就将当前的节点赋值给当前数组,如果有就就判断当前的key的值

是否一样是一样的就将当前的value覆盖,当前节点是红黑树,就将节点加入到红黑树中,

否则加入到链表中

然后判断当前数组大小是否大于扩容容量,是的就扩容

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);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;
}

**resize方法:**在初始化table或者在size超过threshold之后进行扩容执行resize方法

一.判断当前的数组长度是否是大于0,如果大于0,

(1)判断当前数组长度是否大于hashMap规定的最大值,大于最大值,将当前容器大小置为最大 值,然后返回

​ (2)判断当前的数组是否数组长度扩大一倍是是否小于最大值并且大于默认初始容量,就会将当前的容量大小threshold扩大一倍

二、如果当前初始过最大容量,就用初始化的容量,

三、否则,就用默认使用默认初始容量(DEFAULT_INITIAL_CAPACITY),扩容容量为加载因子(0.75),默认初始容量

if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold
}else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;else {               // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}

判断当前扩容容量是否0,如果为0,就将扩容容量改为,数组长度*加载因子

if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);
}

创建hashMap的扩容后的数组,判断旧的数组是否不为空,遍历数组的每一个元素

(1)判断当前元素是否不是只有一个不是链表,将当前hashMap取模存在hashMap数组里面

(2)判断当前是否为红黑树,如果是红黑树,就将红黑树赋给新数组

()判断当前是否为链表,如果是链表,就将链表赋给新数组

hreshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {for (int j = 0; j < oldCap; ++j) {Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;if (e.next == null)newTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { // preserve orderNode<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}
}

resize方法:全部代码

/***初始化或加倍表大小*如果为零,则在中分配*符合字段阈值中保持的初始容量目标。*否则,因为我们使用的是二次幂展开*每个垃圾箱中的物品必须保持在同一索引处,或者移动*在新表格中偏移量为2的幂。**返回table*/final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;int newCap, newThr = 0;if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold}else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;else {               // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr;@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab;if (oldTab != null) {for (int j = 0; j < oldCap; ++j) {Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;if (e.next == null)newTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { // preserve orderNode<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;
}

2.扩容过程?

1.8扩容机制:当元素个数大于threshold时,会进行扩容,使用2倍容量的数组代替原有数组。采用尾插入的方式将原数组元素拷贝到新数组。1.8扩容之后链表元素相对位置没有变化,而1.7扩容之后链表元素会倒置。

1.7链表新节点采用的是头插法,这样在线程一扩容迁移元素时,会将元素顺序改变,导致两个线程中出现元素的相互指向而形成循环链表,1.8采用了尾插法,避免了这种情况的发生。

原数组的元素在重新计算hash之后,因为数组容量n变为2倍,那么n-1的mask范围在高位多1bit。在元素拷贝过程不需要重新计算元素在数组中的位置,只需要看看原来的hash值新增的那个bit是1还是0,是0的话索引没变,是1的话索引变成“原索引+oldCap”(根据e.hash & oldCap == 0判断) 。这样可以省去重新计算hash值的时间,而且由于新增的1bit是0还是1可以认为是随机的,因此resize的过程会均匀的把之前的冲突的节点分散到新的bucket。

3.解决hash冲突的办法有哪些?HashMap用的哪种?

虽然我们不希望发生冲突,但实际上发生冲突的可能性仍是存在的。当关键字值域远大于哈希表的长度,而且事先并不知道关键字的具体取值时。冲突就难免会发 生。另外,当关键字的实际取值大于哈希表的长度时,而且表中已装满了记录,如果插入一个新记录,不仅发生冲突,而且还会发生溢出。因此,处理冲突和溢出是 哈希技术中的两个重要问题。

解决hash冲突的办法:链地址法、再哈希法、建立公共溢出区,开放定址法

HashMap中采用的是 链地址法 。

法1:链地址法
对于相同的哈希值,使用链表进行连接。(HashMap使用此法)

优点

处理冲突简单,无堆积现象。即非同义词决不会发生冲突,因此平均查找长度较短;
适合总数经常变化的情况。(因为拉链法中各链表上的结点空间是动态申请的)
占空间小。装填因子可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计
删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。
缺点

查询时效率较低。(存储是动态的,查询时跳转需要更多的时间)
在key-value可以预知,以及没有后续增改操作时候,开放定址法性能优于链地址法。
不容易序列化
法2:再哈希法
提供多个哈希函数,如果第一个哈希函数计算出来的key的哈希值冲突了,则使用第二个哈希函数计算key的哈希值。

优点

不易产生聚集
缺点

增加了计算时间
法3:建立公共溢出区
将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。

法4:开放定址法
当关键字key的哈希地址p =H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,若p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。

4.使用的hash算法?

Hash算法:取key的hashCode值、高位运算、取模运算。

h=key.hashCode() //第一步 取hashCode值
h^(h>>>16)  //第二步 高位参与运算,减少冲突
return h&(length-1);  //第三步 取模运算

在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:这么做可以在数组比较小的时候,也能保证考虑到高低位都参与到Hash的计算中,可以减少冲突,同时不会有太大的开销。

5.为什么建议设置HashMap的容量?

HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容条件就是当HashMap中的元素个数超过临界值时就会自动扩容(threshold = loadFactor * capacity)。

如果我们没有设置初始容量大小,随着元素的不断增加,HashMap会发生多次扩容。而HashMap每次扩容都需要重建hash表,非常影响性能。所以建议开发者在创建HashMap的时候指定初始化容量。

6.put方法流程?

  1. 如果table没有初始化就先进行初始化过程
  2. 使用hash算法计算key的索引
  3. 判断索引处有没有存在元素,没有就直接插入
  4. 如果索引处存在元素,则遍历插入,有两种情况,一种是链表形式就直接遍历到尾端插入,一种是红黑树就按照红黑树结构插入
  5. 链表的数量大于阈值8,就要转换成红黑树的结构
  6. 添加成功后会检查是否需要扩容

7.红黑树的特点?

  • 每个节点或者是黑色,或者是红色。
  • 根节点和叶子节点(NIL)是黑色的。
  • 如果一个节点是红色的,则它的子节点必须是黑色的。
  • 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

8.在解决 hash 冲突的时候,为什么选择先用链表,再转红黑树?

因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。所以,当元素个数小于8个的时候,采用链表结构可以保证查询性能。而当元素个数大于8个的时候并且数组容量大于等于64,会采用红黑树结构。因为红黑树搜索时间复杂度是 O(logn),而链表是 O(n),在n比较大的时候,使用红黑树可以加快查询速度。

9.HashMap 的长度为什么是 2 的幂次方?

Hash 值的范围值比较大,使用之前需要先对数组的长度取模运算,得到的余数才是元素存放的位置也就是对应的数组下标。这个数组下标的计算方法是(n - 1) & hash。将HashMap的长度定为2 的幂次方,这样就可以使用(n - 1)&hash位运算代替%取余的操作,提高性能。

10.HashMap默认加载因子是多少?为什么是 0.75?

先看下HashMap的默认构造函数:

int threshold;             // 容纳键值对的最大值
final float loadFactor;    // 负载因子
int modCount;  
int size;  

Node[] table的初始化长度length为16,默认的loadFactor是0.75,0.75是对空间和时间效率的一个平衡选择,根据泊松分布,loadFactor 取0.75碰撞最小。一般不会修改,除非在时间和空间比较特殊的情况下 :

  • 如果内存空间很多而又对时间效率要求很高,可以降低负载因子Load factor的值 。
  • 如果内存空间紧张而对时间效率要求不高,可以增加负载因子loadFactor的值,这个值可以大于1。

11.一般用什么作为HashMap的key?

一般用IntegerString这种不可变类当 HashMap 当 key。String类比较常用。

  • 因为 String 是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就是 HashMap 中的key经常使用字符串的原因。
  • 获取对象的时候要用到 equals()hashCode() 方法,而Integer、String这些类都已经重写了 hashCode() 以及 equals() 方法,不需要自己去重写这两个方法。

12.HashMap为什么线程不安全?

  • 多线程下扩容死循环。JDK1.7中的 HashMap 使用头插法插入元素,在多线程的环境下,扩容的时候有可能导致环形链表的出现,形成死循环。
  • 在JDK1.8中,在多线程环境下,会发生数据覆盖的情况。

13.HashMap和HashTable的区别?

HashMap和Hashtable都实现了Map接口。

  1. HashMap可以接受为null的key和value,key为null的键值对放在下标为0的头结点的链表中,而Hashtable则不行。
  2. HashMap是非线程安全的,HashTable是线程安全的。Jdk1.5提供了ConcurrentHashMap,它是HashTable的替代。
  3. Hashtable很多方法是同步方法,在单线程环境下它比HashMap要慢。
  4. 哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。

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

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

相关文章

centos7卸载docker

菜鸟教程-常见命令&#xff1a;https://www.runoob.com/docker/docker-command-manual.html 1. 准备工作&#xff1a; 1.1 杀死docker有关的容器&#xff1a; docker kill $(docker ps -a -q)1.2 删除所有docker容器&#xff1a; docker rm $(docker ps -a -q)1.3 删除所有d…

简单走近ChatGPT

目录 一、ChatGPT整体背景认知 &#xff08;一&#xff09;ChatGPT引起关注的原因 &#xff08;二&#xff09;与其他公司的竞争情况 二、NLP学习范式的发展 &#xff08;一&#xff09;规则和机器学习时期 &#xff08;二&#xff09;基于神经网络的监督学习时期 &…

房产政策松绑,VR看房助力市场回春

近日房贷利率、房产限购开始松绑&#xff0c;房地产市场逐渐被激活&#xff0c;房产行业的线上服务能力&#xff0c;也愈发的受到了重视。随着房贷利率、首付比例变化的消息逐渐推出&#xff0c;部分用户开始入手房产市场&#xff0c;因此房产行业的线上服务也需要不断升级&…

leetCode 122.买卖股票的最佳时机 II 贪心算法

122. 买卖股票的最佳时机 II - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&…

gitlab配置webhook限制提交注释

一、打开gitlab相关配置项 vim /etc/gitlab/gitlab.rb gitlab_shell[custom_hooks_dir] "/etc/gitlab/custom_hooks" 二、创建相关文件夹 mkdir -p /etc/gitlab/custom_hooks mkdir -p /etc/gitlab/custom_hooks/post-receive.d mkdir -p /etc/gitlab/custom_h…

Python教程:PyQt5需要学习,哪些知识点??

PyQt5是基于图形程序框架Qt5的Python语言实现&#xff0c;由一组Python模块构成。它可用于Python 2和3&#xff0c;拥有超过620个类和6000个函数和方法。这是一个跨平台的工具包&#xff0c;可以运行在所有主要的操作系统&#xff0c;包括UNIX、Windows、Mac OS、Linux等。 #我…

vue3学习实战

vue3新增变化 diff算法变化 vue3的diff算法没有vue2的头尾、尾头之间的diff&#xff0c;对diff算法进行了优化&#xff0c;最长递归子序列。 ref VS reactive ref 支持所有的类型&#xff0c;reactive 支持引用类型&#xff0c;array object Map Setref取值、赋值&#xff…

步力宝科技爆款产品定位,开创智能物联网新商业

数据显示&#xff0c;中国处于 “亚健康”状态人口数量约占总人口的70%&#xff0c;亚健康是一种临界状态&#xff0c;指介于健康和疾病之间的状态。亚健康是一个动态演变的过程&#xff0c;既有向慢病发展的趋势&#xff0c;也能通过合理的干预使人体重返健康状态&#xff0c;…

奥斯卡·王尔德

奥斯卡王尔德 奥斯卡王尔德&#xff08;Oscar Wilde&#xff0c;1854年10月16日—1900年11月30日&#xff09;&#xff0c;出生于爱尔兰都柏林&#xff0c;19世纪英国&#xff08;准确来讲是爱尔兰&#xff0c;但是当时由英国统治&#xff09;最伟大的作家与艺术家之一&#xf…

【RuoYi项目分析】在RuoYi网关实现验证码功能

文章目录 1. 验证码功能的类清单2. 验证码的实现2.1. 验证码的获取2.2. 验证码的校验 3. 总结4. 资料参考 本文主要介绍了用户如何实现验证码&#xff0c;以及该功能如何与 Spring Gateway 联系起来。 1. 验证码功能的类清单 类功能CaptchaProperties验证码的 yml 配置Captcha…

25-多线程

多线程 线程(Thread)是一个程序内部的一条执行流程。 程序中如果有一条执行流程&#xff0c;那这个程序就是单线程的程序 多线程是指从软硬件上实现的多条执行流程的技术&#xff08;多条线程由CPU负责调度执行&#xff09;。 再例如&#xff1a;消息通信、淘宝、京东系统都离…

适配器模式详解和实现(设计模式 四)

适配器模式将一个类的接口转换成客户端所期望的另一个接口&#xff0c;解决由于接口不兼容而无法进行合作的问题。 设计基本步骤 1. 创建目标接口&#xff08;Target Interface&#xff09;&#xff0c;该接口定义了客户端所期望的方法。 2.创建被适配类&#xff08;Adaptee…

【Flink】

事件驱动型应用 核心目标&#xff1a;数据流上的有状态计算 Apache Flink是一个框架和分布式处理引擎&#xff0c;用于对无界或有界数据流进行有状态计算。 运行逻辑 状态 把流处理需要的额外数据保存成一个“状态”,然后针对这条数据进行处理,并且更新状态。这就是所谓的“…

c# 中的类

反射 Activator.CreateInstance class Program {static void Main(string[] args){//反射Type t typeof(Student);object o Activator.CreateInstance(t, 1, "FJ");Student stu o as Student;Console.WriteLine(stu.Name);//动态编程dynamic stu2 Activator.Cre…

MVVM框架下两窗口的消息传递

副窗口关闭的时候将bool类型传递出去 var message new CloseWindowMessage {MedicineView_DialogResult true }; //CloseWindowMessage是存储bool类型的标记类 Messenger.Default.Send(message); 主窗体中添加关闭处理的方法 private void HandleCloseWindowMessage(Clo…

在 Windows 终端运行已有的 Python 程序

在同一个路径下&#xff0c;输入全名&#xff0c;如图&#xff1a;

【Spring Cloud】Ribbon 实现负载均衡的原理,策略以及饥饿加载

文章目录 前言一、什么是 Ribbon二、Ribbon 实现负载均衡的原理2.1 负载均衡的流程2.2 Ribbon 实现负载均衡的源码剖析 三、Ribbon 负载均衡策略3.1 负载均衡策略3.2 演示 Ribbon 负载均衡策略的更改 四、Ribbon 的饥饿加载4.1查看 Ribbon 的懒加载4.2 Ribbon 的饥饿加载模式 前…

开放式耳机怎么选择、300之内最好的耳机推荐

开放式耳机凭借不入耳、不伤耳、安全更舒适的佩戴体验&#xff0c;得到了越来越多音乐爱好者和专业人士的青睐。开放式耳机不需要插入耳道&#xff0c;在佩戴时可以更加自然和轻松&#xff0c;减少了长时间佩戴引起的不适感&#xff0c;而且不会完全隔绝外界声音&#xff0c;用…

【VIM】VIM配合使用的工具

6-1 课程总结-vim虐我千百遍&#xff0c;我待 vim 如初恋_哔哩哔哩_bilibili

Koa处理请求数据

在开发中&#xff0c;后端接收到请求参数后&#xff0c;需要解析参数。请求分为很多种类型&#xff0c;比如常见的get和post。 请求参数 Koa本身可以解析get请求参数&#xff0c;不能解析post请求参数。例如&#xff1a; router.get(/api/get/userInfo, async (context) >…