JAVA基础7-Stream基础学习笔记

Stream概念

来源

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

概念

Stream(流)是一个来自数据源的元素队列并支持聚合操作
1) 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
2) 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
3) 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

特点

和以前的Collection操作不同, Stream操作还有两个基础的特征:
1) Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
2) 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

特性

1) stream不存储数据
2) stream不改变源数据
3) stream的延迟执行特性

Stream创建方法

通过Stream静态方法

/**
  * 通过一个Supplier函数,无限生成对象的集合流,也是一个无限流
*/
public static<T> Stream<T> generate(Supplier<T> s);
例子:
Stream.generate(Math::random).limit(10).forEach(System.out::println);
/**
  * 生成无限长度的Stream,和generator不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环
*/
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);
例子:
Stream.iterate(1, e->e+2).limit(10).forEach(System.out::println);
/**
  * 通过一个数组获取一个stream,元素为数组的元素,元素必须是对象,而不能是原始数据类型(int、double等)
*/
public static<T> Stream<T> of(T t)
例子:
String[] arrs = new String[]{"a","b","c","d"};
Stream.of(arrs).forEach(System.out::println);
/**
  * 通过多个对象入参获取一个stream,元素是入参对象
*/
public static<T> Stream<T> of(T... values);
例子:
Stream.of(new String("one"),new String("two")).forEach(System.out::println);

通过collection方法

可以用过集合的接口的默认方法,创建一个流;使用这个方法,包括继承Collection的接口,如:Set,List等等。

/**
 * 创建一个流
*/
default Stream<E> stream();

/**
 * 创建一个并行流
*/
default Stream<E> parallelStream()

例子:

List<String> strList = new ArrayList<String>();
strList.add("lin");
strList.add("wu");
strList.stream().forEach(System.out::println);
strList.parallelStream().forEach(System.out::println);

通过Arrays静态方法

/**
 * 通过一个数据获取流,这泛型的,还存在int、long和double类型
*/
public static <T> Stream<T> stream(T[] array);
例子:
Arrays.stream(new int[]{1,2,3,4}).forEach(System.out::println);

Stream通用语法

在这里插入图片描述
Stream的操作分为3部分:
第一部分是创建一个stream流(可以参照Stream创建方法)
第二部分是stream流的中间操作,返回的还是一个stream流(此部分可以有也可以没有)
第三部分是stream流的最终操作

Stream的常用方法

中间操作(转换)

无状态操作

map(转化元素)

/**
 * 通过实现Function函数接口,遍历转化元素
 */
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

filter(过滤元素)

/**
 * 通过实现Predicate函数接口,遍历元素,做过滤使用
 */
Stream<T> filter(Predicate<? super T> predicate);

flatMap(拆解元素)

/**
 * 将每个元素作为流传进Function中,最后归入父类流里面
 */
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

peek(监视元素)

/**
 * 此方法主要用于支持调试,您希望在元素流经管道中的某个点时查看元素
 */
Stream<T> peek(Consumer<? super T> action);

有状态操作

limit(截取元素)

/**
 * 将取流的前几个元素
 */
Stream<T> limit(long maxSize);

sorted(排序元素)

/**
 * 将元素通过自然排序,其元素必须实现Comparable接口
 */
Stream<T> sorted();

/**
 * 将元素通过自己实现的Comparable接口进行排序
 */
Stream<T> sorted(Comparator<? super T> comparator);

distinct(去重元素)

/**
 * 将元素做去重操作,使用equals作为比较
 */
Stream<T> distinct();

skip(跳过元素)

/**
 * 跳过前n个元素
 */
Stream<T> skip(long n);

最终操作(归纳(reduce))

非短路操作

max(最大值)

/**
 * 通过一个Comparator接口,返回最大值元素
 */
Optional<T> max(Comparator<? super T> comparator);

min(最小值)

/**
 * 通过一个Comparator接口,返回最小值元素
 */
Optional<T> min(Comparator<? super T> comparator);

count(数量)

/**
 * 获取元素个数
 */
long count();

reduce(归纳)
理解为归档函数,简单理解为最后获取一个归纳的值(可能是总和、最大值、最小值等),前面所有函数都可以通过reduce实现,而之所以有上面的函数,是因为常用才定义出来。

/**
 * identity为初始值,然后通过BinaryOperator操作,得到最后的值(与元素相同类型)
 */
T reduce(T identity, BinaryOperator<T> accumulator);
例子:实现sum操作
int[] arrInt = {1,3,4,2};
System.out.println("ints sum is:" + Arrays.stream(arrInt).reduce(0, (sum, item) -> sum + item));

/**
 * 没有初始值,然后通过BinaryOperator操作,得到最后的值(是一个Optional值)
 */
Optional<T> reduce(BinaryOperator<T> accumulator);
例子:实现max操作
int[] arrInt = {1,3,4,2};
System.out.println("ints max is:" + Arrays.stream(arrInt).reduce(0, (item1, item2) -> item1 >item2?item1:item2));

/**
 * identity为初始值
* 当stream是非并行操作时,用法与2个参数的reduce基本一样,通过BiFunction操作,得到最后的值(与元素的类型不一定相同)
* 当stream是并行操作时,先是每个线程使用初始值identity分别进行BiFunction操作,再将得到的每个结果进行BinaryOperator操作
 */
<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

例子1:通过非并行实现过滤filter操作

List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);		 BiFunction<ArrayList<Integer>,Integer,ArrayList<Integer>> bifun = new BiFunction<ArrayList<Integer>,Integer,ArrayList<Integer>>(){
			@Override
			public ArrayList<Integer> apply(ArrayList<Integer> t, Integer u) {
				if(u<5)
					t.add(u);
				return t;
			}
			 
		 };
BinaryOperator<ArrayList<Integer>> binaryOper = new BinaryOperator<ArrayList<Integer>>() {
			@Override
			public ArrayList<Integer> apply(ArrayList<Integer> strings, ArrayList<Integer> strings2) {
				return strings;  
			}
		};
System.out.println("ints filter less than 5 is:" + ints.stream().reduce(new ArrayList<Integer>(),bifun,binaryOper));

collect(收集)
可以理解与reduce一样,但是实现方式可能不同,reduce中通过BiFunction有返回值,而collect通过BiConsumer没有返回值,可能造成影响是对于原始数据类型和String,在collect中每一次BiConsumer操作都无法保留。reduce适合不可变容器归约,collect适合可变容器归约。collect适合并行。

/**
 * supplier为初始值,但与reduce不同是它接受一个表达式
* accumulator,对每个元素进行操作
* combiner,将所有线程结果进行combiner操作
 */
<R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
/**
 * 通过Collector工具类实现与上面方法一致效果
 */
<R, A> R collect(Collector<? super T, A, R> collector);

例子:实现filter功能

List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints filter less than 5 is:" + ints.stream().parallel().collect(()->new ArrayList<Integer>(), (array, ar) -> {if (ar<5) array.add(ar);}, (array1, array2) -> array1.addAll(array2)));

短路操作

findFirst(第一个元素)

/**
 * 获取第一个元素
 */
Optional<T> findFirst();

findAny(任意一个元素)

/**
 * 返回这个集合中,取到的任何一个对象。在串行流中取第一个,在并行流中随机取。
 */
Optional<T> findAny();

anyMatch(任何一个元素符合条件)

/**
 * 元素中只要一个符合predicate条件,即返回true
 */
boolean anyMatch(Predicate<? super T> predicate);

allMatch(所有元素符合条件)

/**
 * 所有元素中都符合predicate条件,即返回true
 */
boolean allMatch(Predicate<? super T> predicate);

noneMatch(没有元素符合条件)

/**
 * 所有元素中都不符合predicate条件,即返回true
 */
boolean noneMatch(Predicate<? super T> predicate);

Comparator

Comparator接口,提供了一下静态方法获取comparator。
1) Comparator.reverseOrder()) 自然排序的逆向排序
2) Comparator.comparing(Student::getAge))使用某个属性排序
3) Comparator.comparing(Student::getAge).reversed(),逆向排序
一般用于sort操作

Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串。一般用于collect操作中。
toList(转换为List)

/**
 * 将stream转化为一个List
 */
public static <T> Collector<T, ?, List<T>> toList();

toSet(转换为Set)

/**
 * 将stream转化为一个Set
 */
public static <T> Collector<T, ?, Set<T>> toSet();

toMap(转换为Map)

/**
 * 将stream转化为一个Map
* keyMapper,是获取key的一个function,如果元素的key存在一样,会报java.lang.IllegalStateException。这是由于没有传入mergeFunction,使用默认的throwingMerger。
* valueMapper,是获取value的一个function
 */
public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper);

/**
 * 将stream转化为一个Map
* keyMapper,是获取key的一个function,如果元素的key存在一样,会报java.lang.IllegalStateException
* valueMapper,是获取value的一个function
* mergeFunction,一个BinaryOperator,解决一个重复key报错问题
 */
public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction);

/**
 * 将stream转化为一个Map
* keyMapper,是获取key的一个function,如果元素的key存在一样,会报java.lang.IllegalStateException
* valueMapper,是获取value的一个function
* mergeFunction,一个BinaryOperator,解决一个重复key报错问题
* mapSupplier,指定返回Map类型,如果没有,默认为HashMap。
 */
public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                                Function<? super T, ? extends U> valueMapper,
                                BinaryOperator<U> mergeFunction,
                                Supplier<M> mapSupplier)

例子:

List<Person> list = new ArrayList<Person>();
list.add(new Person(1, "haha"));
list.add(new Person(2, "rere"));
list.add(new Person(3, "fefe"));
list.add(new Person(1, "linwu"));
list.add(new Person(4, null));
Map<Integer, Person> mapp = list.stream().collect(Collectors.toMap(Person::getId, Function.identity(),(k,v)->v));
System.out.println(mapp);
System.out.println(mapp.get(1).getName());

averaging(获取平均值)
有三种,分别为averagingInt、averagingLong、averagingDouble。返回平均值。
summarizing(获取统计信息)
有三种,分别为summarizingInt、summarizingLong、summarizingDouble。返回一个统计信息,包括个数,总数等。
groupingBy(分组)
分组功能,返回一个Map,key为泛型,value为一个List。有三种类型,可以支持多层分组。
partitioningBy(分区)
分区功能,是分组功能的一种特殊情况,其map的key是boolean,所以只有true和false。可以支持多层分组。

并行操作

根据预估的数据量获取最小处理单元的大小阈值,即当数据量已经小于这个阈值的时候进行计算,否则进行fork 将任务划分成更小的数据块,进行求解。这里值得注意的是,getTargetSize 在第一次调用的时候会设置:
预测数据量大小 / (默认并发度 * 4) 的结果作为最小执行单元的数量(配置的默认值是cpu 数 – 1,可以通过java.util.concurrent.ForkJoinPool.common.parallelism设置)

其他说明

Optional操作

通常聚合操作会返回一个Optional类型,Optional表示一个安全的指定结果类型,所谓的安全指的是避免直接调用返回类型的null值而造成空指针异常,调用optional.ifPresent()可以判断返回值是否为空,或者直接调用ifPresent(Consumer<? super T> consumer)在结果部位空时进行消费操作;调用optional.get()获取返回值。

猜你喜欢

转载自blog.csdn.net/linwu_2006_2006/article/details/95029004