java 8 Stream流的基本使用、部分源码(学习笔记)

Stream

CSDN地址:https://blog.csdn.net/qq_38706585/article/details/101029041
复制代码

stream 是什么

​ 流是Java 8的新成员,容许以声明式来处理数据的集合(经过查询语句来表示,并非像之前的同样写一个方法来处理),就如今来讲,你能够把它们当作遍历数据集的高级迭代器。html

有下面这个集合YxUser这个pojo类记录的用户的基本信息,假如咱们要查找出年龄在18-30的用户的姓名(而且按照年龄降序)在之前咱们是怎么作的?java

在这里插入图片描述

java7:mysql

List<YxUser> ageList = new ArrayList<>();
        for (YxUser y : list) {
            if (y.getAge() <= 30 && y.getAge() >= 18) {
                ageList.add(y);
            }
        }
        Collections.sort(ageList, new Comparator<YxUser>() {
            @Override
            public int compare(YxUser d1, YxUser d2) {
                return Integer.compare(d1.getAge(), d2.getAge());
            }
        });
        List<String> name = new ArrayList<>();
        for (YxUser d : ageList) {
            name.add(d.getUsername());
        }
复制代码

虽然这个方式可以实现咱们想要的东西,可是,咱们使用了好几个变量,而且他们的做用只有一次,可是这个写感受比较麻烦。若是别人要 id在3-9之间的的用户的姓名呢,这些又要改动。-->java8使用流git

java8:github

List<String> collect = list.stream()
                .filter(e -> e.getAge() <= 30 && e.getAge() >= 18)
                .sorted(comparing(YxUser::getAge))
                .map(YxUser::getUsername)
                .collect(Collectors.toList());
System.out.println("collect: " + collect);
复制代码

而且这段代码能够利用多核框架执行算法

List<String> collect = list.parallelStream()
             .filter(e -> e.getAge() <= 30 && e.getAge() >= 18)
             .sorted(comparing(YxUser::getAge))
             .map(YxUser::getUsername)
             .collect(Collectors.toList());
System.out.println("collect: " + collect);
复制代码

在java8中:sql

1.代码是以声明式方式来写的:想完成什么(filter筛选年龄) 而不是像java7同样若是来实现(利用循环和if来控制)。若是有新的需求,那么不用从新复制代码,只须要更改主要条件便可。设计模式

2.把几个基础操做连接起来,来表达复杂的数据处理流水线(在filter后面接上sorted、map和collect操做,示意图以下),同时保持代码清晰可读。filter的结果被传给了sorted方法,再传给map方法,最后传给collect方法api

在这里插入图片描述

并且新的stream api 表达能力特别强,写出来的东西别人可能不会,可是别人可以看懂你要作什么。bash

stream 特色

声明性:简洁、易读

声明性:简洁、易读

可复用:比较灵活

可并行:性能更好

流和集合的关系

在这里插入图片描述

流和集合的迭代器相似,只可以遍历一次。遍历完成后说明这个流已经被消费了。

stream:stream是只计算当前须要的数据,在迭代过程当中,stream是放在内部迭代的,集合的迭代是放在外部。在外部迭代就会须要本身解决管理并行的问题。

外部迭代与内部迭代

外部迭代 :使用Collection接口须要用户去作迭代(好比用for-each)。

内部迭代: 使用Streams库,它帮你把迭代作了,还把获得的流值存在了某个地方,你只要给出一个函数说要干什么就能够了。

在上一个例子的筛选年龄18-30的两种方法,第一个方法就是外部迭代,第二个方法就是内部迭代。

在这里插入图片描述

二者的区别:

外部迭代:

  1. Java 的 for-each 循环/迭代本质上是有序的,它必须按照容器指定的顺序处理元素。
  2. 它限制了 JVM控制流程的可能性,而正是这种可能性使得 JVM 能够经过从新排序、并行处理、短路、延迟处理来提供更好的性能。

内部迭代:

  1. 用户代码只需关注解决问题,无需关注如何解决的细节,从而变得更加清晰了。
  2. 内部迭代使得 JVM 利用短路、并行处理和乱序执行来提高性能成为可能(JVM 是否利用了这些来优化性能取决于 JVM 实现自己,可是有了内部迭代这些至少是可能的,而对于外部迭代来讲则是不可能的)

流的操做

在这里插入图片描述

上图有两个操做

  • filter、map、sorted连成一个流水线。

  • collect 触发流水线执行而且关闭它。

链接起来的流称为 中间操做,关闭流的操做称为终端操做。

中间操做:它操做后会返回另一个流,让多个操做能够链接起来造成一个查询。重要的是,除非流水线上触发一个终端操做,不然中间操做不会执行任何处理——这是由于中间操做通常均可以合并起来,在终端操做时一次性所有处理。

终端操做:他会从流的流水线生成一个结果。结果能够是任何东西(void\integer\list....),可是不能是流。

在这里插入图片描述

Stream使用

筛选

用到的例子

List<Integer> integerList =Arrays.asList(1,2,2,2,2,2,4,5,6,7,8);


 List<YxUser> list = Arrays.asList(
                new YxUser(1, "yanxgin", 12, "8237251670@qq.com", 1, true),
                new YxUser(2, "caoxindi", 16, "2737827527@qq.com", 1, false),
                new YxUser(3, "zhangsan", 18, "334899245@qq.com", 0, true),
                new YxUser(4, "lisi", 23, "774892034@qq.com", 0, false),
                new YxUser(5, "wangwu", 66, "43892475266@qq.com", 1, false),
                new YxUser(6, "zhaoliu", 46, "54654742@qq.com", 0, false),
                new YxUser(7, "liuqi", 30, "54375396@qq.com", 1, true)
        );
复制代码

**谓词筛选filter **

List<String> collect = list.stream()
        .filter(e -> e.getId() > 2)  //谓词筛选
        .collect(Collectors.toList()); // 终端操做 
复制代码

在这里插入图片描述

distinct顾名思义:去掉重复的。

integerList.stream()
           .filter(i->i%2==0)
           .distinct()
           .forEach(System.out::println);
//输出: 2,4,6,8
复制代码

在这里插入图片描述

limit:返回前N个数据,相似mysql的limit上。

integerList.stream()
        .sorted()
        .limit(3)
        .forEach(System.out::println);
//排序后将输出前三个,原来的数据 :1,2,2,2,2,2,4,5,6,7,8
// 输出:1 2 2
复制代码

skip:过滤掉前n个元素。

integerList.stream()
        .sorted()
        .skip(2)
        .limit(2)
        .forEach(System.out::println); 
//排序后,先过滤前两个,在输出前两个。实际输出的是第3,4两个。
// 原来的数据 :1,2,2,2,2,2,4,5,6,7,8
// 输出:2 2
复制代码

映射

map:通常的用法:map就是取其中的一列

List<String> collect = list.stream()
        .filter(e -> e.getId() > 2)  // 中间操做
        .map(YxUser::getUsername) // 中间操做
        .collect(Collectors.toList()); // 终端操做
// 输出:
// collect: [zhangsan, lisi, wangwu, zhaoliu, liuqi]

复制代码

flatMap:流的扁平化

例子:给定单词列表["Hello","World"],你想要返回列表["H","e","l", "o","W","r","d"]。

List<String> strings = Arrays.asList("Hello", "World");

List<String[]> collect1 = strings.stream()
                .map(e -> e.split(""))
                .distinct()
                .collect(Collectors.toList());
System.out.println("collect1 : " + collect1);
// 输出发现:collect1 : [[Ljava.lang.String;@799f7e29, [Ljava.lang.String;@4b85612c]
// 并非咱们想要的

复制代码

这个方法的问题在于,传递给map方法的Lambda为每一个单词返回了一个String[](String列表) 。所以,map返回的流其实是Stream<String[]>类型的。你真正想要的是用Stream来表示一个字符流。

在这里插入图片描述

在这里插入图片描述

flatMap登场

List<String> collect2 = strings.stream()
                .map(e -> e.split(""))
                .flatMap(Arrays::stream)// 将各个流生成为扁平化的单个流
                .distinct()
                .collect(Collectors.toList());
// 输出:collect2 : [H, e, l, o, W, r, d]

复制代码

在这里插入图片描述

在这里插入图片描述

匹配

anyMatch表示数据集中是否是有一个元素可以匹配给定的谓词

allMatch 表示流中的元素是否都可以匹配给定的谓词

noneMatch 表示流中没有匹配改给定的谓词

这三个方法都返回一个Boolearn,代表根据操做存在或者不存在。

boolean isHealthy = menu.stream()
						.allMatch(d -> d.getAge() < 30);

复制代码

查找

findAny方法表示返回当前流中的任意元素

Optional<YxUser> any = list.stream()
        .filter(e -> e.getId() > 5)
        .findAny();

复制代码

Optional:是一个容器类,表示一个值存在仍是不存在,避免findAny找不到值的时候致使null的状况

isPresent :表示optional包含值的时候返回true,反之false

ifPresent(Consumer t) :表示存在时,执行存在的代码块

T get()会在值存在时返回值,不然抛出一个NoSuchElement异常。T orElse(T other)会在值存在时返回值,不然返回一个默认值

查找第一个元素 findFirst

Optional<Integer> first = integerList.stream()
                .map(x -> x * x)
                .filter(x -> x % 3 == 0)
                .findFirst();

复制代码

归约

reduce:首先要有一个初始值,还有第二个参数是执行规约的规则

List<Integer> integerList = Arrays.asList(1, 2, 2, 2, 2, 2, 4, 5, 6, 7, 8);
Integer reduce = integerList.stream()
        .reduce(0, (x, y) -> x + y);
Integer reduce = integerList.stream()
        .reduce(0, Integer::sum);
这两个是同样的 还有Integer::MAX和MIN,初始值要注意

复制代码

在这里插入图片描述

Integer::MAX和MIN

max和min不须要初始值,可是接受的时候,须要用Optional来接收

Optional<Integer> reduce = integerList.stream()
     .reduce(Integer::max);
System.out.println("reduce: "+ reduce.get());

复制代码

在这里插入图片描述

数值流

收集器使用groupingBy:经过用户的用户名进行分组以下

Map<String, List<YxUser>> collect3 = list.stream()
        .collect(groupingBy(YxUser::getUsername));

/** 输出 collect3: { lisi=[com.yangxin.demo.model.YxUser@69663380], liuqi=[com.yangxin.demo.model.YxUser@5b37e0d2], yanxgin=[com.yangxin.demo.model.YxUser@4459eb14], caoxindi=[com.yangxin.demo.model.YxUser@5a2e4553], zhaoliu=[com.yangxin.demo.model.YxUser@28c97a5], zhangsan=[com.yangxin.demo.model.YxUser@6659c656], wangwu=[com.yangxin.demo.model.YxUser@6d5380c2]} */

/*** *多级分组 *首先按照性别分组,而后按照id分组。 */
Map<Integer, Map<String, List<YxUser>>> collect4 = list.stream()
.collect(groupingBy(YxUser::getSex, // 一级分类函数
groupingBy(e -> { // 二级函数
    if (e.getId() > 5) return "hight";
    else if (e.getId() < 4) return "small";
    else return "midle";
})));
/** 输出 collect4: { lisi={midle=[com.yangxin.demo.model.YxUser@69663380]}, liuqi={hight=[com.yangxin.demo.model.YxUser@5b37e0d2]}, yanxgin={small=[com.yangxin.demo.model.YxUser@4459eb14]}, caoxindi={small=[com.yangxin.demo.model.YxUser@5a2e4553]}, zhaoliu={hight=[com.yangxin.demo.model.YxUser@28c97a5]}, zhangsan={small=[com.yangxin.demo.model.YxUser@6659c656]}, wangwu={midle=[com.yangxin.demo.model.YxUser@6d5380c2]}} */


// 按照子组收集数据
Map<Integer, Long> collect = list.stream()
        .collect(groupingBy(YxUser::getSex, counting()));
/** * counting 能够换成maxBy、minBy */

复制代码

若是是本身写的话,会嵌套多层循环,多级分组那么将会更难维护。

使用Collector收集数据

maxBy和minBy在collect中使用,可是参数是自定义的Comparator

Comparator<YxUser> comparator=Comparator.comparingInt(YxUser::getId);
Optional<YxUser> collect = list.stream()
        .collect(minBy(comparator));
// 使用reducing
Optional<YxUser> mostCalorieDish = list.stream().
collect(reducing( (d1, d2) -> d1.getId() < d2.getId() ? d1 : d2));

复制代码

summingInt,在collect中计算总和。

Integer collect = list.stream().collect(summingInt(YxUser::getId));

// 若是使用reducing
int total = list.stream()
    .collect(reducing( 0, //初始值
						YxUser::getId,//转换函数
						Integer::sum);//累积函数
//第一个参数是归约操做的起始值,也是流中没有元素时的返回值,因此很显然对于数值和而言0是一个合适的值。
//第二个参数就是你在6.2.2节中使用的函数,将菜肴转换成一个表示其所含热量的int。
//第三个参数是一个BinaryOperator,将两个项目累积成一个同类型的值。这里它就是对两个int求和

             
int sum=list.stream()
             .mapToInt(YxUSer::getId)
             .sum();

复制代码

还有相似的函数:averagingInt计算平均值

可是还能够经过summarizingInt能够一次性获得:对应的最大值、最小值、平均值、和、数量等信息,能够经过getter获取

在这里插入图片描述

joining链接字符串

​ joining实现字符串链接,是使用的StringBuilder,进行字符串拼接的

String collect1 = list.stream().map(YxUser::getUsername).collect(joining());
System.out.println("collect1:" + collect1);
// 添加分割符
String collect2 = list.stream().map(YxUser::getUsername).collect(joining(", "));
System.out.println("collect2:" + collect2);

复制代码

输出效果:

在这里插入图片描述

求和的几种形式

list.stream().mapToInt(YxUser::getId).sum();

list.stream().map(YxUser::getId).reduce(Integer::sum).get();

list.stream().collect(reducing(0, YxUser::getId, Integer::sum));

list.stream().collect(reducing(0, YxUser::getId, (x, y) -> x + y));

复制代码

字符串拼接的几种形式

list.stream().map(YxUser::getUsername).collect(reducing((s1,s2)->s1+s2)).get();

list.stream().collect(reducing("",YxUser::getUsername,(s1,s2)->s1+s2));

String collect2 = list.stream().map(YxUser::getUsername)
.collect(joining(", "));
// 从性能上考虑,建议使用joining

复制代码

partitioningBy分区函数

返回的主键是 boolean类型,只有true和false两种状况。分区其实就是分组的一种特殊状况。

Map<Boolean, List<YxUser>> collect = list.stream()
.collect(partitioningBy(YxUser::isX));
System.out.println("collect: " + collect);

复制代码

函数大全

在这里插入图片描述
在这里插入图片描述

收集器Collector源码

在这里插入图片描述

Collector 首先有5个主要的函数:supplier、accumulator、combiner、finisher、characteristics。

supplier :调用这个函数的时候会建立一个空的累加器实例,供数据收集使用。

/** 官方的解释 * A function that creates and returns a new mutable result container. * * @return a function which returns a new, mutable result container */
Supplier<A> supplier();

复制代码

accumulator : accumulator函数至关因而一个累加器,进行中间结果的处理。当遍历到流中第n个元素时,这个函数执行,时会有两个参数:保存归约结果的累加器(已收集了流中的前 n-1 个项目),还有第n个元素自己

/** 官方的解释 * A function that folds a value into a mutable result container. * * @return a function which folds a value into a mutable result container */
BiConsumer<A, T> accumulator();

复制代码

finisher : finisher函数主要是最后的工做,主要是将最后的结果进行转换。finisher方法必须返回在累积过程的最后要调用的一个函数,以便将累加器对象转换为整个集合操做的最终结果。

/** * Perform the final transformation from the intermediate accumulation type * {@code A} to the final result type {@code R}. * * <p>If the characteristic {@code IDENTITY_TRANSFORM} is * set, this function may be presumed to be an identity transform with an * unchecked cast from {@code A} to {@code R}. * * @return a function which transforms the intermediate result to the final * result */
Function<A, R> finisher();

复制代码

supplier、accumulator、finisher这三个函数就彻底够流的顺序归约了

在这里插入图片描述

combiner:combiner方法会返回一个供归约操做使用的函数,它定义了对流的各个子部分进行并行处理时,各个子部分归约所得的累加器要如何合并。

/** * A function that accepts two partial results and merges them. The * combiner function may fold state from one argument into the other and * return that, or may return a new result container. * * @return a function which combines two partial results into a combined * result */
BinaryOperator<A> combiner();

复制代码

supplier、accumulator、finisher这三个函数加上combiner这个函数,能够对流进行并行归约了,有点至关于并发环境的fork/join框架,他主要的步骤有一下几步:

第一步:将原始的流分红子流,知道条件不能分为止(分得过小也很差)

第二步:全部的子流并行运行。

第三步:使用收集器combiner方法返回的函数,将全部的部分合并。

这个流程和并发的fork/join差很少,能够参考我一篇博客:www.cnblogs.com/yangdagaoge…

characteristics :这个方法就是返回一个不可变的Characteristics,表示收集器的行为:

​ ①:UNORDERED——归约结果不受流中项目的遍历和累积顺序的影响

​ ②:CONCURRENT——accumulator函数能够从多个线程同时调用,且该收集器能够并行归约流。

​ ③:IDENTITY_FINISH——这代表完成器方法返回的函数是一个恒等函数,能够跳过。

可是点击Collector的实现类的时候发现他只有一个Collectors实现类而且在Collectors中定义了一个内部类CollectorImpl,其中的实现特别简单。以下:

/** * Simple implementation class for {@code Collector}. * * @param <T> the type of elements to be collected * @param <R> the type of the result */
    static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
        // 一系列的成员函数
        private final Supplier<A> supplier;
        private final BiConsumer<A, T> accumulator;
        private final BinaryOperator<A> combiner;
        private final Function<A, R> finisher;
        private final Set<Characteristics> characteristics;

        CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Function<A,R> finisher,
                      Set<Characteristics> characteristics) {
            this.supplier = supplier;
            this.accumulator = accumulator;
            this.combiner = combiner;
            this.finisher = finisher;
            this.characteristics = characteristics;
        }

        CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Set<Characteristics> characteristics) {
            this(supplier, accumulator, combiner, castingIdentity(), characteristics);
        }

        @Override
        public BiConsumer<A, T> accumulator() {
            return accumulator;
        }

        @Override
        public Supplier<A> supplier() {
            return supplier;
        }

        @Override
        public BinaryOperator<A> combiner() {
            return combiner;
        }

        @Override
        public Function<A, R> finisher() {
            return finisher;
        }

        @Override
        public Set<Characteristics> characteristics() {
            return characteristics;
        }
    }

复制代码

1.toList源码

/** * Returns a {@code Collector} that accumulates the input elements into a * new {@code List}. There are no guarantees on the type, mutability, * serializability, or thread-safety of the {@code List} returned; if more * control over the returned {@code List} is required, use {@link #toCollection(Supplier)}. * * @param <T> the type of the input elements * @return a {@code Collector} which collects all the input elements into a * {@code List}, in encounter order */
    public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new,
                        //建立一个ArrayList类型的Supplier收集器
        				List::add,// 使用list的add函数将流中的数据添加到空结果容器中
                        (left, right) -> { left.addAll(right); return left; },
                        // lambda 表达式,将右边的list添加到左边的list中,这就是至关于一个combiner函数
                        CH_ID);// 表示收集器的行为参数
    }

复制代码

使用toList

List<User> collect = list.stream().collect(Collectors.toList());

复制代码

toSet 的源码也是类型,不过吧Supplier 换成了 (Supplier<Set>) HashSet::new

/** * Returns a {@code Collector} that accumulates the input elements into a * new {@code Set}. There are no guarantees on the type, mutability, * serializability, or thread-safety of the {@code Set} returned; if more * control over the returned {@code Set} is required, use * {@link #toCollection(Supplier)}. * * <p>This is an {@link Collector.Characteristics#UNORDERED unordered} * Collector. * * @param <T> the type of the input elements * @return a {@code Collector} which collects all the input elements into a * {@code Set} */
 public static <T>
 Collector<T, ?, Set<T>> toSet() {
     return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
                                (left, right) -> { left.addAll(right); return left; },
                                CH_UNORDERED_ID);
 }

复制代码

2. 字符拼接joining源码

①.无分隔符

/** * Returns a {@code Collector} that concatenates the input elements into a * {@code String}, in encounter order. * * @return a {@code Collector} that concatenates the input elements into a * {@code String}, in encounter order * * CharSequence:这个是字符串序列接口 * joining的源码可得,实现字符串拼接是使用 StringBuilder实现的, */
    public static Collector<CharSequence, ?, String> joining() {
        return new CollectorImpl<CharSequence, StringBuilder, String>(
                // 建立StringBuilder的结果容器
            	// StringBuilder::append:拼接函数(累加器部分)
                StringBuilder::new, StringBuilder::append,
            	// 联合成一个值,combiner部分
                (r1, r2) -> { r1.append(r2); return r1; },
            	// 最后结果的转换
                StringBuilder::toString, CH_NOID);
    }


复制代码

CharSequence 这是个字符串的序列接口,String、StringBuffer、StringBuilder也是实现这个接口。它和String的区别就是,String可读不可变,CharSequence是可读可变

在这里插入图片描述

使用字符串拼接

static List<User> list = Arrays.asList(
            new User("y杨鑫", 50, 5455552),
            new User("张三", 18, 66666), 
            new User("李四", 23, 77777),
            new User("王五", 30, 99999),
            new User("赵柳", 8, 11111),
            new User("王八蛋", 99, 23233)
    );

    public static void main(String[] args) {

        String collect = list.stream().map(User::getUsername)
                .collect(joining());
        System.out.println("collect: " + collect);
    }
////////////////////////////////////////输出/////////////////////////
collect: y杨鑫张三李四王五赵柳王八蛋

复制代码

②.带分割符的

/** * Returns a {@code Collector} that concatenates the input elements, * separated by the specified delimiter, in encounter order. * 返回一个带分割符的拼接串 * @param delimiter the delimiter to be used between each element * @return A {@code Collector} which concatenates CharSequence elements, * separated by the specified delimiter, in encounter order * 将分割符传给了joining三参数的重载函数 */
    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter){
        return joining(delimiter, "", "");
    }

	/** * Returns a {@code Collector} that concatenates the input elements, * separated by the specified delimiter, with the specified prefix and * suffix, in encounter order. * * @param delimiter the delimiter to be used between each element * @param prefix the sequence of characters to be used at the beginning * of the joined result * @param suffix the sequence of characters to be used at the end * of the joined result * @return A {@code Collector} which concatenates CharSequence elements, * separated by the specified delimiter, in encounter order * * 在这个函数中,使用了一个叫StringJoiner的类,这个是java8的封装类,主要的功能是 * 按照 分割符delimiter,字符串开始 prefix,字符串结尾suffix,进行字符串的拼接 */
    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix) {
        return new CollectorImpl<>(
            	// 建立一个Supplier结果容器
                () -> new StringJoiner(delimiter, prefix, suffix),
            	// 字符串的添加至关于 accumulator累加器部分;merge是联合将两个数值整合成一个,至关于combiner部分
                StringJoiner::add, StringJoiner::merge,
            	// toString作最后的结果转换
                StringJoiner::toString, CH_NOID);
    }

复制代码

运行样例

String collect = list.stream().map(User::getUsername)
                .collect(joining());
        System.out.println("collect: " + collect);
        
        String collect1 = list.stream().map(User::getUsername)
                .collect(joining(","));
        System.out.println("collect1: " + collect1);
        
        String collect2 = list.stream().map(User::getUsername)
                .collect(joining(",","[","]"));
        System.out.println("collect2: " + collect2);
///////////////////////输出//////////////////////////////
collect: y杨鑫张三李四王五赵柳王八蛋
collect1: y杨鑫,张三,李四,王五,赵柳,王八蛋
collect2: [y杨鑫,张三,李四,王五,赵柳,王八蛋]


复制代码

StringJoiner源码:

public final class StringJoiner {
 /** * prefix:表示字符串拼接的前缀 * suffix:表示字符串拼接的结尾 * delimiter: 表示分割符 * */
 private final String prefix;
 private final String delimiter;
 private final String suffix;

 /* * StringBuilder的值。构造器从prefix开始添加元素,delimiter分割,可是没有 * 结尾符suffix,那么咱们每次会更容易的去拼接字符串 */
 private StringBuilder value;

 /* * 默认状况,由prefix和suffix拼接的字符串,在返回值的时候使用toString转换。 * 当没有元素添加的时候,那么这个为空,这颇有可能被用户去覆盖一些其余值,包括空串 */
 private String emptyValue;

 /** * 构造器只有delimiter分隔符,prefix和suffix将默认为空串, */
 public StringJoiner(CharSequence delimiter) {
     this(delimiter, "", "");
 }

 /** * 三参数的构造器 */
 public StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix) {
     Objects.requireNonNull(prefix, "The prefix must not be null");
     Objects.requireNonNull(delimiter, "The delimiter must not be null");
     Objects.requireNonNull(suffix, "The suffix must not be null");
     // make defensive copies of arguments
     this.prefix = prefix.toString();
     this.delimiter = delimiter.toString();
     this.suffix = suffix.toString();
     this.emptyValue = this.prefix + this.suffix;
 }

 /** * 设置空值 */
 public StringJoiner setEmptyValue(CharSequence emptyValue) {
     this.emptyValue = Objects.requireNonNull(emptyValue,
             "The empty value must not be null").toString();
     return this;
 }

 /** * * 重写的toString,字符串将是prefix开始,suffix结尾,除非没有添加任何元素,那 * 么就返回空值 */
 @Override
 public String toString() {
     if (value == null) {
         return emptyValue;
     } else {
         if (suffix.equals("")) {
             return value.toString();
         } else {
             int initialLength = value.length();
             String result = value.append(suffix).toString();
             // reset value to pre-append initialLength
             value.setLength(initialLength);
             return result;
         }
     }
 }

 /** * 添加一个拼接的串 * * @param newElement The element to add * @return a reference to this {@code StringJoiner} */
 public StringJoiner add(CharSequence newElement) {
     prepareBuilder().append(newElement);
     return this;
 }

 /** * 将拼接的字串合并 */
 public StringJoiner merge(StringJoiner other) {
     Objects.requireNonNull(other);
     if (other.value != null) {
         final int length = other.value.length();
         // lock the length so that we can seize the data to be appended
         // before initiate copying to avoid interference, especially when
         // merge 'this'
         StringBuilder builder = prepareBuilder();
         builder.append(other.value, other.prefix.length(), length);
     }
     return this;
 }

 private StringBuilder prepareBuilder() {
     if (value != null) {
         value.append(delimiter);
     } else {
         value = new StringBuilder().append(prefix);
     }
     return value;
 }

 /** * 返回长度 */
 public int length() {
     // Remember that we never actually append the suffix unless we return
     // the full (present) value or some sub-string or length of it, so that
     // we can add on more if we need to.
     return (value != null ? value.length() + suffix.length() :
             emptyValue.length());
 }
}

复制代码

测试

StringJoiner joiner = new StringJoiner(",", "[", "]");
     for (YxUser x : list) {
         joiner.add(x.getUsername());
     }
     joiner.merge(joiner);
		// 若是没有merge将输出:joiner: [yanxgin,12,yan34xgin,56,78,90,666]

/** 使用joiner.merge(joiner),将输出joiner: [yanxgin,12,yan34xgin,56,78,90,666,yanxgin,12,yan34xgin,56,78,90,666],使用merge将另一个的StringJoiner合并进来,因此在这儿,他将已经又合并了一次 */
     System.out.println("joiner: " + joiner);

复制代码

3.toCollection源码

/** * * 返回一个{@Code Collector}收集器(输入的元素累加到新的收集器) * {@Code Collection}集合是由工厂建立 * * @param <T> 输入类型 * @param <C> 收集器{@code Collection}的结果类型 * @param 这个集合工厂collectionFactory将返回一个新的适当类型的收集器 * @return 按照顺序将输入的元素收集到一个{@Code Collector}而且返回 * * 函数功能:按照collectionFactory收集器的类型从新收集流中的数据, * 例如: * {@Code * LinkedList<YxUser> collect1 = list.stream().collect(Collectors.toCollection(LinkedList::new)); * // LinkedList能够换成Collection的其余集合类型 * } */
    public static <T, C extends Collection<T>>
    Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
        return new CollectorImpl<>(collectionFactory, Collection<T>::add,
                (r1, r2) -> { r1.addAll(r2); return r1; },
                CH_ID);
    }

复制代码

实现列子

// toCollection的参数的意义就是建立一个类型的集合来收集他。
        list.stream().collect(Collectors.toCollection(LinkedList::new));
        list.stream().collect(Collectors.toCollection(TreeSet::new));
        ......

复制代码

toList、toSet、toCollection区别:

​ toList:表示能够重复、有序。

​ toSet:表示不可重复、无序。

​ toCollection:自定义实现Collection的数据结构收集。

4. mapping源码

/** * Adapts a {@code Collector} accepting elements of type {@code U} to one * accepting elements of type {@code T} by applying a mapping function to * each input element before accumulation. * * 在输入元素的累加前,使用mapping函数将一个接受U类型({@code U})的收集器调 * 整为接受T类型({@code T})的收集器。**感受翻译不太对。 * * @apiNote * {@code mapping()} mapping适用于多层次的筛选, * 例如,Person实体类集合中,计算出每一个城市的姓名、 * <pre>{@code * Map<City, Set<String>> lastNamesByCity * = people.stream().collect(groupingBy(Person::getCity, * mapping(Person::getLastName, toSet()))); * }</pre> * * @param <T> 输入元素的类型。 * @param <U> 接受元素的类型 * @param <A> 收集器的中间累加器的类型 * @param <R> 收集器的结果类型 * @param 应用于输入元素的函数 * @param downstream 收集器接受一个mapper的值 * @return a collector which applies the mapping function to the input * elements and provides the mapped results to the downstream collector */
    public static <T, U, A, R>
    Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
                               Collector<? super U, A, R> downstream) {
        BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
        return new CollectorImpl<>(downstream.supplier(),
                (r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)),
                downstream.combiner(), downstream.finisher(),
                downstream.characteristics());
    }

复制代码

样例

// 获取邮件的性别
list.stream().collect(groupingBy(YxUser::getEmail, mapping(YxUser::getSex, toList())));

复制代码

5.collectingAndThen

/** * * 调整一个收集器{@Code Collector} 去完成一个额外的转换。例如, * {@link #toList()}的调节使得收集器老是产生一个不可变的list。 * <pre>{@code * List<String> people * = people.stream().collect(collectingAndThen(toList(), Collections::unmodifiableList)); * }</pre> * * @param <T> 输入元素的类型 * @param <A> downstream collector收集器中间累加的结果类型 * @param <R> downstream collector收集器的结果 * @param <RR> 结果收集器的类型 * @param downstream a collector * @param finisher 是一个完成最终功能的函数 * @return 返回一个收尾完成的结果搜集器 */
    public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) {
        // 获取收集器的行为特性。
        Set<Collector.Characteristics> characteristics = downstream.characteristics();
        // 若是这个搜集器 是一个恒等函数
        if (characteristics.contains(Collector.Characteristics.IDENTITY_FINISH)) {
            if (characteristics.size() == 1)
                // 长度若是为1,那么置空
                characteristics = Collectors.CH_NOID;
            else {
                // 使用EnumSet枚举集合类来建立一个具备characteristics特征的枚举。
                characteristics = EnumSet.copyOf(characteristics);
                // 去掉恒等行为
                characteristics.remove(Collector.Characteristics.IDENTITY_FINISH);
                //unmodifiableSet 表示是不可改的Set,与此相似的还有 unmodifiableMap
                characteristics = Collections.unmodifiableSet(characteristics);
            }
        }
        /////不是太明白为何要作这个if的操做。
        return new CollectorImpl<>(downstream.supplier(),
                downstream.accumulator(),
                downstream.combiner(),
                downstream.finisher().andThen(finisher),
                characteristics);
    }

复制代码

获取性别分组中最高的Id

list.stream().collect(groupingBy(YxUser::getSex, collectingAndThen(maxBy(Comparator.comparingInt(YxUser::getId)), Optional::get)));

复制代码

输出

collect3: {0=YxUser{id=6, username='90', password='222', lastVisitTime=null, email='8237216470@qq.com', activation=null, createTime=null}, 1=YxUser{id=7, username='666', password='222', lastVisitTime=null, email='823721670@qq.com', activation=null, createTime=null}}

复制代码

6.counting

/** * 返回一个流的数量,若是是空,那么返回为0. * * @implSpec * 这个函数的实现是依靠于 reducing实现的。 * <pre>{@code * reducing(0L, e -> 1L, Long::sum) * }</pre> * * @param <T> 输入元素的类型 * @return a {@code Collector} 返回一个count */
    public static <T> Collector<T, ?, Long>
    counting() {
        return reducing(0L, e -> 1L, Long::sum);
    }

复制代码

几种count的方式

long count = list.stream().count();
        System.out.println("count: " + count);
        Long collect4 = list.stream().collect(counting());
        System.out.println("collect4: " + collect4);
        list.stream().collect(reducing(0, YxUser::getId, (x, y) -> x + y));

复制代码

7.minBy

/** * * minBy其实实现的原理也就是 * return (a,b)->comparator.compare(a,b) < 0 ? a : b * * 可是是使用Optional<T>来接受,防止空值 * * @implSpec * This produces a result equivalent to: * <pre>{@code * reducing(BinaryOperator.minBy(comparator)) * }</pre> * * @param <T> 输入元素的类型 * @param comparator 是一个比较器 * @return 一个最小值 */
    public static <T> Collector<T, ?, Optional<T>>
    minBy(Comparator<? super T> comparator) {
        return reducing(BinaryOperator.minBy(comparator));
    }

复制代码

使用minBy

//本身构造的比较器,是依据YxUser中的Id来比较。
		Comparator<YxUser> comparator=Comparator.comparingInt(YxUser::getId);
        list.stream().collect(groupingBy(YxUser::getUsername, minBy(comparator)));

复制代码

maxBy和这个差很少。

summingInt 函数,是返回最大值、最小值、平均值、count值等,使用getter方法获取便可

IntSummaryStatistics collect5 = list.stream().collect(summarizingInt(YxUser::getId));
     collect5.getAverage();

复制代码

因为类型缘由,还提供了summingLong、summingDouble。

求平均值的:averagingInt、averageingLong、averagingDouble

自定义一个收集器

​ 在Collector和Collectors的源码得知,在Collectors中,使用内部类CollectorImpl来实现的Collector,而后其余的toList、joining等一系列的操做都是,依托于这个内部类CollectorImpl,因此咱们能够自定义一个收集器。他的功能是:使用ArrayList来收集流中的数据:

/** * @author yangxin * @time 2019/8/6 15:02 */
public class ToListCollector<T> implements Collector<T, List<T>, List<T>> {

    // 既然是使用ArrayList来收集流中的数据,那么在supplier中要建立一个ArrayList结果容器,
    //在CollectorImpl中,他在这儿将建立圈交给了外部。
    @Override
    public Supplier<List<T>> supplier() {
        return ArrayList::new;
    }

    // 累加器部分,确定就是添加元素
    @Override
    public BiConsumer<List<T>, T> accumulator() {
        return List::add;
    }

    // 联合部分,量右边的值归并到左边
    @Override
    public BinaryOperator<List<T>> combiner() {
        return (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };
    }

    @Override
    public Function<List<T>, List<T>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        // 为收集器添加IDENTITY_FINISH和CONCURRENT标记
        return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT));
    }
}

复制代码

使用

List<YxUser> list = Arrays.asList(
                new YxUser(1, "yanxgin", "222", "8237251670@qq.com", 1, true),
                new YxUser(2, "12", "222", "8237216670@qq.com", 1, false),
                new YxUser(3, "yan34xgin", "222", "823721670@qq.com", 0, true),
                new YxUser(4, "56", "222", "823721670@qq.com", 0, false),
                new YxUser(5, "78", "222", "82372163@qq.com", 1, false),
                new YxUser(6, "90", "222", "8237216470@qq.com", 0, false),
                new YxUser(7, "666", "222", "823721670@qq.com", 1, true)
        );

        // 自定义的收集器
        List<YxUser> collect = list.stream()
                .collect(new ToListCollector<YxUser>());
        System.out.println("collect: " + collect);

复制代码

本身写的ToListCollector 收集器,和toList的差很少,估计惟一的区别,就是toList有工厂类本身建立,自定义的收集器须要new。咱们能够建立出适用本身的收集器

优化代码

​ 在不少状况下使用java8的特性能够优化不少代码,使得代码更清晰、更易读。

​ ①:使用Lambda表达式取代匿名类。

Runnable runnable=new Runnable() {
            @Override
            public void run() {
                System.out.println("word");
            }
        };
        Runnable runnable1 = () -> System.out.println("hello");

复制代码

须要注意的地方:

Lambda表达式中的this,和super的含义是不一样的。在匿名类中,this表示自己;但在Lambda中,他表示的是包含类。然而,匿名类能够屏蔽包含类的变量,Lambda不能屏蔽。

还有一个问题是:假若有重载函数的时候,由于Lambda表达式的参数根据上下文来肯定的,因此会出现有多个匹配项的存在。这个编译器会帮忙解决这个问题好比:IDEA

int n=10;
     Runnable runnable2 = () -> {
         int n = 2;//会报错
         System.out.println("n: " + n);
     };

     Runnable runnable = new Runnable() {
         @Override
         public void run() {
             int n = 2;
             System.out.println("word" + n);
         }
     };
     runnable.run();
     runnable2.run();

复制代码

在这里插入图片描述

​ ②:使用方法引用重构Lambda表达式。

​ ③:使用StreamAPI重构命令式的数据处理。

使用Lambda重构面对对象的设计模式

1.策略模式

​ 策略模式就是解决一类算法的通用解决方案。一般有3部分组成:①:一个母接口(他表明某个需求)。②:实现母接口的各类策略方法。③:策略对象的客户。

在这里插入图片描述

在传统的方法中,咱们要使用策略模式这三个步骤不可少,可是有时候功能就比较简单,好比比较学生成绩大小,找出性别男女的人数、成绩平均值等,那么你须要去实现对应的策略函数,而后使用的时候以下:

// IsAllLowerCase、IsNumeric就是实现的策略函数
Validator numericValidator = new Validator(new IsNumeric()); 
boolean b1 = numericValidator.excute("aaaa");  
Validator lowerCaseValidator = new Validator(new IsAllLowerCase ());
boolean b2 = lowerCaseValidator.excute("bbbb"); 

复制代码

使用Lambda,那么就会省略策略实现部分:

// 函数式接口
public interface Factory {
    boolean execete(String s);
}

// 客户端
public class FactoryImpl {

    private final Factory factory;

    public FactoryImpl(Factory factory) {
        this.factory = factory;
    }

    public boolean execute(String s){
        return factory.execete(s);
    }
}

		// 使用部分 策略模式,在简单的策略就用实现多余的策略方法。
		FactoryImpl factory = new FactoryImpl((String s) -> s.matches("[a-z]+"));
        System.out.println("boolean: " + factory.execute("ddd"));


复制代码

2.模板方法

​ 模板方法就是但愿使用这个算法,而且呢能够支持对其中的修改,才能达到但愿的效果,正常状况是使用一个抽象的方法,让别人继承这个类的时候实现方法,实现本身的功能。每一次都须要实现的话,重复度太大了。

public YxUser processCustomer(Consumer<YxUser> makeCustomerHappy) {
        YxUser c = new YxUser();
        makeCustomerHappy.accept(c);
        return c;
    }
    
    /** * 在processCustomer中直接使用Lambda将重写的部分写进去,这样避免了大量的继承实现。 */
    YxUser yx = new FactoryImpl().processCustomer(e -> e.setUsername("杨鑫"));

复制代码

3.观察者模式

​ 一个事件发生,主题去通知全部观察者,观察者会根据本身观察的东西进行操做。

在这里插入图片描述

实现观察者模式:①观察者Observer接口;②实现Observer接口的各类观察者。③实现观察者注册和通知的Subject接口和其实现。

使用Lambda,在简单的观察者模式来讲,能够避免第②部分的操做,可是观察者的逻辑过多时,感受lambda不适应。

mark

demo地址:github.com/fireshoot/j…

相关文章
相关标签/搜索