JAVA基础7-Stream基础学习笔记
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()获取返回值。
- Java2核心技术第七版的学习笔记(三) Fundamental Programming Structures in Java(Java语言的基础)(四):
- Java学习笔记(三、面向对象编程基础)
- 我的Java基础的学习笔记
- 整理JAVA学习笔记 JAVA基础需要掌握重点
- Java学习笔记之网络编程基础-获取本机名称
- java基础学习笔记
- 零基础小白JAVA学习笔记(五)
- Java学习笔记(一、Java语言基础)
- java学习笔记-hibernate基础(1)
- Java学习笔记之网络编程基础-通过URLConnection获取HTML页面
- java基础知识学习笔记1
- C\C++ 程序员从零开始学习Android - 个人学习笔记(二) - java基础 - 从源代码到运行
- jdk的配置和JVM内部原理 java 基础学习笔记 第一天
- 零基础小白JAVA学习笔记(六)
- android 学习笔记(四) 4.1 java编程基础
- 传智博客学习笔记4--JAVA编程基础1
- JAVA语言学习笔记之JAVA语言基础
- 【JAVA学习笔记】面向对象基础
- Java学习笔记之语言基础—— 浮点数的取模运算
- Java基础学习笔记(二)常用类String