java stream 取某个字段_java8的函数式编程和stream使用心得

38623f6608a73f37addd823665e722af.png

1:函数式编程

在很多其他的编程语言里面,都可以实现函数式的编程,也就是函数可以作为变量去灵活使用,但是java一直都不可以,之前很多都使用一些匿名内部类这种丑的亚批的代码。java8之后算是可以使用伪函数式编程,其实也是应用了单方法接口去实现。并且设计出了lambda语法。


1.1:lambda

在lambda之前实现thread(真的很丑这代码)。

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("xxx");
            }
        });

在lambda实现thread

Thread thread = new Thread(()-> System.out.println("xx"));

1.2:函数式接口

其实上面的runnable接口就是一个函数式接口,标志就是用@FunctionalInterface实现的接口,我们也可以自己实现一个:

@FunctionalInterface
public interface FunctionInterfaceDemo {
    String run(int i);
    //如果写两个方法就会报错
}

然后就可以当成函数变量使用了,比如说你根据不同的入参调整成不同执行策略fuction,最后去执行这个fuction,就非常的函数式了。

FunctionInterfaceDemo functionDemo = p->{
            p++;
            return String.valueOf(p);
        };

2:大杀器-stream

如果说上面lambda只是美化了代码而已,那么lambda配合stream使用就是非常装逼的无敌大杀器了。

2.1:foreach

其实最常用的就是foreach了,直接展示一个demo,可以用lambda形式进行遍历

       //构建个User对象的list,后面都会使用到 User(id,name,age)
        List<User> userList = Lists.newArrayList();
        userList.add(new User(1, "aaa", 18));
        userList.add(new User(2, "bbb", 21));
        userList.add(new User(3, "ccc", 25));
        userList.stream().forEach(user->{
            System.out.println(user.getName());
        });

2.2:filter

filter可以按照你的要求进行过滤,非常骚包

        userList.stream().filter(user -> user.getAge() > 18).forEach(user -> {
            //输出age大于18的user的name
            System.out.println(user.getName());
        });

2.3:collect实现list转map

collect可以实现各种集合之间按照一定自定义规则的转换,最为典型的就是list转换map的例子

Map<String, User> map = userList.stream().collect(Collectors.toMap(User::getName, pojo -> pojo, (k1, k2) -> k1));
//【解释下toMap的3个参数】:
//第一个参数是用哪个字段作为key
//第二个参数是map的value类型,可以取原来的对象,也可以是原来对象的某个字段 比如pojo->pojo改为 User::getAge 就是value只收集user的age字段
//第三个参数(k1, k2) -> k1)是可选的,在key冲突的时候丢弃其实一个,如果没有这个参数会直接报错
//这边建议第三个参数必须强制设置一个,不然要是重复key程序直接会报错,非常麻瓜。

2.4:collect+map抽取pojo对象的某个字段

List<String> names = userList.stream().map(User::getName)
.collect(Collectors.toList());

2.5:list实现排序

//数字类型list排序
List<Integer> sortList = list.stream().sorted(Comparator.comparing(p -> p))
.collect(Collectors.toList());

//对象类型list排序 
List<Pair> sortList = list.stream()
.sorted((p1, p2) -> p2.getValue() - p1.getValue()).collect(Collectors.toList()); 
// 这里是逆序 ,如果是跑p1-p2就是自然序

2.6:Predicate和Consumer

在这些stream的各种操作组合骚包操作下,有时候直接写一个lambda显得有点丑,特别是这个lambda方法体内东西很多的时候,这个时候就可以使用这2个内置的函数先行定义lambda方法体,这样就非常灵活骚包,而且还可以分开写,痛击你的同事让他看不懂。

        Predicate<User> predicate = p -> p.getName().startsWith("s");
        Consumer<User> consumer = p -> {
            System.out.println(p.getName());
            System.out.println(p.getAge());
        };
        userList.stream().filter(predicate).forEach(consumer);

2.7:parallelStream

stream是单管道流,parallelStream是多管道流。建议符合以下条件时使用parallerStream:

  • 不关心执行顺序
  • 没有并行处理的并发问题
  • 处理事件涉及io阻塞操作,业务处理事件较长

下面我们来测试下stream和parallerStream的执行对比:

        List<User> userList = Lists.newArrayList();
        for (int i = 0; i < 1000; i++) {
            userList.add(new User(1, "aaa", 18));
        }
        AtomicInteger count = new AtomicInteger(0);
        long start = System.currentTimeMillis();
        userList.parallelStream().forEach(p -> {
            count.addAndGet(p.getId());
            //模拟业务处理
            try {
                Thread.sleep(10L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long duration = System.currentTimeMillis() - start;
        System.out.println("count = "+count.get()+" duration = " + duration);
//执行结果如果换成stream就会慢很多

2.8:实现原理和forkjoin框架

其实stream和parallelStream都是通过fork/join框架去执行的,大概是做了这么几件事

  • 使用自带的ForkJoinPool.commonPool()这个公共线程池去执行任务
  • 然后这些任务会被拆分成几个小任务进行对应的流式处理
  • 处理完成后类似future这种方式得到所有结果集再进行合并

这里有一点特别要注意,stream和parallelStream的第一个任务都是默认使用主线程的,所以异常不会逃逸,但是parallelStream的任务数量一旦大于1,就会启动forkjoin的线程,如果不处理好异常,异常就会逃逸掉了。


3:总结

总的来说lambda加stream的组合够骚够装逼,也能简化我们开发的复杂度,并且让代码更加帅!推荐使用!

其他还有很多stream的各种方法可以按照实际场景去尝试使用看看哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值