Stream简介
Lambda表达式作为java8的新特性,早已是‘家喻户晓’,Lambda表达式使得java编程越发的简洁,提高了编程效率和可读性,Lambda表达式允许通过创建只有一个抽象方法的接口(函数式接口)的实例,本博文主要简单接收Stream的操作符,简单原理,实际应用。
操作符
操作符顾名思义就是对数据进行的一种工作,可以是一道加工程序,也可以是具体的字符,就如工人在流水线上对产品加工产品 Stream的操作符大体上分为两种:中间操作符和终止操作符
中间操作符
对于数据流来说,中间操作符在执行制定处理程序后,数据流依然可以传递给下一级的操作符。 中间操作符包含8种(排除了parallel,sequential,这两个操作并不涉及到对数据流的加工操作):
-
map(mapToInt,mapToLong,mapToDouble) 转换操作符,把比如A->B,这里默认提供了转int,long,double的操作符。
-
limit 限流操作,比如数据流中有10个 我只要出前3个就可以使用。
-
distint 去重操作,对重复元素去重,底层使用了equals方法。
-
filter 过滤操作,把不想要的数据过滤。
-
peek 挑出操作,如果想对数据进行某些操作,如:读取、编辑修改等。
-
skip 跳过操作,跳过某些元素。
-
sorted(unordered) 排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。
终止操作符
数据经过了中间过程的操作,会最终对所有的数据做收集和消费,数据到该节点就停止加工,终止操作符只能使用一次
- collect 收集操作,将所有数据收集起来。
- count 统计操作,统计最终的数据个数。
- findFirst、findAny 查找操作,查找第一个、查找任何一个 返回的类型为Optional。
- forEach、forEachOrdered 遍历操作,这里就是对最终的数据进行消费了。
- toArray 数组操作,将数据流的元素转换成数组。
提示:java8专门为函数式接口提供了@FunctionalInterface注解,该注解通常放在接口定义前面,该注解对程序功能没有任何作用,他用于告诉编译器执行更严格检查–检查该接口必须是函数式接口,否则编译报错
常用集锦
转换1
/**
* @author dzx
* @since 2020/9/20
*/
public class Test {
public static void main(String []args){
//=====================================map()、mapToInt、mapToDouble、mapToFloat、mapToLong、toMap()===============================
//List<S> ---> List<T> , S为User[username,sex] T为Person[sex]
List<User> users1 = new ArrayList<>();
users1.add(User.builder().username("dzx").sex("男").build());
users1.add(User.builder().username("sxl").sex("女").build());
//输出 :[Person(sex=男), Person(sex=女)]
List<Person> people = users1.stream().map(v->Person.builder().sex(v.getSex()).build()).collect(Collectors.toList());
System.out.println(people);
//List<S> --> Map<T,S> , S为User[userId,username,sex] T为userId的类型
List<User> users2 = new ArrayList<>();
users2.add(User.builder().userId("DZ2020").username("dzx").sex("男").build());
users2.add(User.builder().userId("DZ2021").username("sxl").sex("女").build());
//输出map结构 :{DZ2021=User(userId=DZ2021, username=sxl, sex=女), DZ2020=User(userId=DZ2020, username=dzx, sex=男)}
Map<String,User> map1 = users2.stream().collect(Collectors.toMap(User::getUserId, Function.identity()));
System.out.println(map1);
//toMap的过程中会出现key冲突,会报Duplicate key的编译错误, 解决Key冲突:引入一个合并函数,它指出在发生冲突的情况下,我们保留现有条目或者替换
users2.add(User.builder().userId("DZ2021").username("hxj").sex("女").build());
//输出map结构 :{DZ2021=User(userId=DZ2021, username=hxj, sex=女), DZ2020=User(userId=DZ2020, username=dzx, sex=男)}
Map<String,User> map2 = users2.stream().collect(Collectors.toMap(User::getUserId, Function.identity(),(exist,replace)->replace));
System.out.println(map2);
//List<String> --> List<Integer>
List<Integer> ints = Stream.of("1", "2", "3").map(Integer::parseInt).collect(Collectors.toList());
//[1, 2, 3]
System.out.println(ints);
//mapToInt
Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
.mapToInt(String::length)
//566125
.forEach(System.out::print);
}
}
转换2
public class Test {
public static void main(String []args){
//基本类型数组->包装类型集合
List<Integer> turnInt = Stream.of(1,2,3,4).collect(Collectors.toList());
//集合->字符串
String turnStr = Stream.of("156","22","18","12").collect(Collectors.joining(","));
//156,22,18,12
System.out.println(turnStr);
//flatMap使用,拍平,把多个元素集合合并为一个集合
List<List<Integer>> lists = new ArrayList<>();
List<Integer> list1 = new ArrayList<>();
list1.add(11);
list1.add(22);
List<Integer> list2 = new ArrayList<>();
list2.add(33);
list2.add(44);
lists.add(list1);
lists.add(list2);
//[[11, 22], [33, 44]]
System.out.println(lists);
List<Integer> collect = lists.stream().flatMap(Collection::stream).collect(Collectors.toList());
//[11, 22, 33, 44]
System.out.println(collect);
}
}
统计(加减)
public static void main(String []args){
//=====================================count()、sum()、max()、min()
//统计集合中的所有元素的总和
List<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);
Integer total1 = integers.stream().mapToInt(v->v).sum();
System.out.println(total1);
//最大值和最小值
Integer total2 = integers.stream().mapToInt(v->v).max().getAsInt();
Integer total3= integers.stream().mapToInt(v->v).min().getAsInt();
System.out.println(total2);
System.out.println(total3);
//bigdecimal大数据相加
List<BigDecimal> bigDecimalsList = new ArrayList<>();
bigDecimalsList.add(new BigDecimal("1000"));
bigDecimalsList.add(new BigDecimal("2000"));
bigDecimalsList.add(new BigDecimal("3000"));
//将user对象的mongey取出来map为Bigdecimal .map(User::getMoney)
//使用reduce聚合函数,实现累加器 reduce 减少【表多个bigdecimal相加聚合在一起,减少了bigdecimal】
System.out.println(bigDecimalsList.stream().reduce(BigDecimal.ZERO,BigDecimal::add));
}
排序
public static void main(String []args){
//================================sort、forEachOrdered
//按照对象的年龄从小到大排序
List<User> users1 = new ArrayList<>();
users1.add(User.builder().age(12).build());
users1.add(User.builder().age(178).build());
users1.add(User.builder().age(1).build());
//1(大于) 0(等于) -1(小于)
List<User> user2 = users1.stream().sorted((x,y)->x.getAge()>y.getAge()?1:(x.getAge().equals(y.getAge()))?0:-1).collect(Collectors.toList());
//输出[User(age=1), User(age=12), User(age=178)]
System.out.println(user2);
//forEachOrdered 适用用于并行流的情况下进行迭代,能保证迭代的有序性
//输出[User(age=178), User(age=12), User(age=1)]
users1.stream().parallel().forEachOrdered(System.out::print);
System.out.println();
//Comparator.comparing()
List<User> user3 = new ArrayList<>();
user3.add(User.builder().age(2).money(11).build());
user3.add(User.builder().age(2).money(10).build());
user3.add(User.builder().age(3).money(0).build());
//默认升序排列(从小到大),按照年龄属性排序,Comparator.reverseOrder()按照从大到小排序(降序) 等同于
// System.out.println(user3.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList()));
System.out.println(user3.stream().sorted(Comparator.comparing(User::getAge,Comparator.reverseOrder())).collect(Collectors.toList()));
//java.util.Collections 排序
Collections.sort(userList, Comparator.comparingInt(User::getAge));//等效于 (x,y)->x.getAge()-y.getAge()
System.out.println(userList);
//先按照年龄排序,若相等则按金额排序
System.out.println(user3.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getMoney)).collect(Collectors.toList()));
}
查找&去重
public static void main(String []args){
//==================================skip()、limit()、findAny、distinct、
//跳过前面某些元素,输出51-48
Stream.of(1,2,51,-48).skip(2).forEach(System.out::print);
//只取前面几个元素,输出12
Stream.of(1,2,51,-48).limit(2).forEach(System.out::print);
//取任意一个
Integer integer = Stream.of(1,2,51,-48).findAny().get();
System.out.println(integer);
//任意匹配其中之一
boolean flag1 = Stream.of(1,2,51,-48).anyMatch(item->item>0);
//对anyMatch取反
boolean flag2 = Stream.of(1,2,51,-48).noneMatch(item->item>0);
//全部匹配
boolean flag3 = Stream.of(1,2,51,48).allMatch(item->item>0);
//true false true
System.out.println(flag1 +" "+flag2+" "+flag3);
//去重,若根据userid去重,需要同时重写equals和hashcode方法
// @Override
// public boolean equals(Object obj) {
// return ((User) obj).getUserId().equals(userId);
// }
//
// @Override
// public int hashCode() {
// return userId.hashCode();
// }
List<User> users1 = new ArrayList<>();
users1.add(User.builder().age(12).userId("D2018").build());
users1.add(User.builder().age(10).userId("D2020").build());
users1.add(User.builder().age(10).userId("D2018").build());
//根据某个字段去重
//collectingAndThen可以理解为收集并进行下一步操作,TreeSet中放入属性的比较器,由于TreeSet是通过TreeMap的形式来去重。
//若遇到相同的key,后者会覆盖前者,类似(exist,replace)->replace,最后通过new ArrayList(TreeSet)的构造形式取出TreeSet里的value
List<User> users2 = users1.stream().distinct().collect(Collectors.toList());
System.out.println(users2);
List<User> userList = new ArrayList<>();
userList.add(new User("3","带头大哥","36","杭州"));
userList.add(new User("1","李大锤","23","上海"));
userList.add(new User("1","李大锤","23","南京"));
//[User(userid=3, username=带头大哥, age=36, address=杭州), User(userid=1, username=李大锤, age=23, address=上海), User(userid=1, username=李大锤, age=23, address=南京)]
System.out.println(userList);
//根据userid去重
ArrayList<User> collect = userList.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getUserid))), ArrayList::new));
//[User(userid=1, username=李大锤, age=23, address=上海), User(userid=3, username=带头大哥, age=36, address=杭州)]
System.out.println(collect);
}
过滤
public static void main(String []args){
//=====================================filte()方法
List<User> users1 = new ArrayList<>();
users1.add(User.builder().age(12).userId("D2018").build());
users1.add(User.builder().age(10).userId("D2020").build());
users1.add(User.builder().age(10).userId("D2018").build());
//过滤年龄大于10
List<User> user2 = users1.stream().filter(v->v.getAge()>10).collect(Collectors.toList());
System.out.println(user2);
}
遍历
public static void main(String []args){
List<String> list = new ArrayList<>();
list.add("dzx");
list.add("sxl");
list.add("sxl1");
//按照for(int i =0;i<list.size();i++)形式遍历
///IntStream.range(0,list.size()),产生[0,list.size())整数序列
IntStream.range(0,list.size()).forEach(item->{
System.out.println(list.get(item));
});
}
分组
public static void main(String[] args) {
//数据准备
List<User> userList = new ArrayList<>();
userList.add(User.builder().username("dzx").age("12").userid("1").build());
userList.add(User.builder().username("sxl").age("12").userid("2").build());
userList.add(User.builder().username("qwe").age("20").userid("3").build());
userList.add(User.builder().username("sxl").age("12").userid("3").build());
//按照用户名分组
Map<String, List<User>> userMap1 = userList.stream().collect(Collectors.groupingBy(User::getUsername));
//{dzx=[User(userid=1, username=dzx, age=12)], sxl=[User(userid=2, username=sxl, age=12), User(userid=3, username=sxl, age=12)], qwe=[User(userid=3, username=qwe, age=20)]}
System.out.println(userMap1);
//按照用户名_年龄,拼接分组
Map<String, List<User>> userMap2 = userList.stream().collect(Collectors.groupingBy(item -> item.getUsername() + "_" + item.getAge()));
//{qwe_20=[User(userid=3, username=qwe, age=20)], sxl_12=[User(userid=2, username=sxl, age=12), User(userid=3, username=sxl, age=12)], dzx_12=[User(userid=1, username=dzx, age=12)]}
System.out.println(userMap2);
//按照年龄条件分组
Map<String, List<User>> userMap3 = userList.stream().collect(Collectors.groupingBy(item -> {
if ("12".equals(item.getAge())) {
return "12";
} else {
return "20";
}
}));
//{12=[User(userid=1, username=dzx, age=12), User(userid=2, username=sxl, age=12), User(userid=3, username=sxl, age=12)], 20=[User(userid=3, username=qwe, age=20)]}
System.out.println(userMap3);
//多级分组,先按照用户名分组,再按照分组后的用户id分组
Map<String, Map<String, List<User>>> userMap4 = userList.stream().collect(Collectors.groupingBy(User::getUsername, Collectors.groupingBy(User::getUserid)));
//{dzx={1=[User(userid=1, username=dzx, age=12)]}, sxl={2=[User(userid=2, username=sxl, age=12)], 3=[User(userid=3, username=sxl, age=12)]}, qwe={3=[User(userid=3, username=qwe, age=20)]}}
System.out.println(userMap4);
//按照userid分组后,对组内按年龄属性求和,对组内求组员总数
Map<String, Integer> userMap5 = userList.stream().collect(Collectors.groupingBy(User::getUserid, Collectors.summingInt(item -> Integer.parseInt(item.getAge()))));
//求和 {1=12, 2=12, 3=32}
System.out.println(userMap5);
Map<String, Long> userMap6 = userList.stream().collect(Collectors.groupingBy(User::getUserid, Collectors.counting()));
//总和 {1=1, 2=1, 3=2}
System.out.println(userMap6);
//结合其他收集器:按照姓名分组后,每个分组只需要保留userid信息
Map<String, List<String>> userMap7 = userList.stream().collect(Collectors.groupingBy(User::getUsername, Collectors.mapping(User::getUserid, Collectors.toList())));
//{dzx=[1], sxl=[2, 3], qwe=[3]}
System.out.println(userMap7);
}
方法引用和构造引用
种类 | 示例 | 说明 | 对应的Lambda表达式 |
---|---|---|---|
引用类方法 | 类名::类方法 | 函数式接口中被实现方法的全部参数传给该类方法作为参数 | (a,b…)->类名.类方法(a,b…) |
引用特定对象的实例方法 | 特定对象::实例方法 | 函数式接口中被实现方法的参数传给该方法作为参数 | (a,b…)->特定对象.实例方法(a,b…) |
引用某类对象的实例方法 | 类名::实例方法 | 函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数 | (a,b…)->a.实例方法(b…) |
引用构造器 | 类名::new | 函数式接口中被实现方法的全部参数传给该构造器作为参数 | (a,b…)->new 类名(a,b…) |
引用
- 《疯狂java讲义第3版》
- 芋道源码 https://mp.weixin.qq.com/s/vxZthSjG3zNYNFd2oDZK3g
- Java 8特性 - Collectors toMap https://www.jianshu.com/p/267c53dd4295