流Stream其实就是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列,集合是数据,流就是计算,把数据经过一系列流水线式的中间操作以后产生一个新流 。1)stream不会自己存储元素 ,2)stream不会改变源对象 3)stream操作是延迟的执行的,这意味着他们会等到需要结果时才执行(后面代码演示)。
Stream操作的三个步骤:
1.创建Stream:创建数据源(集合,数组等),获取一个流
2.中间操作:一个中间操作链,对数据源的数据进行处理
3.终止操作:一个终止操作,执行中间操作链,产生结果
//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> stream1 = Arrays.stream(nums);
//3. 通过 Stream 类中静态方法 of()
Stream<Integer> stream2 = Stream.of(1,2,3);
//4. 创建无限流
//迭代 一元运算+2无限进行,可以用limit限制次数
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
stream3.forEach(System.out::println);
//生成
Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);
中间操作:所有中间操作不会做任何处理
Stream<Employee> stream = emps.stream()
.filter((e) -> {
System.out.println("测试中间操作");
return e.getAge() <= 35;
});
此时运行程序不会打印,只有当做终止操作时,所有中间操作会一次性的全部执行
stream.forEach(System.out::println);
1.筛选和切片类型的中间操作
filter:接受Lambda,从流中排除某些元素(eg:如上面的例子)
limit:截断流使其元素不超过给定数值
@Test
public void test4(){
emps.stream()
.filter((e) -> {
System.out.println("短路!"); // && ||
return e.getSalary() >= 5000;
}).limit(3)
.forEach(System.out::println);
}
skip:跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
@Test
public void test5(){
emps.parallelStream()
.filter((e) -> e.getSalary() >= 5000)
.skip(2)
.forEach(System.out::println);
}
distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素
@Test
public void test6(){
emps.stream()
.distinct()
.forEach(System.out::println);
}
2.映射类型
map:接受Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
@Test
public void test1(){
List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream<String> stream = strList.stream()
.map(String::toUpperCase);
stream.forEach(System.out::println);
}
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流.
@Test
public void test1(){
List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream<Character> stream3 = strList.stream()
.flatMap(TestStreamAPI1::filterCharacter);
stream3.forEach(System.out::println);
}
3.排序类型
sort:自然排序
@Test
public void test2(){
emps.stream()
.map(Employee::getName)
.sorted()
.forEach(System.out::println);
}
sorted:定制排序:
@Test
public void test2(){
emps.stream()
.sorted((x, y) -> {
if(x.getAge() == y.getAge()){
return x.getName().compareTo(y.getName());
}else{
return Integer.compare(x.getAge(), y.getAge());
}
}).forEach(System.out::println);
}
终止操作:
1.查找与匹配类型:
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
@Test
public void test1(){
boolean bl = emps.stream()
.allMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl);
boolean bl1 = emps.stream()
.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl1);
boolean bl2 = emps.stream()
.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl2);
}
@Test
public void test2(){
Optional<Employee> op = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(op.get());
System.out.println("--------------------------------");
Optional<Employee> op2 = emps.parallelStream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(op2.get());
}
@Test
public void test3(){
long count = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.count();
System.out.println(count);
Optional<Double> op = emps.stream()
.map(Employee::getSalary)
.max(Double::compare);
System.out.println(op.get());
Optional<Employee> op2 = emps.stream()
.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op2.get());
}
//注意:流进行了终止操作后,不能再次使用
@Test
public void test4(){
Stream<Employee> stream = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE));
long count = stream.count();
stream.map(Employee::getSalary)
.max(Double::compare);
}
归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。
@Test
public void test1(){
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);
System.out.println("----------------------------------------");
Optional<Double> op = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
}
map和reduce的连接通常称为map-reduce,因谷歌用他搜索而出名,hadoop中会学到。先map提取,再用reduce归约
collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
函数
@Test
public void test4(){
//方法一
//员工总数
Long count = emps.stream().collect(Collectors.counting());
System.out.println(count);
//工资总和
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
//工资平均值
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
//工资最大值员工的工资
Optional<Double> max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
//工资最小值员工的信息
Optional<Employee> op = emps.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
//方法二:
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
System.out.println(dss.getMin());
System.out.println(dss.getCount());
System.out.println(dss.getSum());
}
分组
@Test
public void test5(){
//按照状态分组
Map<Status, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
多级分组
@Test
public void test6(){
//先按状态分组,状态分完按年龄段分组(可以无限扩展)
Map<Status, Map<String, List<Employee>>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(map);
}
分区
@Test
public void test7(){
//true和false两个区,满足条件一个区,不满足的一个区
Map<Boolean, List<Employee>> map = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
System.out.println(map);
}
连接
@Test
public void test8(){
//字符串连接,把名字连接,可根据参数中间拿,间隔。首尾用---
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("," , "----", "----"));
System.out.println(str);
}
并行流
并行流,就是把一个内容分成多块,并用不同的线程处理不同的内容块,java8做了优化,底层是fork/join。把大任务拆分(forlk)后,分别处理,处理完(join)把结果汇总,充分利用了多核。
fork/join与传统线程池的区别,工作窃取模式。。。简单的说就是自己的内容快干完了,会随机去其他线程上从后面窃取一个任务去处理,减少了线程等待,提高了效率。
1.7中fork/join用法
public class ForkJoinCalculate extends RecursiveTask<Long>{
/**
*
*/
private static final long serialVersionUID = 13475679780L;
private long start;
private long end;
private static final long THRESHOLD = 10000L; //临界值
public ForkJoinCalculate(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if(length <= THRESHOLD){
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else{
long middle = (start + end) / 2;
ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
left.fork(); //拆分,并将该子任务压入线程队列
ForkJoinCalculate right = new ForkJoinCalculate(middle+1, end);
right.fork();
return left.join() + right.join();
}
}
进行累加操作一下为测试类
@Test
public void test1(){
long start = System.currentTimeMillis();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinCalculate(0L, 10000000000L);
long sum = pool.invoke(task);
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start)); //112-1953-1988-2654-2647-20663-113808
}
为了对比,写了一个单线程for循环的累加操作
@Test
public void test2(){
long start = System.currentTimeMillis();
long sum = 0L;
for (long i = 0L; i <= 10000000000L; i++) {
sum += i;
}
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start)); //34-3174-3132-4227-4223-31583
}
注释为对比数据结果,当累加输小的时候 第1次测试数据时1E,单线程较快,因为处理数据较少,fork/join的拆分反而浪费了资源,当累加到100E时,单线程明显变慢。
下面是1.8优化后写法
@Test
public void test3(){
long start = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L, 10000000000L)
.parallel()
.sum();
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start)); //2061-2053-2086-18926
}
代码简洁且效率比1.7中的fork/join效率还要高。