概述
了解stream
Java8中有两大最为重要的改变。第一个是 Lambda
表达式;另外一个则是 Stream API
(java.util.stream.*)。
Stream
是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API
提供了一种高效且易于使用的处理数据的方式。
流(Stream): 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
这里的Stream和IO中的Stream不同,它提供了对集合操作的增强,极大的提高了操作集合对象的便利性。
“集合讲的是数据,流讲的是计算!”
特点
Stream
自己不会存储元素;Stream
不会改变源对象; 相反,它们会返回一个持有结果的新Stream;Stream
操作是延迟执行的; 这意味着它们会等到需要结果的时候,才执行;
Stream 操作的三个步骤
- 创建 Stream
一个数据源(如: 集合,数组),获取一个流;
如:可以通过 Collection 集合提供的串行流 stream()
或并行流 parallelStream()
- 中间操作
一个中间操作链,对数据源的数据进行处理; - 终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果;
1.创建stream
1.Collection 扩展接口,提供了两个获取流的方法
default Stream<E> stream()
: 返回一个顺序流default Stream<E> parallelStream()
: 返回一个并行流
2.由数组创建流
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
static <T> Stream<T> stream(T[] array)
: 返回一个流
重载形式,能够处理对应基本类型的数组:
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
3.由Stream静态方法创建
- 由值创建流
Stream.of()
可以使用静态方法Stream.of()
, 通过显示值 创建一个流。它可以接收任意数量的参数。
–public static<T> Stream<T> of(T... values)
: 返回一个流 - 由函数创建流:无限流
Stream.iterate()
或Stream.generate()
– 迭代:public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
– 生成:public static<T> Stream<T> generate(Supplier<T> s)
@Test
public void test1(){
//1通过Collection系列集合提供的stream()或 paralleStream()
List<String> list = new ArrayList<>();
Stream stream1 = list.stream();
Stream stream2 = list.parallelStream();
//2.通过Arrays中的静态方法stream()
Employee[] employees = new Employee[8];
Stream<Employee> stream3 = Arrays.stream(employees);
//3.通过Stream类的静态放of()、iterate()、generate()创建
//of
Stream<String> stream4 = Stream.of("a","b","c");
//无限流1:iterate
Stream<Integer> stream5=Stream.iterate(0,x->x+2);
stream5.limit(5).forEach(x->System.out.print(x)); //02468
//无限流2:generate
Stream<Integer> stream6 = Stream.generate(()->(int)(Math.random()*100));
stream6.limit(6).forEach(x-> System.out.print(x));
}
2.中间操作
筛选与切片
filter
接收lambda,从流中排除某些元素limit
截断流,使其元素不穿过给定数量。limit在filter后一起使用可以短路skip(n)
跳过元素,返回一个丢掉前n个元素的流,若流不足n个,返回一个空流,与limit互补distinct
去重,通过元素的hashCode() 和 equals()比较去重
映射
map
接收lambda 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成为一个新的元素flatMap
接收一个函数为参数,将流中的每个值换成另一个流,然后把所有的流连接成一个流
排序
sorted()
自然排序sorted(Compator com)
定制排序
filter
/**
* filter
* 内部迭代:迭代操作由StreamAPI完成
*/
@Test
public void test1(){
//中间操作 不会执行任何操作
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
Stream<Employee> stream = employees.stream()
.filter((e) -> {
System.out.println("中间操作");
return e.getAge() > 13;
});
//终止操作 一次性执行全部内容,即“惰性求值”
stream.forEach(System.out::println); //Employee(age=20, salary=2000)
}
limit
/**
* 截断流 一旦找到满足条件的数据,后续的迭代结束
*/
@Test
public void test3(){
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
employees.stream()
.filter((e)->{
System.out.println("短路!");
return e.getSalary()>60;
})
.limit(1)
.forEach(System.out::println);
}
//短路!
//Employee(age=12, salary=1000)
skip
/**
* 跳过
*/
@Test
public void test4(){
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
employees.stream()
.filter(e->e.getSalary()>60)
.skip(1)
.forEach(System.out::println);
//Employee(age=20, salary=2000)
}
distinct
/**
* 去重 重写hashCode和equals
*/
@Test
public void test5(){
List<String> ss = Lists.newArrayList("a","a","b","c","c");
ss.stream().distinct()
.forEach(System.out::print);//abc
}
map和flapMap
二者区别 https://blog.csdn.net/andyzhaojianhui/article/details/79047825
类似add
于addAll
的关系。add
参数是集合每个元素,返回至集合。addAll
参数是整个集合,实际操作是加入的集合中每个元素,返回至集合。
map
map的返回结果:取决于map中的函数返回类型,返回结果是什么就是什么,再加上外层的Stream
//map
@Test
public void test6(){
/**
* map
*/
List<String> list = Arrays.asList("a","b","c");
System.out.println(list); //[a, b, c]
//将list中的每个元素转换成数字
Stream<Integer> stream = list.stream().map(s->{
Integer i;
switch (s){
case "a":i=1;break;
case "b":i=2;break;
case "c":i=3;break;
default:i=0;
}
return i;
});
stream.forEach(System.out::print); //123
}
flatMap在取深层元素时很有用。
flatMap的返回结果中,两层Sream包裹的仅保留一层Stream,并去掉集合的一层。
作用1:【输入】将输入去掉一层集合后返回 借助u->u.stream()
//map与flatMap对比
@Test
public void test7(){
/**
* flatMap
*/
List<String> l1= Arrays.asList("a","b","c");
System.out.println(l1);//[a, b]
List<String> l2= Arrays.asList("x","y");
System.out.println(l2);//[x, y]
//map
Stream<List<String>> s =Stream.of(l1,l2);
//map的返回结果:取决于map中的函数返回类型,返回结果是什么就是什么,再加上外层的Stream
Stream<List> s1 = s.map(li -> Lists.newArrayList(li,"1"));
s1.forEach(System.out::println); //[[a, b, c], 1][[x, y], 1]
//flatMap
Stream<List<String>> ss =Stream.of(l1,l2);
//flatMap作用1:将输入去掉一层后返回
Stream<String> s2 = ss.flatMap(u->u.stream());
s2.forEach(System.out::print);
//测试多层Stream和List的情况:仅保留一层Stream并去掉集合的一层
List<List<String>> lists = Lists.newArrayList();
List<String> ls1=Lists.newArrayList("didi");
List<String> ls2=Lists.newArrayList("hi","my");
lists.add(ls1);
lists.add(ls2);
System.out.println(lists); //[[didi], [hi, my]]
Stream<List<List<String>>> si = Stream.of(lists);
Stream<List<String>> s3 = si.flatMap(u->u.stream());
s3.forEach(System.out::print); //[didi][hi, my] 由此可见仅去掉了一层
}
作用2:【输出】flatMap中的函数返回,如果也是Stream,则仅保留一层stream
// flatMap的返回结果测试
public Stream<Character> characterStream(String s) {
List<Character> result = new ArrayList<>();
for (char c : s.toCharArray())
result.add(c);
return result.stream(); //list转stream,一个级别上
}
@Test
public void test8(){
List<String> words = new ArrayList<String>();
words.add("your");
words.add("name");
Stream<Stream<Character>> result1 = words.stream().map(this::characterStream);
//测试map
result1.forEach(System.out::println);//打印的是两个Stream对象
//java.util.stream.ReferencePipeline$Head@12bb4df8
//java.util.stream.ReferencePipeline$Head@4cc77c2e
Stream<Character> result2 = words.stream().flatMap(this::characterStream);
//测试flatMap
result2.forEach(System.out::println); //一个个的字符
}
flatmap示例2:
@Test
public void testMapAndFlatMap() {
List<String> words = new ArrayList<String>();
words.add("hello");
words.add("word");
//将words数组中的元素再按照字符拆分,然后字符去重,最终达到["h", "e", "l", "o", "w", "r", "d"]
//如果使用map,是达不到直接转化成List<String>的结果
List<String> stringList = words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.distinct()
.collect(Collectors.toList());
stringList.forEach(e -> System.out.println(e));
}
sorted() 自然排序(Comparable)
sorted(Compator com) 定制排序 (Comparator)
List<String> list = Arrays.asList("bbb","zzz","yyy","ccc","sss");
list.stream()
.sorted()
.forEach(System.out::println);
// 员工排序
employees.stream()
.sorted((e1,e2) -> {
if(e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e2.getNme());
}else{
return e1.getAge().compareTo(e2.getAge());
}
}).forEach(System.out::println);
3.终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List
、Integer
,甚至是 void
。
查找与匹配
- 查找与匹配
allMatch
检查是否匹配所有元素anyMacth
检查是否至少匹配一个元素noneMacth
检查是否没有匹配所有元素findFirst
返回第一个元素findAny
返回任意一个元素count
返回元素总数max
返回最大值min
返回最小值forEach
内部迭代(使用 Collection 接口需要用户去做迭 代,称为外部迭代。相反,Stream API 使用内部 迭代——它帮你把迭代做了)
归约
reduce
(T identity,BinaryOperator) 可以将流中元素反复结合起来,得到一个值。 返回 Treduce
(BinaryOperator) 可以将流中元素反复结合起来,得到一个值。 返回 Optional
收集
collect(Collector c)
将流转换为其他形式。接收一个 Collector接口的 实现,用于给Stream中元素做汇总的方法
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它 来进行网络搜索而出名
查找匹配demo
@Test
public void test1(){
boolean b1 = studentList.stream()
.allMatch((e) -> e.getStatus() == Student.Status.FREE);
System.out.println(b1);
boolean b2 = studentList.stream()
.anyMatch((e) -> e.getStatus() == Student.Status.FREE);
System.out.println(b2);
boolean b3 = studentList.stream()
.noneMatch((e) -> e.getStatus() == Student.Status.FREE);
System.out.println(b3);
Optional<Student> op1 = studentList.stream()
.sorted((e1, e2) -> -Integer.compare(e1.getScore(), e2.getScore()))
.findFirst();
System.out.println(op1.get());
Optional<Student> op2 = studentList.stream()
.filter((e) -> e.getStatus() == Student.Status.FREE)
.findAny();
System.out.println(op2.get());
//并行流
Optional<Student> op3 = studentList.parallelStream()
.filter((e) -> e.getStatus() == Student.Status.FREE)
.findAny();
System.out.println(op3.get());
//count
long count = studentList.stream()
.count();
System.out.println(count);
//max
Optional<Student> max = studentList.stream()
.max(Comparator.comparing(Student::getName));
System.out.println(max.get());
//min
Optional<Integer> min = studentList.stream()
.map(e -> e.getScore())
.min(Integer::compare);
System.out.println(min.get());
}
reduce
//reduce
@Test
public void test9(){
/*
* 归约
* reduce(T identity, BinaryOperator) / reduce(BinaryOperator)
* 可以将流中元素反复结合起来,得到一个值; 其中 第一个参数identity,表示起始值
*/
// 需求: 将list集合中的元素相加
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0,(x,y) -> x+y);
System.out.println(sum); //55
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
// 获取员工工资的总和
Optional<Integer> op = employees.stream()
.map(Employee::getSalary)
.reduce(Integer::sum);
System.out.println(op.get()); //3000
}
collect
Collector 接口中方法的实现决定了如何对流执行收集操作(如收 集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态 方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
//collect
@Test
public void test10(){
/*
* 收集
* collect: 将流转换为其他形式,接收一个Collector 接口的实现,用于给 Stream 中元素做汇总的方法
* Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List, Set, Map)
* Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例;
*/
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
// 需求: 将员工中年龄收集到 list 集合
List<Integer> list = employees.stream()
.map(Employee::getAge)
.collect(Collectors.toList());
list.forEach(System.out::println);
// 将员工姓名收集到 HashSet 中
HashSet<Integer> hset = employees.stream()
.map(Employee::getAge)
.collect(Collectors.toCollection(HashSet::new)); //toSet()
// 员工总人数
Long count = employees.stream()
.collect(Collectors.counting());
System.out.println("总数"+count);
// 工资平均值
Double avg = employees.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println("平均"+avg);
// 工资总和
Double sum = employees.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println("综合"+sum);
// 按照Status进行分组
// Map<Status, List<Employee>> map = employees.stream()
// .collect(Collectors.groupingBy(Employee::getStatus));
// System.out.println(map);
// 分区(满足条件的一个区,不满足条件的在另一个区)
Map<Boolean, List<Employee>> map = employees.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 1700));
System.out.println(map); //{false=[Employee(age=12, salary=1000)], true=[Employee(age=20, salary=2000)]}
DoubleSummaryStatistics dss = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getSum());
System.out.println(dss.getAverage());
System.out.println(dss.getMax());
// 连接字符串
// String str = employees.stream()
// .map(Employee::getName)
// .collect(Collectors.joining(","));
// System.out.println(str);
}
其他demo。参考 https://blog.csdn.net/keepstrong/article/details/80154295
分组
/**
* 分组
*/
@Test
public void test5(){
Map<Student.Status, List<Student>> map = studentList.stream()
.collect(Collectors.groupingBy(Student::getStatus));
System.out.println(map);
}
@Test
public void test6(){
Map<Student.Status, Map<String, List<Student>>> map = studentList.stream()
.collect(Collectors.groupingBy(Student::getStatus, Collectors.groupingBy((e) -> {
if (e.getAge() < 15) {
return "小朋友";
} else {
return "青年";
}
})));
System.out.println(map);
}
分区
@Test
public void test7(){
Map<Boolean, List<Student>> map = studentList.stream()
.collect(Collectors.partitioningBy((e) -> e.getScore() > 70));
System.out.println(map);
}
统计
@Test
public void test8(){
IntSummaryStatistics iss = studentList.stream()
.collect(Collectors.summarizingInt(Student::getScore));
System.out.println(iss.getSum());
System.out.println(iss.getAverage());
System.out.println(iss.getCount());
System.out.println(iss.getMax());
}
拼接
@Test
public void test9(){
String str = studentList.stream()
.map(Student::getName)
.collect(Collectors.joining(","));
System.out.println(str);
}