JDK1.8新增了Stream类,从而把函数式编程的风格引入到Java语言中,Stream类的API提供了强大的功能,使用Stream后,可以写出更加强大,更加简洁的代码
首先,Stream流有一些特性:
- Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。
- 这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。
- Stream不保存数据,故每个Stream流只能使用一次。
关于应用在Stream流上的操作,可以分成两种:Intermediate(中间操作)和Terminal(终止操作)。中间操作的返回结果都是Stream,故可以多个中间操作叠加;终止操作用于返回我们最终需要的数据,只能有一个终止操作。至于哪些方法是中间操作,哪些方法是终止操作,我们一会儿再说。
使用Stream流,可以清楚地知道我们要对一个数据集做何种操作,可读性强。而且可以很轻松地获取并行化Stream流,不用自己编写多线程代码,可以让我们更加专注于业务逻辑。
默认情况下,从有序集合、生成器、迭代器产生的流或者通过调用Stream.sorted产生的流都是有序流,有序流在并行处理时会在处理完成之后恢复原顺序。unordered()方法可以解除有序流的顺序限制,更好地发挥并行处理的性能优势,例如distinct将保存任意一个唯一元素而不是第一个,limit将保留任意n个元素而不是前n个。
流的常用生成方法
Collection接口的stream()或parallelStream()方法
parallelStream()是并行方法,parallelStream()不一定比stream()快(需要开启线程,线程竞争),而且parallelStream()是线程不安全的,所以使用parallelStream()要综合考量,测试百万数据的List<String> 的System.out.println(),stream()依旧比parallelStream()要快
List<Integer> list = new ArrayList();
Random random = new Random();
for (int i = 0; i < 100000; i++) {
list.add(random.nextInt());
}
Instant start = Instant.now();
// list.stream().forEach(System.out::print); //193毫秒
list.parallelStream().forEach(System.out::print);//275毫秒
Instant end = Instant.now();
System.out.println("");
System.out.println("_______________________________________________________________________");
System.out.println("end:" + end.toEpochMilli());
System.out.println("start:" + start.toEpochMilli());
System.out.println("time:" + (end.toEpochMilli() - start.toEpochMilli()));
静态的Stream.of()、Stream.empty()方法
List<Integer> list = new ArrayList();
Random random = new Random();
for (int i = 0; i < 100000; i++) {
list.add(random.nextInt());
}
Instant start = Instant.now();
// Stream.of(list).forEach(System.out::println); //71毫秒
Stream.empty().forEach(System.out::println);//37毫秒
// list.stream().forEach(System.out::print); //193毫秒
// list.parallelStream().forEach(System.out::print);//275毫秒
Instant end = Instant.now();
System.out.println("");
System.out.println("_______________________________________________________________________");
System.out.println("end:" + end.toEpochMilli());
System.out.println("start:" + start.toEpochMilli());
System.out.println("time:" + (end.toEpochMilli() - start.toEpochMilli()));
Arrays.stream(array, from, to)
包前不包后
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};
Arrays.stream(arr, 1, 3).forEach(System.out::println);
流的常用Intermediate方法(中间操作)
filter(Predicate) 将结果为false的元素过滤掉
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};
//过滤单数保留双数
Arrays.stream(arr).filter(item->(item & 1) == 0).forEach(System.out::println);
map(fun) 转换元素的值,可以用方法引元或者lambda表达式
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};
//filter过滤单数保留双数
//map将剩余元素除以2
Arrays.stream(arr).filter(item->(item & 1) == 0).map(item -> item / 2).forEach(System.out::println);
flatMap(fun) 若元素是流,将流摊平为正常元素,再进行元素转换
简单点描述就是嵌套的集合扁平化
比如集合里面包含集合/数组
List<List<String>> list = Arrays.asList(
Arrays.asList("A", "B"),
Arrays.asList("C", "D"),
Arrays.asList("E", "E"));
// 使用 flatMap 扁平化列表
List<String> collect = list.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(collect); //[A, B, C, D, E, E]
或者集合里面本身没有集合/数组但是可以操作(字符串切割)变成集合/数组
List<Person> list = Arrays.asList(new Person("张三", "1,2,3"),
new Person("李四", "2,3"),
new Person("王五", "2,4"));
Set<Object> collect = list.stream().flatMap(person -> Arrays.stream(person.getHobbies().split(","))).collect(Collectors.toSet());
System.out.println(collect);
limit(n) 保留前n个元素 常配合排序使用
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};
Arrays.stream(arr).limit(2).forEach(System.out::print); //13
skip(n) 跳过前n个元素
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};
Arrays.stream(arr).skip(6).forEach(System.out::print); //9823
distinct() 剔除重复元素
int[] arr = {1, 2, 2, 4, 2};
Arrays.stream(arr).distinct().forEach(System.out::print); //124
sorted() ,sorted(Comparator) 将Comparable元素的流排序
int[] arr = {1, 2, 2, 4, 2};
Arrays.stream(arr).sorted().forEach(System.out::print); //12224
List<Person> list = Arrays.asList(new Person("张三", 18,"2,3"),
new Person("李四", 14,"2,3"),
new Person("王五", 21,"2,4"));
//根据年龄排序 reversed()倒序操作
list.stream().sorted(Comparator.comparing(Person::getAge).reversed()).forEach(System.out::println);
peek(fun) 流不变,但会把每个元素传入fun执行,可以用作调试
可以理解为没有返回结果的forEach()
List<Person> list = Arrays.asList(new Person("张三", 18,"2,3"),
new Person("李四", 14,"2,3"),
new Person("王五", 21,"2,4"));
//根据年龄排序 reversed()倒序操作
list.stream().sorted(Comparator.comparing(Person::getAge).reversed()).peek(item->{
item.setAge(item.getAge() + 1);
}).peek(item->{
item.setAge(item.getAge() + 1);
}).peek(item->{
item.setAge(item.getAge() + 1);
}).forEach(System.out::println);
结果:
Person(name=王五, age=24, hobbies=2,4)
Person(name=张三, age=21, hobbies=2,3)
Person(name=李四, age=17, hobbies=2,3)
流的Terminal方法(终结操作)
max(Comparator)获取最大,min(Comparator)获取最小
List<Person> list = Arrays.asList(new Person("张三", 18,"2,3"),
new Person("李四", 14,"2,3"),
new Person("王五", 21,"2,4"));
Optional<Person> maxPerson = list.stream().max(Comparator.comparingInt(Person::getAge));
maxPerson.map(Person::getName).ifPresent(System.out::println); //王五
Optional<Person> minPerson = list.stream().min(Comparator.comparingInt(Person::getAge));
minPerson.map(Person::getAge).ifPresent(System.out::println); //14
count()数量
List<Person> list = Arrays.asList(new Person("张三", 18,"2,3"),
new Person("李四", 14,"2,3"),
new Person("王五", 21,"2,4"));
long count = list.stream().filter(person -> !person.getName().startsWith("张")).count();
System.out.println(count); //2
findFirst() 返回第一个元素,findAny() 返回任意元素只有在并行流中才有效,不然返回的还是第一个元素
List<Person> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
Person person = new Person("赵" + i, i, "2,4");
list.add(person);
}
list.stream().findFirst().ifPresent(System.out::println);
Map map = new HashMap<>();
for (int i = 0; i < 100; i++) {
list.parallelStream().findAny().ifPresent(person -> {
Integer anInt = MapUtil.getInt(map, person.getAge()) == null ? 0 : MapUtil.getInt(map, person.getAge());
map.put(person.getAge(), anInt + 1);
});
}
map.forEach((k, v) -> {
System.out.println("k;" + k + " v:" + v);
});
/**
* k;656 v:77
* k;640 v:2
* k;577 v:2
* k;531 v:1
* k;327 v:10
* k;281 v:1
* k;906 v:2
* k;218 v:1
* k;827 v:3
* k;718 v:1
*/
anyMatch(Predicate) ,allMatch(Predicate) ,noneMatch(Predicate)
anyMatch(Predicate)任意元素匹配时返回true
allMatch所有元素匹配时返回true
noneMatch没有元素匹配时返回true
List<Person> list = Arrays.asList(new Person("张三", 18,"2,3"),
new Person("李四", 14,"2,3"),
new Person("王五", 21,"2,4"));
boolean b1 = list.stream().anyMatch(person -> "张三".equals(person.getName()));
System.out.println("b1:" + b1);
boolean b2 = list.stream().filter(person -> "张三".equals(person.getName())).allMatch(person -> "张三".equals(person.getName()));
System.out.println("b2:" + b2);
boolean b3 = list.stream().noneMatch(person -> "张w".equals(person.getName()));
System.out.println("b3:" + b3);
reduce(fun),reduce(a, fun),reduce(a, fun1, fun2)
reduce(fun)从流中计算某个值,接受一个二元函数作为累积器,从前两个元素开始持续应用它,累积器的中间结果作为第一个参数,流元素作为第二个参数
reduce(a, fun)a为幺元值,作为累积器的起点
reduce(a, fun1, fun2)与二元变形类似,并发操作中,当累积器的第一个参数与第二个参数都为流元素类型时,可以对各个中间结果也应用累积器进行合并,但是当累积器的第一个参数不是流元素类型而是类型T的时候,各个中间结果也为类型T,需要fun2来将各个中间结果进行合并,a的类型为fun1第一个参数的类型,fun2的类型为流元素的类型,fun1返回的类型可以理解为stream.fun2(fun1返回,fun1返回)
List<Person> list = Arrays.asList(new Person("张三", 18, "2,3"),
new Person("李四", 14, "2,3"),
new Person("王五", 21, "2,4"));
Optional<Integer> reduce1 = list.stream().map(Person::getAge).reduce((b, c) -> b > c ? b : c);
Integer reduce2 = list.stream().map(Person::getAge).reduce(0,(b, c) -> b > c ? b : c);
Integer reduce3 = list.stream().map(Person::getAge).reduce(33,(b, c) -> b > c ? b : c);
Integer reduce4 = list.stream().map(Person::getAge).reduce(33, Integer::sum);
Integer reduce5 = list.stream().map(Person::getAge).reduce(0, Integer::sum, Integer::sum);
Person reduce6 = list.stream().reduce(new Person(), (b, c) -> {
return c;}, (b, c) -> b.getAge() > c.getAge() ? b : c);
System.out.println(reduce1.isPresent()?reduce1.get():null); // 最大年龄21返回Optional
System.out.println(reduce2); // 以0为基准 返回 21 Integer
System.out.println(reduce3);// 以33为基准 返回 33 Integer
System.out.println(reduce4);// 以33为基准 累加 33+53 Integer
System.out.println(reduce5);// 以 0为基准 累加 53 Integer
System.out.println(reduce6);// 以 new Person()为基准 第一个参数返回了相当于list.stream(),第二个函数返回一个年林最大的Person(name=王五, age=21, hobbies=2,4)
iterator()生成一个迭代器
List<Person> list = Arrays.asList(new Person("张三", 18, "2,3"),
new Person("李四", 14, "2,3"),
new Person("王五", 21, "2,4"));
list.stream().iterator().forEachRemaining(System.out::println);
forEach(fun),forEachOrdered(fun)
forEachOrdered可以应用在并行流上以保持元素顺序
List<Person> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
Person person = new Person("赵" + i, i, "2,4");
list.add(person);
}
list.stream().forEach(System.out::println);//可以简写成list.forEach(System.out::println);
list.forEach(System.out::println);
list.parallelStream().forEachOrdered(System.out::println);//会按顺序打印
toArray(),toArray(T[] :: new)
List<Person> list = Arrays.asList(new Person("张三", 18, "2,3"),
new Person("李四", 14, "2,3"),
new Person("王五", 21, "2,4"));
Object[] array1 = list.stream().toArray();
System.out.println(Arrays.toString(array1));
Person[] array2 = list.stream().toArray(Person[]::new);
System.out.println(Arrays.toString(array2));
collect(Collector),collect(fun1, fun2, fun3)
collect(fun1, fun2, fun3)fun1转换流元素;fun2为累积器,将fun1的转换结果累积起来;fun3为组合器,将并行处理过程中累积器的各个结果组合起来