目录
一、什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作。可供流式操作的数据视图,类似数据库中视图的概念,它不改变源数据集合,如果对其进行改变的操作它会返回一个新的数据集合。
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
+--------------------+ +------+ +------+ +---+ +-------+ | stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect| +--------------------+ +------+ +------+ +---+ +-------+
以上的流程转换为 Java 代码为:
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();
二、几个主要机制
1、生成流
filter
在 Java 8 中, 集合接口有两个方法来生成流:
-
stream() − 为集合创建串行流。
-
parallelStream() − 为集合创建并行流。
filter 用于过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
2、forEach limit
Stream 提供了新的方法 'forEach' 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:
limit 用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 / 5 条数据:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
random.ints().limit(5).forEach(x -> System.out.println(x));
3、map sorted
map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:
sorted 排序
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数 map( i -> i*i) i表示每个元素 map类似于mapreduce阶段map distinct去重
List<Integer> squaresList = numbers.stream().map( i -> i*i).sorted().distinct().collect(Collectors.toList());
squaresList.forEach(System.out::println);
4、并行(parallel) count
parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
5、Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
//Collectors.toList() 输出为一个list
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
//Collectors.joining(", ") 将元素合并,以,隔开
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
6、统计
另一些统计结果的收集器也非常有用。主要用于int、double、long等基本类型上,可以用来产生类似如下的统计结果。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
常用中间件
filter:过滤流,过滤流中的元素,返回一个符合条件的Stream
map:转换流,将一种类型的流转换为另外一种流。(mapToInt、mapToLong、mapToDouble 返回int、long、double基本类型对应的Stream)
flatMap:简单的说,就是一个或多个流合并成一个新流。(flatMapToInt、flatMapToLong、flatMapToDouble 返回对应的IntStream、LongStream、DoubleStream流。)
distinct:返回去重的Stream。
sorted:返回一个排序的Stream。
peek:主要用来查看流中元素的数据状态。
limit:返回前n个元素数据组成的Stream。属于短路操作
skip:返回第n个元素后面数据组成的Stream。结束操作
forEach: 循环操作Stream中数据。
toArray: 返回流中元素对应的数组对象。
reduce: 聚合操作,用来做统计。
collect: 聚合操作,封装目标数据。
min、max、count: 聚合操作,最小值,最大值,总数量。
anyMatch: 短路操作,有一个符合条件返回true。
allMatch: 所有数据都符合条件返回true。
noneMatch: 所有数据都不符合条件返回true。
findFirst: 短路操作,获取第一个元素。
findAny: 短路操作,获取任一元素。
forEachOrdered: 暗元素顺序执行循环操作。
三、操作案例
1、常用stream方式
(1)使用Collection下的 stream() 和 parallelStream() 方法
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
(2)使用Arrays 中的 stream() 方法,将数组转成流
Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
(3)使用Stream中的静态方法:of()、iterate()、generate()
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
Stream<Integer> stream2 = Stream.iterate(10, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10 从10开始递加2,共6个
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);
(4)使用 BufferedReader.lines() 方法,将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);
2、中间件案例
import com.xin.Person;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Demo2 {
public static void main(String[] args) {
List<Person> persionList = new ArrayList<Person>();
persionList.add(new Person(1,"张三","男",38));
persionList.add(new Person(2,"小小","女",2));
persionList.add(new Person(3,"李四","男",65));
persionList.add(new Person(4,"王五","女",20));
persionList.add(new Person(5,"赵六","男",38));
persionList.add(new Person(6,"大大","男",65));
//1、只取出该集合中所有姓名组成一个新集合
List<String> nameList=persionList.stream().map(Person::getName).collect(Collectors.toList());
nameList.forEach(System.out::println);
// 2、只取出该集合中所有id组成一个新集合 boxed:数值流转换为流 mapToInt(T -> int) : return IntStream
List<Integer> idList=persionList.stream().mapToInt(Person::getId).boxed().collect(Collectors.toList());
List<Integer> idList2=persionList.stream().map(Person::getId).collect(Collectors.toList());
System.out.println(idList.toString());
//3、list转map,key值为id,value为Person对象-->Person:Person or person:peron p大小写都行
Map<Integer, Person> personmap = persionList.stream().collect(Collectors.toMap(Person::getId,Person -> Person));
System.out.println(personmap.toString());
//4、list转map,key值为id,value为name
Map<Integer, String> namemap = persionList.stream().collect(Collectors.toMap(Person::getId, Person::getName));
System.out.println(namemap.toString());
//5、进行map集合存放,key为age值 value为Person对象 它会把相同age的对象放到一个集合中
Map<Integer, List<Person>> ageMap = persionList.stream().collect(Collectors.groupingBy(Person::getAge));
System.out.println(ageMap.toString());
//6、获取最小年龄
Integer ageMin = persionList.stream().mapToInt(Person::getAge).min().getAsInt();
System.out.println("最小年龄为: "+ageMin);
//7、获取最大年龄
Integer ageMax = persionList.stream().mapToInt(Person::getAge).max().getAsInt();
System.out.println("最大年龄为: "+ageMax);
//8、集合年龄属性求和
Integer ageAmount = persionList.stream().mapToInt(Person::getAge).sum();
System.out.println("年龄总和为: "+ageAmount);
}
}
filter案例
//9.求年纪大于20岁的人数 p -> p.getAge()
long count = persionList.stream().filter(person -> person.getAge() > 20).count();
System.out.println(count);
//10、求年纪大于20岁、性别为男的人数 p -> p.getGender()必须放在前面
long count1 = persionList.stream().filter(p -> p.getAge() > 20).filter(p -> p.getGender().equals("男")).count();
System.out.println(count1);
sorted案例
一、数组
//11、sorted相关例子
String[] arr={"scsa","bi","a","cd","ca156165"};
//按照字典姓名排序
Object[] objects = Arrays.stream(arr).sorted((String o1, String o2) -> o1.length() - o2.length()).toArray();
Object[] objects1 = Arrays.stream(arr).sorted().toArray();
//姓名长度排序
Object[] objects2 = Arrays.stream(arr).sorted(Comparator.comparing(String::length)).toArray();
/**
* 倒序
* reversed(),java8泛型推导的问题,所以如果comparing里面是非方法引用的lambda表达式就没办法直接使用reversed()
* Comparator.reverseOrder():也是用于翻转顺序,用于比较对象(Stream里面的类型必须是可比较的)
* Comparator. naturalOrder():返回一个自然排序比较器,用于比较对象(Stream里面的类型必须是可比较的)
* */
Object[] objects3 = Arrays.stream(arr).sorted(Comparator.comparing(String::length).reversed()).toArray();
for(Object ob:objects){
System.out.println(ob);
}
Arrays.stream(arr).sorted(Comparator.reverseOrder()).forEach(System.out::println);
//输出:bc、abcd、abc、a
Arrays.stream(arr).sorted(Comparator.naturalOrder()).forEach(System.out::println);
//输出:a、abc、abcd、b
//先按照首字母排序,之后按照String的长度排序 com1 自定义静态方法
Arrays.stream(arr).sorted(Comparator.comparing(Demo2::com1).thenComparing(String::length)).forEach(System.out::println);
//输出:a、abc、abcd、bc
}
public static char com1(String x){
return x.charAt(0);
}
二、集合
//1、找到年龄最小的岁数
Collections.sort(persionList, (x, y) -> x.getAge()-(y.getAge()));
Integer age = persionList.get(0).getAge();
System.out.println("年龄最小的有:" + age);
//输出:年龄最小的有:2
//2、找到年龄最小的姓名
String name = persionList.stream()
.sorted(Comparator.comparingInt(x -> x.getAge()))
.findFirst() //取第一个数据
.get().getName();
System.out.println("年龄最小的姓名:" + name);
//输出:年龄最小的姓名:小小
map和flatmap区别
package com;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.function.Function;
public class Demo4 {
public static void main(String[] args) throws IOException {
// 要求:将list最后输出 abc123 or a b c 1 2 3 即各单位独立,而不是abc一组,123一组
// map是输出多个流,flatmap是将多个流输出成一个流
List<String> list = Arrays.asList("a,b,c", "1,2,3");
Stream<String> stream = list.stream();
//objectStream中含有6个元素,因为flagmap压平
Stream<Object> objectStream = stream.flatMap(new Function<String, Stream<?>>() {
@Override
public Stream<?> apply(String s) {
//1去逗号 2、转成Stream
// String[] split = s.split(",");
String[] split = s.split(",");
// String s1 = s.replaceAll(",", "");
// Stream<String> split1 = Stream.of(split);
Stream<String> stream1 = Arrays.stream(split);
return stream1;
}
});
List<Object> collect = objectStream.collect(Collectors.toList());
System.out.println(objectStream.count());
System.out.println(collect.size());
collect.forEach(System.out::print);
System.out.println();
System.out.println("------------------");
//stringStream中含有2个元素,因为list集合有两个元素,map不具备压平机制
Stream<String> stringStream = list.stream().map(new Function<String, String>() {
@Override
public String apply(String s) {
StringBuffer sb = new StringBuffer();
String[] split = s.split(",");
for (String s1 : split) {
sb.append(s1);
}
return sb.toString();
}
});
List<String> collect2 = stringStream.collect(Collectors.toList());
System.out.println(collect2.size());
collect2.forEach(System.out::print);
System.out.println();
System.out.println("------------------");
//将map输出集合中两个数组,分别转换流再转换成数组遍历输出
List<String[]> collect1 = list.stream().map(p -> p.split(",")).collect(Collectors.toList());
System.out.println(collect1.size());
collect1.forEach(s -> Arrays.stream(s).collect(Collectors.toList()).forEach(System.out::print));
}
}