万字详解Stream流,聊聊工作中常用的Lambda表达式

前言


大家好,我是weichert。

在日常开发中,我们经常会用到JDK 8中的Lambda表达式,lambda表达式允许我们把函数作为一个方法的参数,实现复杂的功能。函数式编程相较于面向对象编程(OOP),可以使我们的代码更加简洁,更加优雅,可以使用更少的代码量,实现更加强大的功能。stream流针对于list的操作更加的简洁,集成了许多常用的api,供我们进行调用,极大的提高了我们的开发效率。在这篇文章中,我整理了在我工作中常用的一些Stream流,供大家学习参考。

  • 公众号、掘金账号: weichert

函数式接口原理


函数式接口是Java中的一种接口类型,它只包含一个抽象方法。使用@FunctionalInterface注解。,这个注解只可以标记在有且仅有一个抽象方法的接口上,并且JDK8接口中的静态方法和默认方法,都不算是抽象方法。常用函数式接口如Function等也随Lambda一同增加在了java.util.function包中。

图片

在jdk8中底层的实现原理:在我们实际编写lambda表达式中,就是将该接口进行重写,我们只需要关注参数和返回值即可,例如Function中的参数泛型,返回值泛型。

jdk8中的函数式接口放在java.util.function目录下:

图片

lambda表达式的写法


Java中lambda表达式符号:->

写法: (参数) -> {返回值}

生产型接口(无参数有返回值):( ) -> {业务代码,return 返回值}

消费型接口(有参数无返回值):(参数值) -> {业务代码,无返回值}

转换型接口(有参数有返回值):(参数值) -> {业务代码,return 返回值}

备注:

  • 当参数值只有一个时,()可以省略不写。
    - 但业务代码只有一行时,{}可以省略不写。
    - -> 符号,不可以省略。

示例:Thread类的函数式接口

// 1.使用匿名内部类的方式创建多线程
new Thread(new Runnable() {    
	@Override    
	public void run() {        
	System.out.println("这是多线程1");    
}}).start();

// 2.使用lambda表达式的方式创建多线程表达式
new Thread(() -> System.out.println("这是多线程2")).start();

Thread(函数式接口)

图片

图片

stream流常用方法


1.创建stream流

方式1:

List<Integer> list = new ArrayList<>();Stream<Integer> stream = list.stream();

方式2:

Stream<String> stream = Stream.of("张三", "李四", "王五");

备注: stream流创建后只能使用一次,创建了一个stream流使用两次会报IllegalStateException异常。

@Test
public void testReuseStream() {    
Stream<Integer> stream = Stream.of(416, 1854, 157, 187);    
stream.sorted().forEach(System.out::println);    
stream.sorted(((o1, o2) -> o2-o1)).forEach(System.out::println);
}

图片

2.ForEach

forEach方法和Java中的for循环是一样,主要是是做遍历操作。**
**

图片

该函数式接口为消费型接口,有参无返回值,并且是终结接口(表示处理结束)。


public void testForEach(){        
	List<String> list = new ArrayList<String>() {{            
	add("1");            
	add("2");            
	add("3");        
}};
	list.forEach(s-> System.out::println);    }

3.Collect

转化接口,我们经常使用该接口将stream流转换为list,该接口可以作为终结接口使用,collect(Collectors.toList())最常用。

@Testpublic void testCollect(){    
List<String> list = new ArrayList<String>() {{        
	add("1");        
	add("2");        
	add("2");    
}};
    
//转换为新的list    
List newList = list.stream().map(s -> Integer.valueOf(s)).collect(Collectors.toList());}

4.Filter

这是一个过滤方法,就像一个过滤器,满足条件的才会保存起来,不满足条件的就会被过滤掉。

图片

该函数式接口,返回值是一个布尔值,为true则保存,为false则丢弃。

public void testFilter() {        
List<String> list = new ArrayList<String>() {{            
	add("1");            
	add("2");            
	add("3");        
}};                        
              
// 过滤掉我们希望留下来的值                
// 表示我们希望字符串是 1 能留下来                
// 其他的过滤掉                
list.stream().filter(str -> "1".equals(str)).collect(Collectors.toList());}

5.Map

map 方法可以让我们进行一些流的转化,比如原来流中的元素是 A,通过 map 操作,可以使返回的流中的元素是 B,这是一个转换方法。

图片

这是方法是所有方法中使用频率最高的,常见的转化操作都会使用该接口,比如将String类型的数据转换为Integer类型

public void testMap() {        
List<String> list = new ArrayList<String>() {{            
	add("1");            
	add("2");            
	add("3");        
}};        
//通过 map 方法list中元素转化成 小写        
List<String> strLowerList = list.stream().map(str -> str.toLowerCase())
.collect(Collectors.toList());}

6.Distinct

distinct 方法类似于SQL中的去重操作,可以去掉重复的参数。

图片

备注:对于引用数据类型的去重需要重写hashCode和equals方法。

public void testDistinct(){        
List<String> list = new ArrayList<String>() {{            
	add("1");            
	add("2");            
	add("2");        
}};        

list.stream().map(s -> Integer.valueOf(s)).distinct().collect(Collectors.toList());    
}

7.Sorted

Sorted 方法提供了排序的功能,并且允许我们自定义排序,可以正序,倒序,自定义规则排序等。

图片

8.findFirst

这个方法就像SQL中的limit1一样,只会返回集合中的第一个元素,在获取第一名,最后一名这种操作下,常见。**
**

图片

@Testpublic void testCollect(){    
List<String> list = new ArrayList<String>() {{        
	add("1");        
	add("2");        
	add("2");    
}};    

//转换为新的list    
Optional<Integer> first = list.stream().map(s -> Integer.valueOf(s)).sorted().findFirst();}

9.Max,Min

这两个方法顾名思义是求集合中的最大值、最小值。

public void testMaxMin(){        
List<String> list = new ArrayList<String>() {{            
	add("1");            
	add("2");            
	add("2");        
}};
        
list.stream().max(Comparator.comparing(s -> Integer.valueOf(s))).get();        list.stream().min(Comparator.comparing(s -> Integer.valueOf(s))).get();    
}

10.groupingBy

groupingBy 是能够根据字段进行分组,toMap 是把 List 的数据格式转化成 Map 的格式,类似于SQL中的group by。

public void testGroupBy(){        
List<String> list = new ArrayList<String>() {{            
	add("1");            
	add("2");            
	add("2");        
}};

Map<String, List<String>> strList = list.stream().collect(Collectors.groupingBy(s -> { 
	if("2".equals(s)) {                
		return "2";            
	}else {                
		return "1";            
	}        
}));    
}

总结


本文中,主要描述了lambda表达式的实现原理,核心关键点在于函数式接口,以及函数式接口的分类,十种stream流的常用方法。

归根结底,在使用stream时,我们只需要关注函数式接口的参数和返回值即可,根据函数式接口的要求,实现我们的业务代码,即可优雅的使用函数式接口,简化我们的代码。

stream流中的方法和MySQL中的一些函数,关键字的作用类似,都是对数据的处理操作。

stream流和MySQL中函数的区别

stream流MySQL
min,maxmin,max
findFirstlimit 1
groupingBygrouping by
distinctdistinct
sortedorder by
filterwhere

最后


stream的使用,也是熟能生巧的事,群里面有小伙伴在交流该问题:

可以在公众号联系我进群哦!

只要多多练习就能熟练的掌握stream流的使用,帮助你优雅地写出高质量的代码。

欢迎关注公众号:weichert

每天分享一篇实用的技术文章,对面试,工作都有帮助

如果您觉得有收获,记得点赞,转发,分享,谢谢

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值