stream流是对集合迭代器的增强,使得对集合可以进行高效的聚合操作(过滤,排序、统计分组)或者大批量数据操作,stream流中用到了lambda表达式进行高效率代码书写。lambda可以用来代替匿名函数,使得代码更为简洁。
stream流可分为stream串行流(默认)和parallelStream并行流。一般来说,并行流是不安全的,需要加同步锁;并行流一般适用于CPU使用较多的环境下的通信,因此一些I/O操作频繁的情况不适用并行流。
stream流应用大体流程
1) 把集合转换成流stream
2) 在管道里对流进行中间操作
3) 得到最后的结果,并将流转成集合
lambda表达式的使用条件有
1) 接口中只有一个未被实现
2) 有默认实现的方法除外(default)
3) Object类对应的方法除外
综上,lambda是基于函数式接口编程。
1、与lambda相识
lambda表达式(参数)->{表达式};其中参数可以带数据类型,表达式只有一条可以舍弃大括号,同理参数若有多个便不能舍弃括号。为了方便理解下面是一个打卡的简单例子
/**
* @Auther:刘兰斌
* @Date: 2021/05/30/10:40
* @Explain:
*/
public class test116 {
//打卡
interface Sign{
void sign(String people);
}
//员工打卡
public void signway(String people,Sign si){
si.sign(people);
}
public static void main(String[] args) {
test116 tes6 = new test116();
String people ="小刚";
//法一:用lambda表达式
tes6.signway(people,p -> System.out.println(p));
//法二:用匿名内部类
// Sign s = new Sign() {
// @Override
// public void sign(String people) {
// System.out.println(people);
// }
// };
// tes6.signway(people,s);
}
}
再来一个案例,下面
在这个吃苹果的案例中,lambda表达式部分相当于使用方法的引用去实现,将表达式中的逻辑方法传入到eat方法中,随后对其进行相同逻辑操作。
public class LambdaTest {
@Test
public void test() {
eat((a,b)-> a+b);
}
public static void eat(myinterface mface) {
System.out.println(mface.eatApple("小飞", 1));
}
@FunctionalInterface
interface myinterface {
String eatApple(String name, int number);
}
}
2、初识stream
例a
public static void main(String[] args) {
List<String> names = Arrays.asList("Tommi", "Davy", "Philipp", "Henry", "Roy", "Jack", "Jerry");
List<String> res = names.stream()
.filter(e -> e.startsWith("J"))
.map(String::toLowerCase)
.sorted()
.collect(Collectors.toList());
System.out.println(res);
}
结果:[jack, jerry]
解析:上面例子中集合转成流后,filter()中存放着过滤规则,可以用lambda表达式或者谓词逻辑;map()存放着对每个元素的数据转换规则,包括自定义的函数或者系统自带的函数;sorted()存放着排序规则,默认升序排列,最后将流再转成集合。若数据的存储方式为数组,那么转换方式为:int[] a= {2,4,5,6}; Stream.of(a)…
public static void main(String[] args) {
String[] str = {"Tommi", "Davy", "Philipp", "Henry", "Roy", "Jack", "Jerry"};
Stream.of(str)
.mapToInt(s -> s.length())//等价于.mapToInt(String::length)
.forEach(System.out::println);
}
}
同样的,集合类中的set、map都可以转换。
如果是文本文件则用Files.lines获取:如下
public static void main(String[] args) {
//文本转成流操作
try {
Stream<String> stream6 = Files.lines(Paths.get("src/main/resources/test116.txt"));
List<String> res = stream6.filter(s -> s.equals("Haha"))
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(res);
} catch (IOException e) {
e.printStackTrace();
}
}
例b
在filter()中使用谓词逻辑,可以增加程序的复用性。当然可以直接在filter中写lambda表达式,如下面例子中的filter可以写成
filter(s->s.getAge>60 && s.getGender().euquals(“F”))
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @Auther:刘兰斌
* @Date: 2021/05/30/12:01
* @Explain:为了方便描述,将对象与测试方法写在一起
* 需求:找出年龄超过60的女性职工
*/
public class FilterTest {
public static void main(String[] args) {
Staff staff01 = new Staff(1,23,"F","Rick","Beethoven");
Staff staff02 = new Staff(2,13,"M","Rock","ven");
Staff staff03 = new Staff(3,73,"M","Re","oven");
Staff staff04 = new Staff(4,53,"F","james","Bee");
Staff staff05 = new Staff(5,63,"M","Raty","hove");
Staff staff06 = new Staff(6,33,"M","dtt","love");
Staff staff07 = new Staff(7,83,"F","yu","ko");
Staff staff08 = new Staff(8,103,"M","tu","so");
Staff staff09 = new Staff(9,113,"F","po","bye");
Staff staff10 = new Staff(10,123,"M","jo","all");
List<Staff> staffs = Arrays.asList(staff01, staff02, staff03, staff04, staff05, staff06, staff07, staff08, staff09, staff10);
List<Staff> res = staffs.stream()
.filter(Staff.ageOver60.and(Staff.genderIsF))//这里.and代表并,表达或用.or,表达否定用negate()
.collect(Collectors.toList());
System.out.println(res);
}
}
@Data
@AllArgsConstructor
class Staff{
private int id;
private int age;
private String gender;
private String firstname;
private String lastname;
//声明谓语,方便复用
public static Predicate<Staff> ageOver60 = a -> a.getAge()>60;
public static Predicate<Staff> genderIsF = g -> g.getGender().equals("F");
}
例c
map()进行数据类型转换,有返回值,可以用peek()替换map(),此无返回值
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Auther:刘兰斌
* @Date: 2021/05/30/12:41
* @Explain:
* 需求:
* 将所有员工的年龄加1,并且性别改为全称,如F->female;M->male
*/
public class MapTest {
public static void main(String[] args) {
Staffn staff01 = new Staffn(1,23,"F","Rick","Beethoven");
Staffn staff02 = new Staffn(2,13,"M","Rock","ven");
Staffn staff03 = new Staffn(3,73,"M","Re","oven");
Staffn staff04 = new Staffn(4,53,"F","james","Bee");
Staffn staff05 = new Staffn(5,63,"M","Raty","hove");
Staffn staff06 = new Staffn(6,33,"M","dtt","love");
Staffn staff07 = new Staffn(7,83,"F","yu","ko");
Staffn staff08 = new Staffn(8,103,"M","tu","so");
Staffn staff09 = new Staffn(9,113,"F","po","bye");
Staffn staff10 = new Staffn(10,123,"M","jo","all");
List<Staffn> staffs = Arrays.asList(staff01, staff02, staff03, staff04, staff05, staff06, staff07, staff08, staff09, staff10);
List<Staffn> res = staffs.stream()
.map(s -> {
s.setAge(s.getAge() + 1);
s.setGender(s.getGender().equals("M") ? "male" : "female");
return s;
})
.collect(Collectors.toList());
System.out.println(res);
}
}
@Data
@AllArgsConstructor
class Staffn{
private int id;
private int age;
private String gender;
private String firstname;
private String lastname;
}
Stream.of("hello", "flatmap")
.flatMap(fm -> Arrays.stream(fm.split("")))
.forEach(System.out::println);
例d
多用户多线程多请求的公共数据等就是状态,filter、map、flatmap就是无状态的,即它能独立的做完手头的东西,另外一些有状态的操作如distinct、limit、skip、sorted等就属于有状态的操作,它需要同其他操作一起进行。
List<String> res = Stream.of("Tommi", "Davy", "Philipp", "Henry", "Roy", "Jack", "Jerry")
.limit(2)//.forEach(System.out::println)
.collect(Collectors.toList());
System.out.println(res);
List<String> res2 = Stream.of("Tommi", "Davy", "Philipp", "Henry", "Roy", "Jack", "Jerry")
.skip(2)
.collect(Collectors.toList());
System.out.println(res2);
List<String> res3 = Stream.of("Tommi", "Davy", "Philipp", "Henry", "Roy", "Jack", "Jerry")
.sorted()
.collect(Collectors.toList());
System.out.println(res3);
List<String> res4 = Stream.of("Tommi", "Davy", "Philipp", "Henry", "Roy", "Jack", "Jerry", "Tommi", "Davy", "Philipp", "Henry")
.distinct()
.collect(Collectors.toList());
System.out.println(res4);
结果
[Tommi, Davy]
[Philipp, Henry, Roy, Jack, Jerry]
[Davy, Henry, Jack, Jerry, Philipp, Roy, Tommi]
[Tommi, Davy, Philipp, Henry, Roy, Jack, Jerry]
注:并行状态中会导致有状态操作失真。并行模式更适合数组、ArrayList、HashMap等,链表就比较适合串行模式。
例e
sort()
public static void main(String[] args) {
//排序方法
List<Integer> number = Arrays.asList(1, 5, 7, 3, 9, 0, 34, 88);
// number.sort(Comparator.naturalOrder());//字母表大小写
number.sort(Comparator.reverseOrder());//逆序
System.out.println(number);
List<String> strings = Arrays.asList("Tommi", "Davy", "Philipp", "Henry", "Roy", "Jack", "Jerry", "dd");
//strings.sort(Comparator.naturalOrder());//先大写再小写
strings.sort(String.CASE_INSENSITIVE_ORDER);//按大小写不敏感排序
System.out.println(strings);
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.function.Predicate;
@Data
@AllArgsConstructor
public class Employee {
private Integer id;
private Integer age;
private String gender;
private String firstname;
private String lastname;
//写上谓词逻辑,为了复用
public static final Predicate<Employee> ageOver70 = a -> a.getAge()>70;
public static final Predicate<Employee> genderIsM = g -> g.getGender().equals("M");
}
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Auther:刘兰斌
* @Date: 2021/05/30/13:34
* @Explain:
* 需求:
* 按年龄倒序输出后,再按年龄倒序输出
*/
public class SortTest {
public static void main(String[] args) {
// //传统排序方法
// List number = Arrays.asList(1, 5, 7, 3, 9, 0, 34, 88);
// // number.sort(Comparator.naturalOrder());//字母表大小写
// number.sort(Comparator.reverseOrder());//逆序
// System.out.println(number);
// List strings = Arrays.asList("Tommi", "Davy", "Philipp", "Henry", "Roy", "Jack", "Jerry", "dd");
// //strings.sort(Comparator.naturalOrder());//先大写再小写
// strings.sort(String.CASE_INSENSITIVE_ORDER);//按大小写不敏感排序
// System.out.println(strings);
Employee e1 = new Employee(1,23,"F","Rick","Beethoven");
Employee e2 = new Employee(2,13,"M","Rock","ven");
Employee e3 = new Employee(3,73,"M","Re","oven");
Employee e4 = new Employee(4,53,"F","james","Bee");
Employee e5 = new Employee(5,63,"M","Raty","hove");
Employee e6 = new Employee(6,33,"M","dtt","love");
Employee e7 = new Employee(7,83,"F","yu","ko");
Employee e8 = new Employee(8,103,"M","tu","so");
Employee e9 = new Employee(9,113,"F","po","bye");
Employee e10 = new Employee(10,123,"M","jo","all");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
employees.sort(Comparator.comparing(Employee::getGender)
.thenComparing(Employee::getAge)
.reversed()
);
employees.forEach(System.out::println);
}
}
可以自定义排序规则
import java.util.Arrays;
import java.util.List;
/**
* @Auther:刘兰斌
* @Date: 2021/05/30/13:34
* @Explain:
* 需求:
* 年龄逆序输出
*
*/
public class SortTest {
public static void main(String[] args) {
Employee e1 = new Employee(1,23,"F","Rick","Beethoven");
Employee e2 = new Employee(2,13,"M","Rock","ven");
Employee e3 = new Employee(3,73,"M","Re","oven");
Employee e4 = new Employee(4,53,"F","james","Bee");
Employee e5 = new Employee(5,63,"M","Raty","hove");
Employee e6 = new Employee(6,33,"M","dtt","love");
Employee e7 = new Employee(7,83,"F","yu","ko");
Employee e8 = new Employee(8,103,"M","tu","so");
Employee e9 = new Employee(9,113,"F","po","bye");
Employee e10 = new Employee(10,123,"M","jo","all");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
employees.sort((p1,p2)->{
if(p1.getAge() == p2.getAge()){
return 0;
}
return p2.getAge()-p1.getAge();
});
employees.forEach(System.out::println);
}
}
public class TestNewDay {
/*
在这个类中需要先建立一个JavaBean,包含
private Integer id;
private String color;
private Integer weight;
private String origin;
生成相应的getter、setter方法
通过终止操作符进行收集,按照颜色分组,再计算组内各个元素的平均值。
*/
private static List<NewDay> list = new ArrayList<>();
static{
list.add(new NewDay(1,"Red",300,"China"));
list.add(new NewDay(2,"blue",230,"河北"));
list.add(new NewDay(6,"Red",400,"China"));
list.add(new NewDay(5,"yellow",730,"河北"));
list.add(new NewDay(8,"Red",900,"China"));
list.add(new NewDay(7,"yellow",270,"河北"));
list.add(new NewDay(3,"Red",350,"China"));
list.add(new NewDay(11,"blue",232,"河北"));
}
@Test
//统计所有颜色的苹果的平均重量
public void getAverageWeightByColer(){
Map<String, Double> collect = list.stream()
.collect(Collectors.groupingBy(e -> e.getColor(),
Collectors.averagingInt(a -> a.getWeight())));
collect.forEach((k,v)-> System.out.println(k+":"+v));
}
}
注:lambda表达式只能用于函数式接口
函数式接口特点:
3、匹配规则
以下匹配规则可以是lambda表达式或者谓词
anymatch(“匹配规则”);返回值是布尔类型;
allmatch(“匹配规则”):判读是不是所有的都符合匹配规则
nonematch(“匹配规则”):判读是不是不存在匹配规则的数据
optional类型来说明所要找的数据是否存在,存在则用get()取出,否则抛出异常
isPresent():查找的值是否存在,返回值是布尔类型
findall()、findany()
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
/**
* @Auther:刘兰斌
* @Date: 2021/05/30/13:34
* @Explain:
*
*/
public class SortTest {
public static void main(String[] args) {
Employee e1 = new Employee(1,23,"F","Rick","Beethoven");
Employee e2 = new Employee(2,13,"M","Rock","ven");
Employee e3 = new Employee(3,73,"M","Re","oven");
Employee e4 = new Employee(4,53,"F","james","Bee");
Employee e5 = new Employee(5,63,"M","Raty","hove");
Employee e6 = new Employee(6,33,"M","dtt","love");
Employee e7 = new Employee(7,83,"F","yu","ko");
Employee e8 = new Employee(8,103,"M","tu","so");
Employee e9 = new Employee(9,113,"F","po","bye");
Employee e10 = new Employee(10,123,"M","jo","all");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
boolean b = employees.stream()
.anyMatch(e -> e.getAge() > 100);
System.out.println(b);
boolean b1 = employees.stream().allMatch(Employee.ageOver70);
System.out.println(b1);
boolean b2 = employees.stream().noneMatch(c -> c.getAge() > 300);
System.out.println(b2);
employees.stream()
.filter(e -> e.getAge() == 33)
.findAny()//.findFirst()
.orElse(new Employee(0,0,"F","",""));//.ifPresent(s-> System.out.println(s)); .isPresent();
}
}
4、规约
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
/**
* @Auther:刘兰斌
* @Date: 2021/05/30/13:34
* @Explain:
*
*/
public class SortTest {
public static void main(String[] args) {
//集合元素规约reduce
List<Integer> integers = Arrays.asList(24, 8, 35, 23, 11, 0, 6, 3);
Integer res = integers.stream().reduce(0, (already, curr) -> already + curr );
//等价于
Integer res2 = integers.stream().reduce(0, Integer::sum);
System.out.println(res+" "+res2);
List<String> strings = Arrays.asList("a", "sd", "b", "sb");
String res3 = strings.stream().reduce("", (al, cur) -> al.concat(cur));
System.out.println(res3);
Employee e1 = new Employee(1,23,"F","Rick","Beethoven");
Employee e2 = new Employee(2,13,"M","Rock","ven");
Employee e3 = new Employee(3,73,"M","Re","oven");
Employee e4 = new Employee(4,53,"F","james","Bee");
Employee e5 = new Employee(5,63,"M","Raty","hove");
Employee e6 = new Employee(6,33,"M","dtt","love");
Employee e7 = new Employee(7,83,"F","yu","ko");
Employee e8 = new Employee(8,103,"M","tu","so");
Employee e9 = new Employee(9,113,"F","po","bye");
Employee e10 = new Employee(10,123,"M","jo","all");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
Integer res5 = employees
.parallelStream()//数据多时可以用并行流
.map(s -> s.getAge())
.reduce(0, Integer::sum);
System.out.println(res5);
//上面写法与下面等价
Integer res6 = employees.parallelStream()
.reduce(0, (subtotal, cur) -> subtotal + cur.getAge(), Integer::sum);
System.out.println(res6);
}
}
学习视频资源:https://www.bilibili.com/video/BV1sE411P7C1