一、Stream的操作三个步骤
- 创建Stream
- 一个数据源(如:集合,数组),获取一个流
- 中间操作
- 一个中间操作链,对数据源的数据进行处理
终止操作(终端操作)
-
二、获取Stream流的几种方式
@Test
public void test() {
// 1.可以通过Collection系列结合提供的stream() 或 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Stream<String> stringStream = list.parallelStream();
// 2.通过Arrays中的静态方法stream()获取数组流
Integer[] array = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(array);
// 3.通过Stream类中的静态方法of()
Stream<String> stream2 = Stream.of("aa", "bb", "cc");
// 4.创建无限流
// 4.1 迭代
Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);
iterate.limit(10).forEach(System.out::println);
// 4.2 生成
Stream<Double> generate = Stream.generate(() -> Math.random());
generate.forEach(System.out::println);
}
三、Stream中常用方法(中间操作)
多个中间操作可以连接起来形成一个流水线,除非流水线触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
1、筛选与切片
-
- filter()——接收Lambda,从流中排除某些元素。
- limit(n)——截断流,使其元素不超过非定数量
- skip(n)——跳过元素,返回一个扔掉了前n个元素的流,若流中的元素不足n个,则返回一个空流。与limit()互补
distinct——筛选,通过流所生成元素的hashCode()和equals()去除重复元素
2、映射
map——接收lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
3、排序
sorted()——自然排序
sorted(Comparator com)——定制排序
/**
* 排序
* sorted()——自然排序(Comparable)
* sorted(Comparator com)——定制排序
*/
@Test
public void testSort() {
List<String> list = Arrays.asList("aaa", "ddd", "bbb", "zz", "fff");
// 自然排序
list.stream()
.sorted()
.forEach(System.out::println); // aaa,bbb,ddd,fff,zz
// 定制排序
List<Person> people = Arrays.asList(new Person("周杰伦", 18), new Person("方文山", 40), new Person("林俊杰", 28), new Person("周杰伦", 20));
people.stream()
.sorted( (p1,p2) -> {
if (p1.getName().equals(p2.getName())) {
return p1.getAge().compareTo(p2.getAge());
} else {
return p1.getName().compareTo(p2.getName());
}
}).forEach(System.out::println);
}
四、Stream中常用方法(终止操作)
1. 查找与匹配
allMatch()——检查是否匹配所有元素
- anyMatch()——检查是否至少匹配一个元素
- noneMatch()——检查是否没有匹配所有元素
- findFirst()——返回第一个元素
findAny()——返回当前流中的任意元素
@Test
public void test() {
List<Person> people = Arrays.asList(new Person("周杰伦", 18), new Person("方文山", 40), new Person("昆凌", 40), new Person("薛之谦", 40));
// 检查是否匹配所有元素
boolean b = people.stream()
.allMatch(e -> e.getName().equals("周杰伦"));
System.out.println(b); // false
// 检查是否至少匹配一个元素
boolean b1 = people.stream()
.anyMatch(e -> e.getName().equals("周杰伦"));
System.out.println(b1); // true
// 检查是否没有匹配所有元素
boolean b2 = people.stream()
.noneMatch(e -> e.getName().equals("林俊杰"));
System.out.println(b2); // true
// 返回第一个元素
Optional<Person> first = people.stream()
.findFirst();
System.out.println(first); // Optional[Person(name=周杰伦, age=18)]
// 返回当前流中的任意元素
for (int i = 0; i < 100; i++) {
Optional<Person> any = people.parallelStream()
.findAny();
System.out.println(any);
}
}
count()——返回流中元素的总个数
- max()——返回流中最大值
min()——返回流中最小值
@Test
public void test3() {
List<Person> people = Arrays.asList(new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
// 返回流中元素的总个数
long count = people.stream()
.count();
System.out.println(count); // 4
// 返回流中最大值
Optional<Person> max = people.stream()
.max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
System.out.println(max); // Optional[Person(name=方文山, age=45)]
// 返回流中最小值
Optional<Integer> min = people.stream()
.map(Person::getAge)
.min((p1, p2) -> Integer.compare(p1, p2));
System.out.println(min); // Optional[18]
}
4、归约
reduce(T identity,BinaryOperator)/reduce(BinaryOperator)——可以将流中元素反复结合起来 ,得到一个值
备注:map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名。
@Test
public void test4() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 可以将流中元素反复结合起来 ,得到一个值(两个参数的)
Integer reduce = list.stream()
.reduce(0, (x, y) -> x + y); // 第一个参数是起始值,此例中:0作为x,然后从集合中取出第一个元素1,将二者相加,再取第二个元素2.....以此类推
System.out.println(reduce); // 55
// 可以将流中元素反复结合起来 ,得到一个值(一个参数的)
Optional<Integer> reduce2 = list.stream()
.reduce(Integer::sum);
System.out.println(reduce2); // 55
}
collect——将流转换为其他形式,接收一个Collector接口的实现,用于stream中元素做汇总的方法。
Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List,Set,Map)。但是COllectors实用类提供了很多静态方法,可以方便的创建常见收集器实例。
@Test
public void test5() {
List<Person> people = Arrays.asList(new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
// 将流转换为其他形式,接收一个Collector接口的实现,用于stream中元素做汇总的方法。
List<String> collect = people.stream()
.map(Person::getName)
.collect(Collectors.toList());
collect.forEach(System.out::print); // 周杰伦周杰伦方文山昆凌薛之谦
System.out.println();
System.out.println("-------------------");
Set<String> collect1 = people.stream()
.map(Person::getName)
.collect(Collectors.toSet()); // 做到去重效果
collect1.forEach(System.out::print); // 周杰伦方文山薛之谦昆凌
System.out.println();
System.out.println("-------------------");
HashSet<String> collect2 = people.stream()
.map(Person::getName)
.collect(Collectors.toCollection(HashSet::new)); // Collectors.toCollection() 想放到哪个集合中,就指定哪个集合
collect2.forEach(System.out::print); // 周杰伦方文山薛之谦昆凌
}
@Test
public void test6() {
List<Person> people = Arrays.asList(new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
// 1.总数
Long collect = people.stream()
.collect(Collectors.counting());
System.out.println(collect); // 5
// 2.平均值
Double collect1 = people.stream()
.collect(Collectors.averagingInt(Person::getAge));
System.out.println(collect1); // 32.2
// 总和
Integer collect2 = people.stream()
.collect(Collectors.summingInt(Person::getAge));
System.out.println(collect2); // 161
// 最大值
Optional<Integer> collect3 = people.stream()
.map(Person::getAge)
.collect(Collectors.maxBy((p1, p2) -> Integer.compare(p1, p2)));
System.out.println(collect3); // Optional[45]
// 最小值
Optional<Integer> collect4 = people.stream()
.map(Person::getAge)
.collect(Collectors.minBy((p1, p2) -> Integer.compare(p1, p2)));
System.out.println(collect4); // Optional[18]
}
/**
* 分组
*/
@Test
public void test7() {
List<Person> people = Arrays.asList(new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
Map<String, List<Person>> collect = people.stream()
.collect(Collectors.groupingBy(Person::getName)); // 按照姓名分组
// 注意:map不能forEach.必须要获取到他的key或value集合才能forEach
System.out.println(collect); // {周杰伦=[Person(name=周杰伦, age=18), Person(name=周杰伦, age=18)], 薛之谦=[Person(name=薛之谦, age=40)], 方文山=[Person(name=方文山, age=45)], 昆凌=[Person(name=昆凌, age=40)]}
/**
* 输出:
* {
* 周杰伦=[Person(name=周杰伦, age=18), Person(name=周杰伦, age=18)],
* 薛之谦=[Person(name=薛之谦, age=40)],
* 方文山=[Person(name=方文山, age=45)],
* 昆凌=[Person(name=昆凌, age=40)]
* }
*/
}
/**
* 多级分组
*/
@Test
public void test8() {
List<Person> people = Arrays.asList(new Person("周杰伦", 45),new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
Map<String, Map<Integer, List<Person>>> collect = people.stream()
.collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(Person::getAge)));
System.out.println(collect);
/**
* 输出:
* {
* 周杰伦={
* 18=[Person(name=周杰伦, age=18), Person(name=周杰伦, age=18)],
* 45=[Person(name=周杰伦, age=45)]
* },
* 薛之谦={40=[Person(name=薛之谦, age=40)]},
* 方文山={45=[Person(name=方文山, age=45)]},
* 昆凌={40=[Person(name=昆凌, age=40)]}
* }
*/
}
/**
* 分区
* 满足条件的一个区
* 不满足条件的一个区
*/
@Test
public void test9() {
List<Person> people = Arrays.asList(new Person("周杰伦", 45),new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
Map<Boolean, List<Person>> collect = people.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() > 20));
System.out.println(collect);
/**
* 输出:
* {
* false=[Person(name=周杰伦, age=18), Person(name=周杰伦, age=18)],
* true=[Person(name=周杰伦, age=45), Person(name=方文山, age=45), Person(name=昆凌, age=40), Person(name=薛之谦, age=40)]
* }
*/
}
@Test
public void test10() {
List<Person> people = Arrays.asList(new Person("周杰伦", 45),new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
IntSummaryStatistics collect = people.stream()
.collect(Collectors.summarizingInt(Person::getAge));
System.out.println(collect.getSum());
System.out.println(collect.getAverage());
System.out.println(collect.getMax());
System.out.println(collect.getMin());
System.out.println(collect.getCount());
/**
* 输出:
* 206
* 34.333333333333336
* 45
* 18
* 6
*/
}
@Test
public void test11() {
List<Person> people = Arrays.asList(new Person("周杰伦", 45),new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
String collect = people.stream()
.map(Person::getName)
.collect(Collectors.joining()); // 连接
System.out.println(collect); // 周杰伦周杰伦周杰伦方文山昆凌薛之谦
String collect1 = people.stream()
.map(Person::getName)
.collect(Collectors.joining(",")); // 连接,并且想在名字之间加个“,”
System.out.println(collect1); // 周杰伦,周杰伦,周杰伦,方文山,昆凌,薛之谦
String collect2 = people.stream()
.map(Person::getName)
.collect(Collectors.joining(",","开始——(",")——结束")); // 连接,并且想在名字之间加个“,”,再在收尾加个标识
System.out.println(collect2); // 开始——(周杰伦,周杰伦,周杰伦,方文山,昆凌,薛之谦)——结束
}
五、并行流与顺序流(串行流)
1、简介
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Steam API 可以声明性地通过paraller()与sequential()在并行流与顺序流之间进行切换。2、了解Fork/Join框架
2.1 简介
Fork/Join框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时 ),再将一个个的小任务运算结果进行join汇总。
2.2 Fork/Join框架与传统线程池的区别
采用“工作窃取”模式(work—stealing)
当执行新的任务时他可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷入一个并把它放到自己的队列中。
相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上,在一般的线程正在执行的任务由于某些原因无法继续执行,那么该线程将会处于等待状态,而fork/join框架实现中,如果某个子问题由于等待另一个子问题的完成而无法继续执行,那么处理该子问题的线程会主动寻求其他尚未运行的子问题来执行,这种方式减少了线程的等待时间,提高了性能。2.3 案例
@Test
public void test12() {
long start = System.currentTimeMillis();
long sum = 0;
for (long i = 0; i < 10000000000L; i++) {
sum += i;
}
System.out.println(sum);
Long end = System.currentTimeMillis();
System.out.println(end-start); // 2297
}
// 并行流
@Test
public void test13() {
Instant start = Instant.now();
long reduce = LongStream.rangeClosed(0, 10000000000L)
.parallel()
.reduce(0, Long::sum);
System.out.println(reduce);
Instant end = Instant.now();
System.out.println(Duration.between(start,end).toMillis()); // 475
}
六、Optional类
Optional
类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用null来表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。 常用方法: