java流的基本认识和底层原理(一)

Stream的基本认识

java和Stream相关的类都在java.util.stream包下。
java提供stream的目的:是为了更好的操作集合!

stream简单介绍

stream的基本构成
1、源。
2、零个或者多个中间操作。
3、终止操作

stream的特性
惰性求值、及早求值。
这句话怎么理解呢?stream中可能有多个中间操作,但是如果没有遇到终止操作,那么不管多少个中间操作都不会执行,只有遇到终止操作才会执行这些中间操作。

stream的创建方式
1、

		Stream s = stream.of("hello","world","bins");

2、

 		String[] strings = new String[]{"hello","world"};
        Stream stream = Arrays.stream(strings);

3、

		List<String> list = Arrays.asList("hello","world","bins");
        Stream stream = list.stream();

stream的特性
1、stream是不存储值的,通过管道的方式获取值
2、stream本质是函数式的,对stream操作会生成一个结果,但是并不会修改底层数据源。
3、同一个stream不能多次消费。
4、流虽然是通过集合来创建的。流关注的是集合中数据的计算,而集合关注的是数据和数据存储本身。

stream与迭代器的区别
1、stream可以并行化操作,迭代器只能命令式串行执行。
2、使用并行流去遍历的时候,数据会被分成多段,每段都会在不同的线程中执行,然后将结果合并输出。
3、stream并行流依赖于jdk7提供的fork/join框架。

外部迭代和内部迭代的区别
外部迭代操作的是集合,而内部迭代操作的是流。

注:
stream存在多个中间操作,但是这些中间操作并不会一个一个的迭代操作,而是通过stream中提供的一些特性值,将这些中间操作串联起来,遇到终止操作时一块执行!

stream存在短路操作。

比如下面这段代码:

List<String> list = Arrays.asList("hello","world","bins");
        list.stream().mapToInt(item -> {
            int lenth = item.length();
            System.out.println(item);
            return lenth;
        }).filter(item -> item == 5).findFirst().ifPresent(System.out::println);

这个是通过findFirst来实现的,感觉和传统的for循环中的break类似。

List<String> list = Arrays.asList("hello","world","bins");
for(String s : list){
            int lenth = s.length();
            System.out.println(s);
            if(lenth == 5){
                System.out.println(s);
                break;
            }
        }

map和flatMap使用
map和flatMap都是做映射的,但是如果想让多个stream同时操作就应该使用flatMap了,因为flatMap会将多个stream打平变成一个stream。

分组和分区
分组:通过Collectors.groupingBy()来实现分组,这个有很多重载方法。
分区:通过Collectors.partitionBy()来实现分区。partitioningBy也有重载方法。

收集器 Collector

Collector可以作为collect方法的参数,Collector是一个接口!支持串行、并行。
Collectors是一个类,它里面有很多方法可以返回Collector,并提供常见的汇聚实现。(类似于Collector的工厂类)

Collector有4个比较重要的方法
1、 Supplier supplier();创建一个结果容器,不接受参数,返回一个结果。
2、 BiConsumer<A, T> accumulator();将新的数据元素合并到结果容器,这个结果容器有supplier()方法提供!
3、BinaryOperator combiner();将两个结果容器合并到一个结果容器中!
4、Function<A, R> finisher();执行最终的转换,将中间类型转换成最终类型(这是一个可选操作!)

还有一个枚举类型的特性值 enum Characteristics
它包含3中类型
CONCURRENT 代表并行操作!
UNORDERED 代表无序的!
IDENTITY_FINISH 代表中间的结果容器和最终类型一致,可以进行强制类型转换,并不需要执行 finisher(),因此也证明了 finisher()是一个可选操作!

下面这段代码是自己构建一个Collector

package com.bins9.comparator;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class MySetCollector2<T> implements Collector<T, Set<T>, Map<T,T>> {
    @Override
    public Supplier<Set<T>> supplier() {
        System.out.println("supplier invoked!");
        return () -> {
            System.out.println("supplier 被使用");
            return new HashSet<>();
        };
    }

    @Override
    public BiConsumer<Set<T>, T> accumulator() {
        System.out.println("accumulator invoked!");
        return (set,item) -> {
            System.out.println("accumulator中执行线程名字::" + Thread.currentThread().getName() + "。set为:" +  set + "。添加字段为:" + item);
            set.add(item);
        };
    }

    @Override
    public BinaryOperator<Set<T>> combiner() {
        System.out.println("combiner invoked!");
        return (set1,set2) -> {
            System.out.println("调用combiner啦————————————————");
            set1.addAll(set2);
            return set1;
        };
    }

    @Override
    public Function<Set<T>, Map<T, T>> finisher() {
        System.out.println("finisher invoked!");
        return set -> {
            Map<T,T> map = new TreeMap<>();
            set.forEach(item -> {
                map.put(item,item);
            });
            return map;
        };
    }

    @Override
    public Set<Characteristics> characteristics() {
        System.out.println("characteristics invoked");
        System.out.println(Runtime.getRuntime().availableProcessors());
        return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED));
    }

    public static void main(String[] args) {
        for(int i = 0; i < 1; i ++){
            List<String> list = Arrays.asList("hello","world","hello  world","asd","qwe","qwedf","hello","z","x","c","j","p");
            Set<String> set = new HashSet<>();
            set.addAll(list);
            System.out.println("set :" + set);
            Map<String,String> map = set.stream().parallel().collect(new MySetCollector2<>());
            System.out.println(map);
        }
    }

}

通过以上代码我们可以得到如下结论:
1、使用CONCURRENT特性时,结果容器只有一个,多个线程可以同时操作同一个结果容器。
2、如果使用并行流而不使用CONCURRENT,则会出现多个线程操作多个中间结果容器,则会使用combiner()方法,进行中间结果容器的合并,如果使用CONCURRENT,则只有一个结果容器,并不会执行combiner()方法!

注意:如果对顺序没有要求的话,可以使用groupingByConcurrent来进行排序!
使用groupingByConcurrent的前提是Characteristics的特性值必须为CONCURRENT、UNORDERED
还需要注意一下,groupingByConcurrent使用的中间结果容器是ConcurrentMap,线程安全的。

因为如果没有CONCURRENT,则并行流有几个线程就会创建几个中间结果容器,但是如果结果容器进行合并就会用到Synchronized关键字,保证线程安全,因此会造成一定性能的消耗。
但是如果有CONCURRENT,此时只有一个中间结果容器,并不需要conbiner方法,因此也就不会涉及到线程安全问题,ConcurrentMap本身就是线程安全的!

简单说一下比较器:
Comparator 这个也是一个函数式接口!
使用场景:Collections.sort(List list, Comparator<? super T> c),
例子:
Collections.sort(list, Comparator.comparingInt(String::length).thenComparing(item -> item));

如果第一次比较相等,可以使用thenComparing再次进行比较!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值