最近公司新来了一个小伙伴,问了磊哥一个比较“奇怪”的问题,这个问题本身的难度并不大,但比较“隐蔽”,那究竟是什么问题呢?接下来我们一起来看。

起因

最近公司的系统要增加一个新的列表展示功能,功能本身难度并不大,但遇到了一个很“可怪”的问题。小伙伴在执行查询列表时,明明已经使用了 order by 进行排序了,但最终查询出来的数据却还是乱的。

预期中的(正确)结果:



现实中的(非预期)结果:



那到底是哪里出现了问题呢?

问题展示

为了方便展示,我把复杂的业务程序简化成了以下代码:

import java.util.HashMap;

public class App {

    public static void main(String[] args) {
HashMap<String, Object> result = getList();
result.forEach((k, v) -> {
System.out.println(k + ":" + v);
});
} // 查询方法(简化版)
public static HashMap<String, Object> getList() {
HashMap<String, Object> result = new HashMap<>(); // 最终返回的结果集
// 伪代码:从数据库中查询出了数据,然后对数据进行处理之后,存到了
for (int i = 1; i <= 5; i++) {
result.put("2022-10-" + i, "hello java" + i);
}
return result;
}
}

以上程序的执行结果如下:

预期的结果应该是按时间的先后顺序展示的,如下图所示:

PS:以上示例代码中,插入元素的顺序是有序的(从 1 到 5),相当于实际业务场景中的 order by。

原因分析

既然原数据使用了 order by 排序,那么原数据肯定是没问题的,那问题就只会出现在返回集 HashMap 上,然后我们再把焦点放到 HashMap 上, 瞬间醒悟,哦,原来如此。HashMap 使用的是哈希方式进行存储的,因此存入和读取的顺序可能是不一致的,这也说 HashMap 是无序的集合,所以会导致插入的(或 order by 的)顺序,与最终展示的顺序不一致。

解决方案

经过上面的分析我们顺利找到了问题,那接下来就是制定相应的解决方案了,我想到的解决方案有两个:

  1. 稍微麻烦一点但正确的解决方案:将返回的不确定数据类型 HashMap 改为确定的数据类型,比如 List;
  2. 简单一点但并不是最优的解决方案:将无序的 HashMap 改为有序的 LinkedHashMap,此方案的优点是,只需要改动一个单词就可以解决整个问题了。

第一种解决方案大家都懂这里就不演示了,接下来咱们使用第二种解决方案将上面的问题改造一下,最终的实现代码如下:

import java.util.HashMap;
import java.util.LinkedHashMap; public class App { public static void main(String[] args) {
HashMap<String, Object> result = getList();
result.forEach((k, v) -> {
System.out.println(k + ":" + v);
});
} // 查询方法(简化版)
public static HashMap<String, Object> getList() {
HashMap<String, Object> result = new LinkedHashMap<>(); // 最终返回的结果集
// 伪代码:从数据库中查询出了数据,然后对数据进行处理之后,存到了
for (int i = 1; i <= 5; i++) {
result.put("2022-10-" + i, "hello java" + i);
}
return result;
}
}

以上程序的执行结果如下:



从上述结果可以看出,当使用 LinkedHashMap 替代了 HashMap 之后,返回的顺序就能和插入的顺序保持一致了。

LinkedHashMap 的魔力

为什么 HashMap 是无序的,而 LinkedHashMap 却是有序的呢?

这要从二者的实现说起了,LinkedHashMap 属于 HashMap 的子类,所以 LinkedHashMap 除了拥有 HashMap 的所有特性之后,还具备自身的一些扩展属性,其中就包括 LinkedHashMap 中额外维护了一个双向链表,这个双向链表就是用来保存元素的(插入)顺序的,这也是为什么 LinkedHashMap 可以实现访问顺序和插入顺序一致的原因了。

总结

本文演示了 HashMap 作为返回类型时隐藏的一个小“坑”,因为 HashMap 本身是无序的,所以它会导致查询顺序和插入顺序不一致的问题,对应的解决方案有两种:使用确定的数据类型来替代 HashMap,比如 List,或者使用有序的 LinkedHashMap 来替代无序的 HashMap。

关注公众号「Java中文社群」查看更多 Java 总结性系列文章。

HashMap 中的一个“坑”!的更多相关文章

  1. andriod8.1.0源码编译中的一个坑-package com.sun.javadoc does not exist

    这里记录编译过程中的一个坑!!! 编译过程中出现了下面的报错 external/doclava/src/com/google/doclava/ClassInfo.java:20: error: pac ...

  2. 记前端状态管理库Akita中的一个坑

    记状态管理库Akita中的一个坑 Akita是什么 Akita是一种基于RxJS的状态管理模式,它采用Flux中的多个数据存储和Redux中的不可变更新的思想,以及流数据的概念,来创建可观察的数据存储 ...

  3. Spring Boot 计划任务中的一个“坑”

    计划任务功能在应用程序及其常见,使用Spring Boot的@Scheduled 注解可以很方便的定义一个计划任务.然而在实际开发过程当中还应该注意它的计划任务默认是放在容量为1个线程的线程池中执行, ...

  4. xcode中得一个坑

    因项目需求变动,我必须在coredata中的WorkLogModel表中添加一个字段:抄送人.起初我给这个字段起名为copyPerson,一切准备就绪后,发现从数据库读取这个copyPerson时,第 ...

  5. PHP中的一个”坑“

    说一个极有可能在工作中遇到的问题——foreach的引用 foreach $arr = range(1,3); //[1,2,3] foreach($arr as &$val) { } for ...

  6. 关于abp中使用的sweetalert对话框组件的confirm确认对话框中的一个坑

    今天修改了一个功能,限制删除用户,在删除的时候不满足条件的时候提示用户原因,使用的sweet alert组件. abp框架前端集成了sweet alert 对http请求的error做了全局处理,我在 ...

  7. 在Java中==的一个坑

    观察下面代码,输出结果是什么? public static void main(String[] args) { Integer p = 10000; Integer q = 10000; Syste ...

  8. mysql 线上not in查询中的一个坑

    今天早上开发又过来说,怎么有个语句一直没有查询出结果,数据是有的呀,并发来了如下的sql(为了方法说明,表名及查询均做了修改): select * from t2 where t2.course no ...

  9. Makefile中的一个坑

    问题描述:Makefile中,我想将一个变量的后缀全部进行替换,如将所有的.c后缀变成.d后缀 方法:$(CUR_SOURCE: .c = .d ) 说明:查阅相关资料,了解到上述这种语法就可以将所有 ...

随机推荐

  1. 彻底搞明白PHP的中引用的概念

    之前我们其实已经有过几篇文章讲过引用方面的问题,这次我们来全面的梳理一下引用在PHP到底是怎么回事,它和C中的指针有什么不同,在使用的时候要注意些什么. 什么是引用? 在 PHP 中引用意味着用不同的 ...

  2. Jmeter系列(18)- 八大组件

    说明 取样器(Sampler):不与其他元件发⽣交互的作⽤的元件 逻辑控制器(Logic Controller):只对其⼦节点的sampler有效,⽽其他元件需要与sampler等元件交互 前置处理器 ...

  3. MySQL update 链表 (一个表数据当做条件错误时候的转换)

    MySQL语句一: UPDATE lesson_p set solve_status = 6 where lesson_p_id in (SELECT lp.lesson_p_id FROM `les ...

  4. 使用php函数 json_encode ,数据存入mysql

    $data = json_encode($array); // 过滤 $data = addslashes($data); // 插入数据库 $db->insert($table_name,ar ...

  5. npm卸载appium,重新安装桌面版appium

    大家好,appium环境搭建的时候,我选的是使用npm安装appium,但我在使用过程中,发现这样安装的appium没有界面,都是需要通过命令行操作,对于我来说,有点吃力.最后,还是觉得初学就先用桌面 ...

  6. P2350-[HAOI2012]外星人【线性筛】

    正题 题目链接:https://www.luogu.com.cn/problem/P2350 题目大意 给出\(N\)质因数分解之后的结果,求每次\(N=\varphi(N)\),多少次后\(N=1\ ...

  7. 域名系统-DNS

    域名系统DNS 域名系统DNS(Domain Name System)是互联网使用的命名系统,用来把便于人们使用的机器名转化为IP地址,域名系统就是名字系统. 很多应用层的软件经常直接使用DNS.DN ...

  8. C#开发BIMFACE系列42 服务端API之图纸对比

    BIMFACE二次开发系列目录     [已更新最新开发文章,点击查看详细] 在我的前一篇博客<C#开发BIMFACE系列42 服务端API之图纸对比>中详细介绍了BIMFACE服务端接口 ...

  9. 深入剖析 Spring WebFlux

    一.WebFlux 简介 WebFlux 是 Spring Framework5.0 中引入的一种新的反应式Web框架.通过Reactor项目实现Reactive Streams规范,完全异步和非阻塞 ...

  10. 苹果应用内购 ios 开发者根据用户提供的邮件中的订单号查看该订单是否支付成功

    苹果应用内购 ios 开发者根据用户提供的邮件中的订单号查看该订单是否支付成功 这是苹果wwdc2021 推出的新功能 参考官网链接 App Store Server API | Apple Deve ...