如何对PriceRow列表按productCode分组后排序并取首元素?
嘿,你的现有方案已经能很好地解决问题了!不过咱们可以借助Java Stream的排序API来简化代码,让整个逻辑更紧凑、可读性更强,一起来梳理下并看看优化方案吧~
问题背景
我们有一批PriceRow(productCode, key, sector)类型的对象列表:
List<PriceRow> priceRowList = new ArrayList<>(); priceRowList.add(new PriceRow("10kgbag","", "SECTOR")); priceRowList.add(new PriceRow("10kgbag","12345", "")); priceRowList.add(new PriceRow("10kgbag","", "")); priceRowList.add(new PriceRow("20kgbag","", "SECTOR")); priceRowList.add(new PriceRow("20kgbag","12345", "")); priceRowList.add(new PriceRow("20kgbag","", "")); priceRowList.add(new PriceRow("30kgbag","", "SECTOR")); priceRowList.add(new PriceRow("30kgbag","", "")); priceRowList.add(new PriceRow("40kgbag","", "")); priceRowList.add(new PriceRow("50kgbag","", ""));
核心需求
按productCode对列表分组,分组内的排序优先级为:
key非空的元素优先- 若
key都为空,则sector非空的元素优先 - 最后选取
key和sector都为空的元素
最终取每组排序后的首个元素生成Map<String, PriceRow>
预期断言验证
assertEquals("12345",map.get("10kgbag").getFlightKey()); assertEquals("12345",map.get("20kgbag").getFlightKey()); assertEquals("SECTOR",map.get("30kgbag").getSector()); assertEquals("",map.get("40kgbag").getFlightKey()); assertEquals("",map.get("50kgbag").getFlightKey());
现有解决方案
import org.apache.commons.lang.StringUtils; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Example { public Map<String,PriceRow> evaluate(List<PriceRow> priceRowList) { Map<String,PriceRow> map = priceRowList.stream() .collect(Collectors.groupingBy(priceRow -> priceRow.getProductCode(), Collectors.collectingAndThen(Collectors.toList(), value -> getMostEligibleValue(value)))); return map; } private PriceRow getMostEligibleValue(List<PriceRow> priceRowList){ for(PriceRow priceRowWithKey : priceRowList) if(StringUtils.isNotBlank(priceRowWithKey.getKey())) return priceRowWithKey; for(PriceRow priceRowWithSector : priceRowList) if(StringUtils.isNotBlank(priceRowWithSector.getSector())) return priceRowWithSector; return priceRowList.stream().findFirst().get(); } }
更优解决方案
我们可以直接在Stream流水线中通过自定义排序逻辑实现需求,避免额外的循环方法,让逻辑更连贯:
import org.apache.commons.lang.StringUtils; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Example { public Map<String, PriceRow> evaluate(List<PriceRow> priceRowList) { // 定义排序规则:key非空优先 → sector非空优先 Comparator<PriceRow> eligibilityComparator = Comparator .comparing((PriceRow pr) -> StringUtils.isBlank(pr.getKey())) // key非空的元素会返回false,排序更靠前 .thenComparing(pr -> StringUtils.isBlank(pr.getSector())); // 同理,sector非空的元素更靠前 return priceRowList.stream() .collect(Collectors.groupingBy( PriceRow::getProductCode, // 分组后按规则排序,取优先级最高的第一个元素 Collectors.collectingAndThen( Collectors.minBy(eligibilityComparator), // 业务场景下分组不会为空,用orElseThrow抛出明确异常替代get() opt -> opt.orElseThrow(() -> new IllegalArgumentException("Empty group for product code")) ) )); } }
优化点说明
- 逻辑更紧凑:把排序和选取逻辑整合到Stream流水线中,无需额外遍历方法
- 可读性更强:通过
Comparator链式调用清晰表达排序优先级,规则一目了然 - 更贴合Stream风格:利用
Collectors.minBy获取排序后的首个元素(符合条件的元素比较值更小,所以min就是优先元素) - 安全性提升:用
orElseThrow替代get(),若出现空分组(业务中大概率不会)会抛出明确异常,避免模糊的NoSuchElementException
如果你的项目使用Java 11+,还可以用JDK自带的String.isBlank()替代Apache Commons的工具类,减少第三方依赖:
// Java 11+ 可使用的比较器 Comparator<PriceRow> eligibilityComparator = Comparator .comparing((PriceRow pr) -> pr.getKey().isBlank()) .thenComparing(pr -> pr.getSector().isBlank());
内容的提问来源于stack exchange,提问作者rowen
相关产品推荐
相关产品推荐

