LinkedHashMap 一些示例、BigDecimal
BigDecimal:Java 专门用来存金额、小数、财务数字的类型
(double/float会出现精度丢失,算钱绝对不能用,所以营收、订单金额一律用BigDecimal)
BigDecimal.ZERO:BigDecimal类自带的静态常量,代表数字 0,是已经提前创建好的0对象。
等价于:new BigDecimal(0),但好处:
- 不用每次new新对象,节省内存;
- 代码规范、可读性强,开发统一写法。
配套常用三个常量:
BigDecimal.ZERO→ 0BigDecimal.ONE→ 1BigDecimal.TEN→ 10
LinkedHashMap 实战场景案例
先牢记两个核心特性:
- 默认:插入有序(put先后顺序,遍历就什么顺序)
- 传第三个参数true:访问有序(get/put过后元素排末尾,做LRU缓存专用)
一、简单场景(日常写接口天天碰到)
案例1:下拉枚举字典,固定顺序展示(最常用)
需求:前端页面角色下拉框,必须按顺序:超级管理员 → 运营 → 财务 → 普通用户
HashMap存放遍历顺序随机,前端下拉选项顺序会乱,必须用LinkedHashMap
publicMap<String,Integer>getRoleOptions(){Map<String,Integer>roleMap=newLinkedHashMap<>();// 按展示顺序依次存入roleMap.put("超级管理员",1);roleMap.put("运营人员",2);roleMap.put("财务人员",3);roleMap.put("普通用户",4);returnroleMap;}返回给前端JSON,key-value永远保持存入顺序,下拉列表不会错乱。
案例2:IN查询,自定义ID顺序要保留
需求:前端勾选了一批订单ID:105, 22, 78, 9,要查询这几个订单详情,返回列表严格按照这个ID顺序排列
MySQL执行select * from order where id in (105,22,78,9)
数据库查询出来的数据顺序是主键从小到大,不会遵从你in括号里的顺序。
解决办法:
- 先把前端传来的ID数组遍历;
- 循环查询到的订单,以id为key放入LinkedHashMap;
- 最终遍历map,顺序完全和前端勾选顺序一致。
// 前端传来的顺序数组List<Long>idList=Arrays.asList(105L,22L,78L,9L);// 数据库查出所有符合条件的订单(顺序乱的)List<Order>dbOrderList=orderMapper.selectBatchIds(idList);// 1. 存入HashMap做快速查找Map<Long,Order>tempMap=newHashMap<>();for(Ordero:dbOrderList){tempMap.put(o.getId(),o);}// 2. 按照前端原始顺序放入LinkedHashMap,保证最终有序Map<Long,Order>resultMap=newLinkedHashMap<>();for(Longid:idList){if(tempMap.containsKey(id)){resultMap.put(id,tempMap.get(id));}}// 遍历resultMap:105 → 22 → 78 → 9 严格有序案例3:导出Excel,表头列固定先后顺序
导出Excel表格,表头列:序号、姓名、手机号、地址、创建时间
表头集合必须固定顺序,用 LinkedHashMap 存储 表头key 和 列宽,导出时循环遍历绘制列,列不会颠倒。
二、中等复杂度场景
BigDecimal:Java 专门用来存金额、小数、财务数字的类型
(double / float 会出现精度丢失,算钱绝对不能用,所以营收、订单金额一律用 BigDecimal )
BigDecimal.ZERO:BigDecimal类自带的静态常量,代表数字 0,是已经提前创建好的0对象。
等价于:new BigDecimal(0),但好处:
- 不用每次new新对象,节省内存;
- 代码规范、可读性强,开发统一写法。
配套常用三个常量:
BigDecimal.ZERO→ 0BigDecimal.ONE→ 1BigDecimal.TEN→ 10
案例4:接口多维度统计数据,按时间段有序返回
需求:统计近7天每日营收,key是日期字符串2026-07-24 ~ 2026-07-30,返回前端必须从早到晚依次排列
方案:
- 先循环生成7天日期,依次put进LinkedHashMap,初始营收0;
- 用SQL查出每一天实际营收,回填map;
最终遍历出来天然是时间先后顺序,不用额外排序。
// 初始化7天时间有序容器Map<String,BigDecimal>dayIncome=newLinkedHashMap<>();dayIncome.put("2026-07-24",BigDecimal.ZERO);dayIncome.put("2026-07-25",BigDecimal.ZERO);dayIncome.put("2026-07-26",BigDecimal.ZERO);dayIncome.put("2026-07-27",BigDecimal.ZERO);dayIncome.put("2026-07-28",BigDecimal.ZERO);dayIncome.put("2026-07-29",BigDecimal.ZERO);dayIncome.put("2026-07-30",BigDecimal.ZERO);// 只查出有营收的那些天数据List<DayStat>statList=statMapper.select7DayIncome();// 循环把查到的真实营收回填到map中,覆盖原来的0for(DayStatitem:statList){// key:日期 value:当日收入dayIncome.put(item.getDay(),item.getIncome());}// 返回前端,日期永远从前往后排序查询结果: 假设数据库只查到3天有收入:24号:100元、26号:200元、30号:500元 循环回填之后map内部数据:1.2026-07-24→1002.2026-07-25→03.2026-07-26→2004.2026-07-27→05.2026-07-28→06.2026-07-29→07.2026-07-30→500对比:用HashMap的话日期顺序完全打乱,前端还要自己排序。
案例5:接口日志参数记录,保存请求参数插入顺序
接收前端表单一堆参数:姓名、年龄、住址、邮箱、备注
需要把所有参数原样有序存储到日志里,方便排查问题,用LinkedHashMap接收请求参数,保留用户提交顺序。
三、复杂场景(框架底层、缓存开发、面试高频)
案例6:自定义本地LRU内存缓存(最经典用途)
需求:本地缓存热门商品详情,最多存放10条数据;
超过10条时,自动删掉最久没有访问过的商品,释放内存。
依靠 LinkedHashMap 访问顺序特性 + 重写淘汰规则实现,不用自己维护链表:
/** * LRU本地缓存:最多存10个元素 */publicclassGoodsCacheextendsLinkedHashMap<Long,Goods>{// 初始容量、负载因子、true=开启访问排序publicGoodsCache(){super(16,0.75f,true);}// 重写方法:返回true代表删除最久未使用的元素@OverrideprotectedbooleanremoveEldestEntry(Map.Entry<Long,Goods>eldest){// 缓存容量大于10,触发淘汰returnsize()>10;}}使用:
GoodsCachecache=newGoodsCache();// 存入15个商品,自动删掉最早没访问的5个for(longi=1;i<=15;i++){cache.put(i,newGoods(i,"商品"+i));}cache.get(3L);// 访问3号商品,3被挪到链表末尾,最晚被淘汰适用场景:
- 小型项目无Redis,本地临时缓存热点数据;
- Redis底层、MyBatis二级缓存底层都大量借鉴这个思路。
案例7:路由规则有序匹配(权限系统场景)
后台权限拦截器,配置多条URL拦截规则,需要从上到下依次匹配:
- /api/login 放行
- /api/register 放行
- /api/admin/** 需要管理员权限
- /** 需要登录认证
规则有优先级顺序,必须从上往下遍历匹配,HashMap无序无法实现,只能LinkedHashMap存储规则。
四、哪些场景绝对不要用 LinkedHashMap
- 单纯存取数据、不需要任何顺序:HashMap性能更高,内存开销更小;
- 需要根据key大小升序/降序排列:TreeMap;
- 多线程并发读写缓存:线程不安全,要用 ConcurrentHashMap;
- 百万级大数据存储:不适合放内存。
一句话选型口诀
要保留存入顺序、遍历顺序固定 → LinkedHashMap
其余普通存取 → HashMap
需要自动按键排序 → TreeMap
多线程并发 → ConcurrentHashMap