Stream是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API来并行执行操作。简而言之,Stream API提供了一-种高效且易于使用的处理数据的方式。
①Stream自己不会存储元素
②Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
③Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
通过Collection系列集合提供的stream()或parallelStream( )
@Test
public void test1(){
List list=new ArrayList<>();
Stream stringStream=list.stream();
}
通过Arrays中的静态方法stream( )获取数组流
@Test
public void test2(){
Cat[] cats=new Cat[]{};
Stream catStream= Arrays.stream(cats);
}
通过Stream类中的静态方法of( )
@Test
public void test3(){
Stream stringStream=Stream.of("a","b","c");
}
创建无限流
@Test
public void test4(){
Stream integerStream=Stream.iterate(0,x->x+2);
integerStream.limit(10).forEach(System.out::println);
}
多个中间操作可以连接起来形成-一个流水线, 除非在流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
筛选和切片:
filter-接收Lambda,从流中排除某些元素;
limit-截断流,使其元素不超过给定数量;
skip(n) -跳过元素,返回一个去除了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补;
distinct-筛选排重,通过流所生成元素的hashCode()和equals()去除重复元素。
举个栗子
public class Cat {
int age;
String name;
public Cat(){
}
public Cat(int age){
this.age=age;
}
public Cat(int age,String name){
this.age=age;
this.name=name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Cat{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
@Test
public void testStream(){
List list=Arrays.asList(
new Cat(2,"Kitty"),
new Cat(3,"Garfield"),
new Cat(5,"Tom")
);
list.stream() // 创建流
.filter(x->x.getAge()>2) // 中间操作
.forEach(System.out::println); // 终止操作
}
结果
Cat{age=3, name='Garfield'}
Cat{age=5, name='Tom'}
映射:
map - 接收Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素;
flatMap - 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
举个栗子
@Test
public void testStreamMap(){
Stream stringStream=Stream.of("aaa","bbb","ccc");
stringStream.map(x->x.toUpperCase())
.forEach(System.out::println);
}
输出结果为
AAA
BBB
CCC
再举个栗子
@Test
public void testStreamMap2(){
List list=Arrays.asList(
new Cat(2,"Kitty"),
new Cat(3,"Garfield"),
new Cat(5,"Tom")
);
list.stream().map(Cat::getName).forEach(System.out::println);
}
输出结果为
Kitty
Garfield
Tom
排序:
sorted()-自然排序(Comparable);
sorted(Comparator com)- 定制排序(Comparator)。
查找与匹配:
allMatch-检查是否匹配所有元素;
anyMatch-检查是否至少匹配一个元素;
noneMatch-检查是否没有匹配所有元素;
findFirst-返回第一个元素;
findAny-返回当前流中的任意元素;
count-返回流中元素的总个数;
max-返回流中最大值;
min-返回流中最小值。
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
java8对并行流进行了一些优化,方便用户很容易的对数据进行并行操作,Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
从JDK1.7开始,Java提供了ForkJoin框架用于并行地执行任务,它的思想就是将一大大的任务拆分成若干个小任务,最终汇总每个小任务的结果得到这个大任务的结果。
举个栗子:假设现在我们需要对0到50000000000进行求和
栗子1:使用for循环进行单线程操作,CPU利用率低,耗时42911ms
@Test
public void test1() {
Instant start = Instant.now();
long sum = 0L;
for (long i = 0; i <= 50000000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间为: " + Duration.between(start, end).toMillis());
}
栗子2:使用 ForkJoin 进行任务拆分,CPU利用率100%,耗时25225ms
public class ForkJoinCalculate extends RecursiveTask {
private long start;
private long end;
private static final long THRESHOLD = 10000;
public ForkJoinCalculate(long start, long end) {
this.start = start;
this.end = end;
}
/**
* 运算或者 任务拆分
* @return
*/
@Override
protected Long compute() {
long length = end - start;
if (length <= THRESHOLD) {
//当前任务数据区间少于10000
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
} else {
//当前任务数据区间大于10000 进行任务拆分
long middle = (start + end) / 2;
ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
left.fork();
right.fork();
return left.join() + right.join();
}
}
}
@Test
public void test2() {
Instant start = Instant.now();
ForkJoinPool fjp = new ForkJoinPool( );
ForkJoinTask task = new ForkJoinCalculate( 0 , 50000000000L ) ;
Long sum = fjp.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println( "耗费时间为。"+Duration.between(start, end). toMillis());
}
栗子3:使用Stream的并行流,使用起来更加简单,也可以充分的利用CPU
@Test
public void test3(){
Instant start = Instant . now();
long res = LongStream.rangeClosed(0, 1000000L).
parallel().reduce(0,Long::sum) ;
System.out.println(res);
Instant end = Instant.now();
System.out.println( "耗费时间为: "+Duration. between(start, end). toMillis());
}