总结一些java8常用表达式

lambda表达式和StreamAPI实践
java8是个创新的版本,带来很多有用多多特性,其中比较重要的特性就有Lambda表达式和StreamAPI,在过往多项目中,我们也在很多场景下都使用过它们。Lambda表达式特性赋予了java语言函数式编程的能力,让我们可以用简洁优美的语法完成一个功能。概念和原理就不介绍了,下面我们将直接演示我们在项目中会涉及的实践例子。

  1. 对List数据进行数据筛选
    从商品列表获取单个值, 注意findFirst取得的对象要做null值检查
Goods goods = goodsList.stream().filter(x->Objects.equals(x.getGoodsId,1L)).findFirst().orElse(null);
if(goods == null)
{
    throw new ShopException();
}

还有一种做法是返回一个可选的Optional对象,Optional也是java8引入的新特性,然后通过get方法拿到我们想要的结果。

Optional<Goods> goodsOptional = goodsList.stream().filter(x->Objects.equals(x.getGoodsId,1L)).findFirst();
if(goodsOptional.isPresent())
{
   Goods goods = goodsOptional.get();
}else{
    //do something
}

注意下两种情况的不同处理方式,第一种为null直接抛出了异常,表示对象是@NotNull的,第二种不管对象是不是null都有对应的处理逻辑,表示对象是@Nullable的。

  1. 数据类型转换
    从订单商品列表的获取商品id列表示例:
List<Integer> commonIdList = orderGoodsList.stream().map(x->x.getCommonId()).distinct().collect(Collectors.toList());

注意因为订单商品列表中的商品是可以重复的,所以要使用distinct()来去重。

对象数据类型转换

 List<String> list = Arrays.asList("1","2","3");
 List<Integer> list1 = list.stream().map(Integer::valueOf).collect(Collectors.toList());
list1.forEach(System.out::println);

注意两种map使用方式的区别,本质上都是函数接口,x->x是一个Lambda表达式,Integer::valueOf是一个方法引用,idea会推荐你把Lambda表达式进一步简化为方法引用。

  1. 数值流求和
    获取商品购买数量合计示例:
List<Integer> sum = orderGoodsList.stream().mapInt(x->x.getOrdersGoods().buyNum()).sum();

这个用到了一个mapInt数值流转换方法,类似的还有mapToInt、mapToDouble、mapToLong,数值流汇总方法还有max()、min()、average()、count()等,用法都差不多。

  1. 去除流中的重复元素
    这个方法是通过对象的 equals 方法来判断两个元素是否相等的。

简单类型去重

List<String> list = Arrays.asList("1","2","3");
List<String> listDistinct = list.stream().distinct().collect(Collectors.toList());
listDistinct.forEach(System.out::println);

对象列表去重

需要先重写对象的hashCode()和equals()。

public class Member {
    private String memberName = "";
    private int age;
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Member)) {
            return false;
        }
        Member member = (Member) o;
        if (age != member.age) {
            return false;
        }
        return memberName.equals(member.memberName);
    }
    @Override
    public int hashCode() {
        int result = age;
        result = 31 * result + memberName.hashCode();
        return result;
    }
}

去重演示

 List<Member> list = new ArrayList<>();
        {
           list.add(new Member("小明", 15));
           list.add(new Member("小明", 15));
           list.add(new Member("小李", 26));          
           list.add(new Member("小李", 26));
           list.add(new Member("大黄", 30));
        }
        long l = list.stream().distinct().count();
        System.out.println("No. of distinct members:"+l);
        list.stream().distinct().forEach(b -> System.out.println(b.getMemberName()+ "," + b.getAge()));

复杂类型去重

distinct()不提供按照属性对对象列表进行去重的直接实现。如果我们想要按照对象的属性,对对象列表进行去重,我们可以通过其它方法来实现。如下代码段所示:

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
}
users.stream().filter(distinctByKey(users::getName)).collect(Collectors.toList());
  1. 对列表进行排序
    简单类型正向排序
List<String> list = Arrays.asList("1","2","3");
List<String> listDistinct = list.stream().sorted().collect(Collectors.toList());
listDistinct.forEach(System.out::println);

简单类型逆向排序

List<String> list = Arrays.asList("1","2","3");
List<String> listDistinct = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
listDistinct.forEach(System.out::println);

复杂类型正向排序

首先使用 name 排序,紧接着在使用ege 排序,来看下使用效果

users.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getName)).collect(Collectors.toList())
users.forEach(System.out::println);

复杂类型逆向排序

users.stream().sorted(Comparator.comparing(User::getAge).reversed().thenComparing(User::getName)).collect(Collectors.toList())
users.forEach(System.out::println);
  1. 综合案例
    业务场景:对用户购买的订单商品按照日期、店铺和区域分组统计。

此案例演示了通过sorted和Comparator.comparing方法对列表进行排序,collect和Collectors.groupingBy方法对订单商品数据进行分组,然后实现商品字段的行转列。

数据库中原始数据如下:

日期 店铺 配送区域 商品名称 购买数量
2018-12-11 浦东店 浦东 鸭蛋 123
2018-12-11 浦东店 浦东 鸡蛋 123
2018-12-11 浦东店 浦东 鹅蛋 123
2018-12-11 浦东店 杨浦 鸭蛋 533
2018-12-11 浦东店 虹口 鸭蛋 533
2018-12-11 青浦店 宝山 鸭蛋 533
2018-12-11 青浦店 静安 鸭蛋 533
对于多条件的分组,最简单的方式就是把多个字段组合成一个key来分组。

Map<String, List<StatOrdersGoodsDayVo>> getListByGroup =
                statDaysListCurr.stream().sorted(Comparator.comparing(StatOrdersGoodsDayVo::getCreateTimeDate))
                        .collect(Collectors.groupingBy(x-> ShopHelper.formatTimestamp(x.getCreateTimeDate(),"yyyy-MM-dd")+"_"+x.getStoreName()+"_"+x.getAreaName()));

通过分组还可以实现对数据表行转列,这上面的例子中,原始数据是订单商品数表,即每行都是一个商品记录,通过分组我们实现了日期、店铺、区域、商品列表的数据结构,在对商品列表进行循环处理,即可实现行转列效果如下表:

日期 店铺 配送区域 鸭蛋 鸡蛋 鹅蛋
2018-12-11 浦东店 浦东 129 600 334
2018-12-11 浦东店 杨浦 22 533 555
2018-12-11 浦东店 虹口 22 533 555
2018-12-11 青浦店 宝山 22 533 555
2018-12-11 青浦店 静安 22 533 555
先构造表头,把商品id和商品名称取出,组合成一个表头map。

List<Object> tHeadList = new ArrayList<>();
for (Integer commonId : commonIdList) {
    StatOrdersGoodsSumVo statOrdersGoodsSumVo = statOrdersGoodsSumVoList.stream()
        .filter(x->x.getStatOrdersGoods().getCommonId()==commonId).findFirst().orElse(null);
    LinkedHashMap<String,Object> goodsMap = new LinkedHashMap<>();
    goodsMap.put("commonId",statOrdersGoodsSumVo.getStatOrdersGoods().getCommonId());
    goodsMap.put("goodsName",statOrdersGoodsSumVo.getStatOrdersGoods().getGoodsName());
    tHeadList.add(goodsMap);
}

行转列示例代码:

List<Object> goodsSumList = new ArrayList<>();
        for (Map.Entry<String, List<StatOrdersGoodsDayVo>> entry : getListByGroup.entrySet()) {
            String[] temp = entry.getKey().split("_");
            String createTimeDate = temp[0];
            String storeName = temp[1];
            String areaName = temp[2];
            HashMap<String, Object> tableTr = new HashMap<>();
            tableTr.put("createTimeDate",createTimeDate);
            tableTr.put("storeName",storeName);
            tableTr.put("areaName",areaName);
            //遍历所有商品获取统计值,如果没有商品默认为零
            for (Integer commonId : commonIdList) {
                StatOrdersGoodsDayVo statOrdersGoodsDayVo = entry.getValue().stream()
                    .filter(x->x.getStatOrdersGoods().getCommonId() == commonId).findFirst().orElse(null);
                if(statOrdersGoodsDayVo == null){
                    tableTr.put("commonId"+commonId,"0");
                }else {
                    tableTr.put("commonId"+commonId,statOrdersGoodsDayVo.getGoodsBuyNumSum());
                }
            }
            goodsSumList.add(tableTr);
        }

行转列是数据库的概念,实际上通过sql的方式也可以很好的实现,以后如果有机会分享sql最佳实践的话再做演示。这里的方式是采用按照商品外的3个字段分组后循环商品列表,然后把"commonId"+commonId做为字段名,商品统计值作为字段值,在前台展示的时候,根据字段名去对应表头的商品名称实现商品统计数据的呈现。需要注意的是表头的商品排序和商品合计的排序要一致

总结
Lambda表达式和StreamAPI的内容远不止这些,在实际的开发中,能应用的场景也还有很多,避免内容太多消化不良,以上只是列了我们目前比较常用的,以后如果有碰到其他场景,我们再补充进来。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值