掘金 后端 ( ) • 2024-05-08 00:29

大家好,我是G探险者。

今天主要介绍一下MultiValueMap和HashMap的区别。

事情起因是这样的

image.png

在我们项目code review的时候,客户方提了一个问题,说,你们在用restTemplate进行远程调用的时候,为啥使用MultiValueMap来传递参数,而不用HashMap。 这有啥区别么?

image.png

当时没有给出专业的解答。事后我就好好查询了一番。

从以下几个方面来阐述。

1. MultiValueMap 和 HashMap的区别?

MultiValueMapHashMap在数据结构上有一些差异,这导致了它们对待键值对的方式不同,从而使得MultiValueMap可以支持一个键对应多个值,而HashMap则不支持。

MultiValueMap:

MultiValueMap是Spring Framework中的接口,它的实现类通常是LinkedMultiValueMap。它的实现方式是每个键可以映射到一个值的列表,而不是单个值。这样就允许一个键拥有多个值。

HashMap:

HashMap在jdk<=1.7是基于数组+链表 在jdk>=1.8是数据+链表+红黑树,每个键值对映射到一个唯一的键上。因此,当你向HashMap中添加一个已经存在的键时,新值会覆盖旧值,而不是追加在旧值的后面。这就是为什么HashMap不支持一个键对应多个值的原因。

对比:

  • 存储方式MultiValueMap采用列表来存储值,一个键可以对应多个值;而HashMap每个键只能对应一个值,不支持多值。
  • 数据结构MultiValueMap内部使用了链表或者类似结构,以便存储多个值;HashMap内部使用数组+链表(或者红黑树),每个键对应一个唯一的哈希值。
  • 使用场景MultiValueMap适用于需要存储多个值的情况,比如HTTP请求参数;HashMap适用于需要唯一键值对的情况,比如建立字典。

下面是一个对比MultiValueMapHashMap的区别的表格:

特性 MultiValueMap HashMap 数据结构 键对应值的列表 哈希表 是否支持多值 是 否 用途 表单数据、HTTP请求参数 通用键值对存储 处理重复键 支持,每个键可以对应多个值 后添加的值会覆盖之前的值 参数编码 无需手动编码,自动处理 需要手动进行URL编码 框架集成 Spring框架中常用 通用 示例代码 MultiValueMap<String, List<String>> HashMap<String, String>

通过这个表格,可以清晰地了解到MultiValueMapHashMap在数据结构、支持多值、参数编码、框架集成等方面的区别。MultiValueMap更适用于处理需要支持多值的情况,特别是在Spring框架中;而HashMap适用于一般的键值对存储。

综上所述,虽然MultiValueMapHashMap都用于存储键值对,但它们的底层数据结构和对键值对的处理方式不同,导致了它们在支持一个键对应多个值这个特性上的差异。

了解了以上、MultiValueMap 和 HashMap在数据结构上的差异,我们再来讨论下面的问题

image.png

2. form表单提交的请求类型x-www-form-urlencoded为啥使用MultiValueMap来传递参数更合适?

在使用HttpClient发送x-www-form-urlencoded类型的请求时,通常需要使用MultiValueMap来传递参数,这是因为x-www-form-urlencoded格式要求参数以键值对的形式进行传递,而且在同一个键名下可能存在多个值。MultiValueMap是Spring Framework中的一种数据结构,用于表示键值对,并且支持一个键对应多个值的情况。

总结起来,主要有以下几个原因:

  • 多值支持: x-www-form-urlencoded格式要求同一个参数名可以对应多个值,而MultiValueMap正是为了支持这种情况而设计的。在HashMap中,相同的键只能对应一个值,如果需要传递多个值,你可能需要自己手动处理这种情况,而使用MultiValueMap则可以直接支持多值。

  • 参数编码: x-www-form-urlencoded格式要求参数需要进行URL编码,以确保特殊字符正确传输。Spring框架中的MultiValueMap会自动处理这种编码,确保参数在传输过程中不会出现问题。如果使用HashMap,你需要自己编写代码来手动进行参数的编码,这样就增加了额外的工作量和复杂度。

  • 框架集成: 在Spring框架中,很多API都会接受MultiValueMap类型的参数,因此使用MultiValueMap能够更好地与Spring的其他功能集成。例如,Spring提供了RestTemplate类来发送HTTP请求,该类的一些方法接受MultiValueMap类型的参数,这样你可以直接传递参数而无需进行额外的转换。

综上所述,虽然你可以使用HashMap来传递参数,但是为了更好地符合x-www-form-urlencoded格式的要求,以及在Spring框架中更好地与其他功能集成,推荐使用MultiValueMap来传递参数。

使用MultiValueMap能够更方便地构建这种参数形式,确保参数按照x-www-form-urlencoded格式正确编码。另外,Spring框架提供了一些便捷的方法来构建和处理MultiValueMap,使得参数的处理更加简单和灵活。

下面我分别从代码层面来阐述一下,使用MultiValueMap和使用HashMap进行传值的区别

image.png

2.1 使用 MultiValueMap:

import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

// 创建 MultiValueMap 对象
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();

// 添加参数到 MultiValueMap
params.add("key1", "value1");
params.add("key2", "value2");
params.add("key2", "value3"); // 添加同名参数,支持多值

// 使用 MultiValueMap 发送请求
// 假设 HttpClient 已经实例化为 httpClient
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> response = httpClient.exchange(url, HttpMethod.POST, requestEntity, String.class);

2.2 使用 HashMap:

import java.util.HashMap;
import java.util.Map;

// 创建 HashMap 对象
Map<String, String> params = new HashMap<>();

// 添加参数到 HashMap
params.put("key1", "value1");
params.put("key2", "value2");
params.put("key2", "value3"); // 重复的键会覆盖之前的值

// 构建 x-www-form-urlencoded 格式的参数字符串
StringBuilder requestBody = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
    if (requestBody.length() > 0) {
        requestBody.append("&");
    }
    requestBody.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
    requestBody.append("=");
    requestBody.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
}

// 发送请求
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> requestEntity = new HttpEntity<>(requestBody.toString(), headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

从以上两个示例代码我们可以清楚的看到如下区别:

2.3 区别:

  1. 多值支持MultiValueMap支持一个键对应多个值,而HashMap不支持,需要手动处理。

  2. 编码处理MultiValueMap会自动处理参数值的URL编码,而HashMap需要手动编码。

  3. 代码复杂度:使用MultiValueMap更简洁,不需要手动构建参数字符串。

总的来说,使用MultiValueMap更方便、更符合Spring框架的习惯,而使用HashMap则需要手动处理更多的细节。

如果喜欢的话,请点个关注哦~

image.png