场景
最近项目上遇到一个需求:运营后台设置商品分子分类组合查询条件,前端APP由子分类进入展示商品列表。
其中有一种查询条件是在后台添加或导入商品,商品可指定展示的排序,排序号可以重复,也可不设置排序。
商品需满足特定条件才展示,如定位的门店有库存且上架,即可能后台设置了10个商品,但只有5个商品满足展示条件,也可能都不满足条件都不展示。
满足上架条件通过ES查询DSL来过滤,后台配置的打分规则用于ES排序,后台设置的商品排序在接口里由Java代码来实现。
前端APP展示满足条件可展示的商品列表,如果设置了排序号按从小到大排序,如果排序号重复按后台列表展示的顺序展示,如果没有设置排序按ES查询里的打分排序。
如后台设置了如下商品:
商品1008,无排序
商品1001,排序1
商品1002,排序6
商品1003,排序5
商品1004,排序3
商品1005,排序3
商品1006,排序4
商品1007,排序2
商品1009,无排序
只有商品1002,1003,1004,1005,1007满足展示条件,那么展示的列表应为:
商品1007,排序2
商品1004,排序3
商品1005,排序3
商品1003,排序5
商品1002,排序6
商品1008,无排序
商品1009,无排序
注:因商品1004,1005排序值相同都为3,按后台展示的顺序展示,1004排前面;商品1008,1009没有设置排序,排在列表的最后面。
实际项目中运营后台设置按商品编码的组合条件为json格式,如:{"productCodes":"1001-1,1002,1003-2"},
通过构建ES查询条件查询ES,在由Java代码里按运营后台的设置来排序,最后通过redis查询商品库存、价格等构建商品列表数据。
这里我们主要讨论Java的排序,因此忽略ES查询和构建商品数据,将需求描述简化后,用代码模拟如下:
// 运营后台已添加且满足可展示条件的商品编码列表,注:1008,1009后台添加了商品但为设置排序号
List<String> productCodes = new ArrayList<>();
productCodes.add("1008");
productCodes.add("1002");
productCodes.add("1003");
productCodes.add("1004");
productCodes.add("1005");
productCodes.add("1007");
productCodes.add("1009");
// 运营后台设置的商品排序map,key:商品编码,value:排序号
Map<String, Integer> productSortMap = new LinkedHashMap<>();
productSortMap.put("1001", 1);
productSortMap.put("1002", 6);
productSortMap.put("1003", 5);
productSortMap.put("1004", 3);
productSortMap.put("1005", 3);
productSortMap.put("1006", 4);
productSortMap.put("1007", 2);
思路
- 遍历productCodes列表,如果设置了排序值在列表中去掉,并且构建一个map保存
- 通过该map将不满足可展示条件的商品编码在运营后台设置的商品排序map中去掉
- 运营后台设置的商品排序map按value排序,加到productCodes列表的最前面
实现
// 遍历productCodes列表,将设置了排序值的商品构建一个map保存,并在列表中去掉该商品
Map<String, Integer> canDisplayProductSortMap = null;
Iterator<String> iterator = productCodes.iterator();
while (iterator.hasNext()) {
String productCode = iterator.next();
// 在运营后台没有设置排序的跳过处理
if (!productSortMap.containsKey(productCode)) {
continue;
}
// 懒汉模式创建map,当都没设置排序时不用创建map
if (canDisplayProductSortMap == null) {
canDisplayProductSortMap = new LinkedHashMap<>();
}
canDisplayProductSortMap.put(productCode, productSortMap.get(productCode));
// 将设置了排序的商品编码在列表里去掉
iterator.remove();
}
if (canDisplayProductSortMap != null) {
Map<String, Integer> finalCanDisplayProductSortMap = canDisplayProductSortMap;
// 通过该map将不满足可展示条件的商品编码在运营后台设置的商品排序map中去掉
productSortMap.entrySet().removeIf(entry -> !finalCanDisplayProductSortMap.containsKey(entry.getKey()));
// 运营后台设置的商品排序map按value排序,加到productCodes列表的最前面
productCodes.addAll(0, productSortMap.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).collect(Collectors.toList()));
// 打印最终排序好的商品编码列表,输出为:[1007, 1004, 1005, 1003, 1002, 1008, 1009]
System.out.println(productCodes);
} else {
// 如果可展示的商品在后台都没有设置排序,则无需处理
System.out.println(productCodes);
}
总结
- list可通过
list.iterator()
遍历支持在遍历中删除数据 - map可通过
map.entrySet().stream().sorted(Map.Entry.comparingByValue())
按value值排序
参考
- java8 实现map以value值排序操作 https://www.yht7.com/news/126068
- Java Map实现按value从大到小排序 https://www.cnblogs.com/zzlback/p/12381881.html
- Java8 让代码更优雅之List排序 https://my.oschina.net/sdlvzg/blog/2243766