java8-集合新特性

java8-Predicate

Predicate是个函数式接口,和Function接口一样,不同的是Predicate接口的定义为Predicate,也就是Predicate接口定义了接受一个类型为T的参数,返回一个boolean类型的结果。
根据函数式接口的规定,只能有一个抽象方法,Predicate接口总共有三个主要的方法,一个抽象方法test和两个default方法and和or。

boolean test(T t);用于检测实现类中的语句是true或false

default Predicate and(Predicate other)连接两个Predicate

default Predicate or(Predicate other)连接两个Predicate

比如下面的代码用于检测Student对象的年龄是否大于2

          public static void main(String[] args) {
                   Student stu = new Student("A", 3);
                   System.out.println(testPredicate(stu, s -> s.getAge() > 2));
          }

          public static  boolean testPredicate(T t, Predicate predicate) {
                   return predicate.test(t);
          }

Predicate接口一般和Stream的filter方法结合使用,用来过滤集合中的元素。

java8-Stream

Stream是java8基于函数式编程而出现的用于集合复杂操作的工具类,Stream的中文翻译是流、流式。字面感觉和java的IO很像,但其实没有任何的关联。Stream是以一种流式处理的思想对集合的元素进行处理。
Stream的出现基于著名的大数据并行处理思想MapReduce,MapReduce思想最早由谷歌公司提出,用来解决其搜索性能问题,后来发展成为一种通用的大数据并行处理思想。MapReduce是基于函数式编程中的Map函数和Reduce函数得来,Map函数对待处理的数据进行遍历处理,Reduce函数对Map之后的数据进行迭代式处理。
举个例子:如果有一堆数据,里面包含整型、浮点型甚至字符串,现在要求对其中的整型进行求和,显然必须先对数据遍历处理,过滤或者转换的方式得到整型,然后对得到的整型数据进行迭代式的求和。这其中的前半部分就是Map函数所做的事,后半部分就是Reduce函数做的事。
简而言之一句话:Map是对数据遍历得到想要的数据,Reduce对想要的数据进行处理得到最终想要的结果。
要了解Stream,先看下面一部分代码:

          public static void main(String[] args) {

                   List stuList = new ArrayList<>();
                   stuList.add(new Student("A", 1));
                   stuList.add(new Student("B", 3));
                   stuList.add(new Student("C", 2));
                   stuList.add(new Student("D", 4));

                   List stuNameList = stuList
                   .stream()
                   .filter(stu ->stu.getAge() > 1)
                   .sorted((stu1, stu2) -> stu1.getAge() - stu2.getAge())
                   .map(stu->stu.getName())
                   .collect(Collectors.toList());
                   
                   System.out.println(JSON.toJSONString(stuNameList));
          }

上面这段代码的最终输出结果是

["C","B","D"]

整段代码的含义是从给定的学生列表stuList中,筛选出age大于1的学生,并且按照学生的age升序排列,最后得到学生name的集合。
在java8出现Stream之前,我们要实现上面的功能,一行代码肯定解决不了。这就是Stream的强大之处。
上面的代码主要涉及下面几个方法:

  • stuList.stream() 获取stuList的Stream对象
  • filter(Predicate) 对Stream中的元素过滤,filter的参数是Predicate接口,Predicate前面提到返回的是boolean类型的值
  • sorted(Comparator) 对Stream中的元素按照定义的Comparator规则排序,值得注意的是Comparator接口虽然是很早就出现在jdk中了,但是jdk8中Comparator接口增加了@FunctionalInterface注解,这表明该接口可以使用lambda表达式实现。
  • map(Function) 对stream中的元素做处理,Function这里的输入参数肯定是Student类型,但是输出可以是任意值,这里我们是获取stu的name,也可以对stu的名称拼接上stu的age返回,map接受的是Function参数,map得到的是Function返回的值。
  • collect(Collector) 对前面一系列处理得到的数据进行收集,collect方法接受的是Collector接口参数,Collector接口下面会详细讲解。

从对上面几个方法的简单讲解,结合Steam的MapReduce思想,Stream的方法或者Stream的处理流程总共分为3部分。

  • stream的获取
  • stream中元素的处理,处理后得到一个新的Stream(MapReduce中的Map过程)
  • stream的终止(MapReduce中的Reduce过程)

值得注意的是Stream在调用终止方法之前,不会立即执行stream中的处理方法,也就是所有对Stream的处理操作都会延迟到最终的终止方法调用时再执行。这一点和Spark很像,spark框架是一种并行的大数据处理框架,其核心思想是RDD(弹性分布式数据集),每个rdd支持两种操作Transformation和action,所有的变换在最终的action之前都不会立即执行,而是在action之时再执行。因此只要没有调用最终的终止方法,Stream所属对象的集合可以进行增删改的操作

1. stream的获取

Stream常用的获取方式有两种,Stream静态方法(java8接口支持静态方法)和Collection中的stream()方法。Stream的静态方法包含两个:
of(T t) 返回一个只有一个元素的Stream
of(T... values) 返回一个包含values的Stream
如下我代码我们希望从给定的字符串数组中过滤出包含字母b的元素,并将b替换为B

        String[] sarray = { "abc", "bcd", "cde" };
                   List sList = Stream.of(sarray)
                   .filter(s ->s.contains("b"))
                   .map(s -> s.replace("b", "B"))
                   .collect(Collectors.toList());
        System.out.println(JSON.toJSONString(sList));

静态方法一般用来处理非集合的数据源很方便,避免将数组先转换为集合的困扰。
Collection接口定义了stream()方法,用来获取集合的流对象,根据对java8接口增强的了解,这个方法肯定是default方法。

2.Stream中元素的处理

Stream中元素的处理就是MapReduce思想中的Map过程(此Map并不等同于stream中的map方法),处理之后会得到一个新的Stream。Stream中对元素的处理方法很多,除了前面提到的最常用的filter、map、sorted之外,还有如下几个重要方法:

  • distinct() 无参数,过滤Stream中的重复元素,得到是无重复元素的Stream,重复元素的依据是元素的equals方法

  • peek(Consumer) peek接收的参数为Consumer类型,Consumer在之前讲到过接收一个T类型参数,无返回值,这是peek方法和map方法的主要区别,peek用来处理无返回值的操作,比如打印元素,而map用来处理有返回值的操作。

  • sorted() 和上面的带参数的sorted一样,只不过这个无参数,按照对象自定义的Comparator接口实现。如果对象未实现Comparator接口会报错。

  • skip(long) 跳过Stream中的前n个元素,如果Stream元素个数小于n,返回空的stream

  • limit(long) 获取Stream中的前n个元素,如果Stream元素个数小于n,返回全部元素

3.Steam的终结

Stream的终结就是MapReduce思想中的Reduce过程(此Reduce也不等同于Stream中的reduce方法),Stream的常用终结方法除上面提到的collect方法之外,还包括以下几种:

  • count() 返回stream中的元素个数
  • forEach(Consumer) 对Stream中的元素迭代执行Consumer操作
  • max(Comparator) 按照给定的排序规则取出Stream中的最大值,该方法返回的并不是Stream的元素,而是一个Optional对象,通过Optional对象的get方法获取具体元素。Optional在之后讲解。
  • reduce()有三个重载方法,目前还未使用过,使用到的时候补充
  • allMatch(Predicate)判断Stream中的元素是否都满足Predicate,全部满足返回true,否则返回false
  • anyMatch(Predicate)同上,任意一个满足Predicate即返回true
  • noneMatch(Predicate)同上,全部不满足Predicate即返回true

java8-Optional

Optional提供了一种安全的获取值的方式,避免之前为了防止出现NPE而进行繁琐的检查的工作。Optional有很多方法,常用的方法或者组合使用方式为:

  • Optional.ofNullable(T t) 获取一个内部元素为t的Optional对象,t如果为空,则返回空的Optional对象
  • ifPresent(Consumer) 判断一个Optional内部的元素是否为空,为空不执行consumer,不为空会执行consumer
  • orElseGet(Supplier) 尝试从Optional中获取其元素,如果Optional为空,则执行Supplier函数提供一个元素返回,否则返回Optional中的元素

组合方式:一般对一个方法的返回值使用Optional包装,然后使用ifPresent执行不为空的操作,或者使用orElseGet方法获取方法的返回值或者默认值。

                   System.out.println(Optional.ofNullable(null).orElseGet(() -> {
                             return "NULL";
                   }));//输出NULL

                   System.out.println(Optional.ofNullable("ss").orElseGet(() -> {
                             return "NULL";
                   }));//输出ss

                   Optional.ofNullable(null).ifPresent(System.out::println);//无输出
                   Optional.ofNullable("sss").ifPresent(System.out::println);//输出sss

java8-Collector

Stream的collect方法接收一个Collector对象,Collector的官方解释是对Stream的可变减少操作。不太理解这里的可变减少是什么含义。

Collector主要有两方面的能力:

  • 聚合Stream中的元素,比如转为集合,Map或者求和,求平均值等
  • 对Stream中的元素进行分组
对Stream中元素进行聚合

这是Collector最常见的用法,常见的操作是将Stream中的元素转为List,Set或者Map,而这三者在Collector对应的工具类Collectors中都有具体的方法可用:

Collectors.toList(),Collectors.toSet(),Collectors.toMap(Function key,Function value)
除了转List和Set不需要参数之外,转Map的方法接收两个Function函数,用来决定key和value如何生成。
下面的代码演示了如何将stuList中满足年龄大于1的学生收集到List,Set以及按照name对应stu转成Map

                   List stuList = new ArrayList<>();
                   stuList.add(new Student("B", 1));
                   stuList.add(new Student("A", 3));
                   stuList.add(new Student("A", 3));
                   stuList.add(new Student("C", 2));
                   stuList.add(new Student("D", 4));

                   List stuFilterList = 
                        stuList.stream()
                        .filter(stu ->stu.getAge() > 1)
                        .collect(Collectors.toList()); 
                   System.out.println(JSON.toJSONString(stuFilterList));
                   
                   Set stuFilterSet = 
                        stuList.stream()
                        .filter(stu ->stu.getAge() > 1)
                        .collect(Collectors.toSet());
                   System.out.println(JSON.toJSONString(stuFilterSet));
                   
                   Map stuFilterMap =
                        stuList.stream()
                        .filter(stu -> stu.getAge() > 1)
                        .collect(Collectors.toMap(stu -> stu.getAge(), stu -> stu));
                   System.out.println(JSON.toJSONString(stuFilterMap));

上面的代码运行结果为:

[{"age":3,"name":"A"},{"age":3,"name":"A"},{"age":2,"name":"C"},{"age":4,"name":"D"}]
[{"age":4,"name":"D"},{"age":2,"name":"C"},{"age":3,"name":"A"}]
Exception in thread "main" java.lang.IllegalStateException: Duplicate key 
com.zong.test.Student@45f......

转List和Set都得到了我们预期的结果,Set中也对元素做了去重(Student类重写了hashcode和equals方法),但是转Map却抛出了Key重复的异常,原因是因为我们使用age作为可以,而其中有两个元素的age都为3,因此出现异常。在绝大数的使用场景下,这种情况很普遍,因此Collectors还额外提供了支持处理重复key的toMap方法

toMap(Function key,Function value,BinaryOperator mergeFunction)

三个参数中,前两个参数和之前的toMap方法含义一样,定义了获取key和value的方式,第三个参数定义了当有重复key的时候应当做的操作。上面的代码我们修改成如下即可:

Map stuFilterMap = 
    stuList.stream()
    .filter(stu -> stu.getAge() > 1)
    .collect(Collectors.toMap(stu -> stu.getAge(), stu -> stu, (stu1, stu2) -> stu2));
System.out.println(JSON.toJSONString(stuFilterMap));

其中的 (stu1, stu2) -> stu2 表示当遇到重复元素时保留新的值,我们如果希望当遇到重复的key时判断name的长度,name较长的保留,则可以写成下面的方式:

Map stuFilterMap = 
    stuList.stream()
    .filter(stu -> stu.getAge() > 1)
    .collect(Collectors.toMap(stu -> stu.getAge(), stu -> stu, 
    (stu1, stu2) -> stu1.getName().length() > stu2.getName().length() ? stu1 : stu2));
System.out.println(JSON.toJSONString(stuFilterMap));
对Stream元素进行分组

Collector的另一个功能是对Stream中的元素进行分组,在Collectors中同样提供对应的方法,常用的分组方法有两个。

partitioningBy(Predicate) 接收一个Predicate对象,按照元素在Predicate中的结果分为true和false两部分
groupingBy(Function)接收一个Function函数,分组的结果是根据Function函数的输出结果作为key,不同的元素在Function函数中的输出结果相同的在同一个key下

下面代码分别演示两个函数的使用。

                   List stuList = new ArrayList<>();
                   stuList.add(new Student("B", 1));
                   stuList.add(new Student("A", 3));
                   stuList.add(new Student("E", 3));
                   stuList.add(new Student("C", 2));
                   stuList.add(new Student("D", 4));

                   // 我们希望将学生分为年龄大于2和年龄小于2两部分
                   Map> stuPartMap = 
                   stuList.stream()
                   .collect(Collectors.partitioningBy(stu -> stu.getAge() > 2));
                   System.out.println(JSON.toJSONString(stuPartMap));

                   // 我们希望将学生按照年龄分类,年龄相同的在一组
                   Map> stuGroupMap = 
                    stuList.stream()
                    .collect(Collectors.groupingBy(stu -> stu.getAge()));
                   System.out.println(JSON.toJSONString(stuGroupMap));

第一部分输出结果:

{false:[{"age":1,"name":"B"},{"age":2,"name":"C"}],true:[{"age":3,"name":"A"},{"age":3,"name":"E"},{"age":4,"name":"D"}]}

第二部分输出结果:

 {1:[{"age":1,"name":"B"}],2:[{"age":2,"name":"C"}],3:[{"age":3,"name":"A"},   {"age":3,"name":"E"}],4:[{"age":4,"name":"D"}]}

显然,我们完全可以使用groupingBy函数代替partitioningBy函数,比如第一部分我们也可以写成下面的形式:

Map> stuGroupMap1 = 
stuList.stream().collect(Collectors.groupingBy(stu -> stu.getAge() > 2));

java8方法引用

方法引用是java8针对lambda表达式所提供的的一种简化语法,其本质是lambda表达式。前面提到的例子中,比如我们希望从stuList中获取stu的name集合,我们的一般的写法是:

List nameList = 
stuList.stream().map(stu -> stu.getName()).collect(Collectors.toList());

但是有了方法引用之后,我们可以更简化的写成下面的形式:

List nameList = 
stuList.stream().map(Student::getName).collect(Collectors.toList());

这里面Student::getName其实就是对lambda表示 stu->stu.getName() 的简化。更为常见的用法是System.out.println方法。

stuList.forEach(stu->System.out.println(stu));
stuList.forEach(System.out::println);

还有转Map的时候:

Map stuFilterMap = 
    stuList.stream()
    .collect(Collectors.toMap(stu -> stu.getAge(), stu -> stu));

Map stuFilterMap = 
    stuList.stream()
    .collect(Collectors.toMap(Student::getAge, stu -> stu));

方法引用共分为四类:

  • 静态方法引用,类名::静态方法名,如String::valueOf,等同于s->String.valueOf(s)
  • 实例对象引用,实力名::方法名
  • 构造方法引用,类名::new

java8集合新增方法

removeIf(Predicate)

删除集合中满足条件的元素,不会出现UnmodifiedException

//删除年龄小于等于2的学生
stuList.removeIf(stu -> stu.getAge() <= 2);
replaceAll(Function)

对集合中的所有元素执行Function函数,并使用执行的结果替代原始元素,一般用于对字符串的操作(复杂对象可以直接使用foreach处理)

List phones = Arrays.asList("186-1001-8262", "186-1001-8263", 
"186-1001-8264");
//需要将手机号码中的-去掉
phones.replaceAll(phone -> phone.replace("-", ""));
sort(Comparator)

这是List接口的方法,用于对元素按照跟定的规则排序

//对学生列表按照年龄升序排列
stuList.sort((stu1, stu2) -> stu1.getAge() - stu2.getAge());

java8Map新增方法

forEach

类似于集合的forEach方法,不同的是传入的是两个参数

map.forEach((k, v) -> System.out.println(k + "=" + v))
getOrDefault(k,v)

获取指定key的value,不存在的返回默认的v

putIfAbsent(k,v)

只有k不存在或者k对应的value为null时,才将k,v放入map

remove(k,v)

是对remove(k)方法的增强,只有当k对应的value和v相等时才删除

replaceAll(BiFunction)

对Map中的每个key对应的元素执行Function操作,得到的结果替换原来的value

merge(k,v,BiFunction)

如果map中的key对应的value不存在或者为null,就将v放入k。否则就执行BiFunction,执行的结果如果不为null就和k关联#### java8-Predicate
Predicate是个函数式接口,和Function接口一样,不同的是Predicate接口的定义为Predicate,也就是Predicate接口定义了接受一个类型为T的参数,返回一个boolean类型的结果。
根据函数式接口的规定,只能有一个抽象方法,Predicate接口总共有三个主要的方法,一个抽象方法test和两个default方法and和or。

boolean test(T t);用于检测实现类中的语句是true或false

default Predicate and(Predicate other)连接两个Predicate

default Predicate or(Predicate other)连接两个Predicate

比如下面的代码用于检测Student对象的年龄是否大于2

          public static void main(String[] args) {
                   Student stu = new Student("A", 3);
                   System.out.println(testPredicate(stu, s -> s.getAge() > 2));
          }

          public static  boolean testPredicate(T t, Predicate predicate) {
                   return predicate.test(t);
          }

Predicate接口一般和Stream的filter方法结合使用,用来过滤集合中的元素。

java8-Stream

Stream是java8基于函数式编程而出现的用于集合复杂操作的工具类,Stream的中文翻译是流、流式。字面感觉和java的IO很像,但其实没有任何的关联。Stream是以一种流式处理的思想对集合的元素进行处理。
Stream的出现基于著名的大数据并行处理思想MapReduce,MapReduce思想最早由谷歌公司提出,用来解决其搜索性能问题,后来发展成为一种通用的大数据并行处理思想。MapReduce是基于函数式编程中的Map函数和Reduce函数得来,Map函数对待处理的数据进行遍历处理,Reduce函数对Map之后的数据进行迭代式处理。
举个例子:如果有一堆数据,里面包含整型、浮点型甚至字符串,现在要求对其中的整型进行求和,显然必须先对数据遍历处理,过滤或者转换的方式得到整型,然后对得到的整型数据进行迭代式的求和。这其中的前半部分就是Map函数所做的事,后半部分就是Reduce函数做的事。
简而言之一句话:Map是对数据遍历得到想要的数据,Reduce对想要的数据进行处理得到最终想要的结果。
要了解Stream,先看下面一部分代码:

          public static void main(String[] args) {

                   List stuList = new ArrayList<>();
                   stuList.add(new Student("A", 1));
                   stuList.add(new Student("B", 3));
                   stuList.add(new Student("C", 2));
                   stuList.add(new Student("D", 4));

                   List stuNameList = stuList
                   .stream()
                   .filter(stu ->stu.getAge() > 1)
                   .sorted((stu1, stu2) -> stu1.getAge() - stu2.getAge())
                   .map(stu->stu.getName())
                   .collect(Collectors.toList());
                   
                   System.out.println(JSON.toJSONString(stuNameList));
          }

上面这段代码的最终输出结果是

["C","B","D"]

整段代码的含义是从给定的学生列表stuList中,筛选出age大于1的学生,并且按照学生的age升序排列,最后得到学生name的集合。
在java8出现Stream之前,我们要实现上面的功能,一行代码肯定解决不了。这就是Stream的强大之处。
上面的代码主要涉及下面几个方法:

  • stuList.stream() 获取stuList的Stream对象
  • filter(Predicate) 对Stream中的元素过滤,filter的参数是Predicate接口,Predicate前面提到返回的是boolean类型的值
  • sorted(Comparator) 对Stream中的元素按照定义的Comparator规则排序,值得注意的是Comparator接口虽然是很早就出现在jdk中了,但是jdk8中Comparator接口增加了@FunctionalInterface注解,这表明该接口可以使用lambda表达式实现。
  • map(Function) 对stream中的元素做处理,Function这里的输入参数肯定是Student类型,但是输出可以是任意值,这里我们是获取stu的name,也可以对stu的名称拼接上stu的age返回,map接受的是Function参数,map得到的是Function返回的值。
  • collect(Collector) 对前面一系列处理得到的数据进行收集,collect方法接受的是Collector接口参数,Collector接口下面会详细讲解。

从对上面几个方法的简单讲解,结合Steam的MapReduce思想,Stream的方法或者Stream的处理流程总共分为3部分。

  • stream的获取
  • stream中元素的处理,处理后得到一个新的Stream(MapReduce中的Map过程)
  • stream的终止(MapReduce中的Reduce过程)

值得注意的是Stream在调用终止方法之前,不会立即执行stream中的处理方法,也就是所有对Stream的处理操作都会延迟到最终的终止方法调用时再执行。这一点和Spark很像,spark框架是一种并行的大数据处理框架,其核心思想是RDD(弹性分布式数据集),每个rdd支持两种操作Transformation和action,所有的变换在最终的action之前都不会立即执行,而是在action之时再执行。因此只要没有调用最终的终止方法,Stream所属对象的集合可以进行增删改的操作

1. stream的获取

Stream常用的获取方式有两种,Stream静态方法(java8接口支持静态方法)和Collection中的stream()方法。Stream的静态方法包含两个:
of(T t) 返回一个只有一个元素的Stream
of(T... values) 返回一个包含values的Stream
如下我代码我们希望从给定的字符串数组中过滤出包含字母b的元素,并将b替换为B

        String[] sarray = { "abc", "bcd", "cde" };
                   List sList = Stream.of(sarray)
                   .filter(s ->s.contains("b"))
                   .map(s -> s.replace("b", "B"))
                   .collect(Collectors.toList());
        System.out.println(JSON.toJSONString(sList));

静态方法一般用来处理非集合的数据源很方便,避免将数组先转换为集合的困扰。
Collection接口定义了stream()方法,用来获取集合的流对象,根据对java8接口增强的了解,这个方法肯定是default方法。

2.Stream中元素的处理

Stream中元素的处理就是MapReduce思想中的Map过程(此Map并不等同于stream中的map方法),处理之后会得到一个新的Stream。Stream中对元素的处理方法很多,除了前面提到的最常用的filter、map、sorted之外,还有如下几个重要方法:

  • distinct() 无参数,过滤Stream中的重复元素,得到是无重复元素的Stream,重复元素的依据是元素的equals方法

  • peek(Consumer) peek接收的参数为Consumer类型,Consumer在之前讲到过接收一个T类型参数,无返回值,这是peek方法和map方法的主要区别,peek用来处理无返回值的操作,比如打印元素,而map用来处理有返回值的操作。

  • sorted() 和上面的带参数的sorted一样,只不过这个无参数,按照对象自定义的Comparator接口实现。如果对象未实现Comparator接口会报错。

  • skip(long) 跳过Stream中的前n个元素,如果Stream元素个数小于n,返回空的stream

  • limit(long) 获取Stream中的前n个元素,如果Stream元素个数小于n,返回全部元素

3.Steam的终结

Stream的终结就是MapReduce思想中的Reduce过程(此Reduce也不等同于Stream中的reduce方法),Stream的常用终结方法除上面提到的collect方法之外,还包括以下几种:

  • count() 返回stream中的元素个数
  • forEach(Consumer) 对Stream中的元素迭代执行Consumer操作
  • max(Comparator) 按照给定的排序规则取出Stream中的最大值,该方法返回的并不是Stream的元素,而是一个Optional对象,通过Optional对象的get方法获取具体元素。Optional在之后讲解。
  • reduce()有三个重载方法,目前还未使用过,使用到的时候补充
  • allMatch(Predicate)判断Stream中的元素是否都满足Predicate,全部满足返回true,否则返回false
  • anyMatch(Predicate)同上,任意一个满足Predicate即返回true
  • noneMatch(Predicate)同上,全部不满足Predicate即返回true

java8-Optional

Optional提供了一种安全的获取值的方式,避免之前为了防止出现NPE而进行繁琐的检查的工作。Optional有很多方法,常用的方法或者组合使用方式为:

  • Optional.ofNullable(T t) 获取一个内部元素为t的Optional对象,t如果为空,则返回空的Optional对象
  • ifPresent(Consumer) 判断一个Optional内部的元素是否为空,为空不执行consumer,不为空会执行consumer
  • orElseGet(Supplier) 尝试从Optional中获取其元素,如果Optional为空,则执行Supplier函数提供一个元素返回,否则返回Optional中的元素

组合方式:一般对一个方法的返回值使用Optional包装,然后使用ifPresent执行不为空的操作,或者使用orElseGet方法获取方法的返回值或者默认值。

                   System.out.println(Optional.ofNullable(null).orElseGet(() -> {
                             return "NULL";
                   }));//输出NULL

                   System.out.println(Optional.ofNullable("ss").orElseGet(() -> {
                             return "NULL";
                   }));//输出ss

                   Optional.ofNullable(null).ifPresent(System.out::println);//无输出
                   Optional.ofNullable("sss").ifPresent(System.out::println);//输出sss

java8-Collector

Stream的collect方法接收一个Collector对象,Collector的官方解释是对Stream的可变减少操作。不太理解这里的可变减少是什么含义。

Collector主要有两方面的能力:

  • 聚合Stream中的元素,比如转为集合,Map或者求和,求平均值等
  • 对Stream中的元素进行分组
对Stream中元素进行聚合

这是Collector最常见的用法,常见的操作是将Stream中的元素转为List,Set或者Map,而这三者在Collector对应的工具类Collectors中都有具体的方法可用:

Collectors.toList(),Collectors.toSet(),Collectors.toMap(Function key,Function value)
除了转List和Set不需要参数之外,转Map的方法接收两个Function函数,用来决定key和value如何生成。
下面的代码演示了如何将stuList中满足年龄大于1的学生收集到List,Set以及按照name对应stu转成Map

                   List stuList = new ArrayList<>();
                   stuList.add(new Student("B", 1));
                   stuList.add(new Student("A", 3));
                   stuList.add(new Student("A", 3));
                   stuList.add(new Student("C", 2));
                   stuList.add(new Student("D", 4));

                   List stuFilterList = 
                        stuList.stream()
                        .filter(stu ->stu.getAge() > 1)
                        .collect(Collectors.toList()); 
                   System.out.println(JSON.toJSONString(stuFilterList));
                   
                   Set stuFilterSet = 
                        stuList.stream()
                        .filter(stu ->stu.getAge() > 1)
                        .collect(Collectors.toSet());
                   System.out.println(JSON.toJSONString(stuFilterSet));
                   
                   Map stuFilterMap =
                        stuList.stream()
                        .filter(stu -> stu.getAge() > 1)
                        .collect(Collectors.toMap(stu -> stu.getAge(), stu -> stu));
                   System.out.println(JSON.toJSONString(stuFilterMap));

上面的代码运行结果为:

[{"age":3,"name":"A"},{"age":3,"name":"A"},{"age":2,"name":"C"},{"age":4,"name":"D"}]
[{"age":4,"name":"D"},{"age":2,"name":"C"},{"age":3,"name":"A"}]
Exception in thread "main" java.lang.IllegalStateException: Duplicate key 
com.zong.test.Student@45f......

转List和Set都得到了我们预期的结果,Set中也对元素做了去重(Student类重写了hashcode和equals方法),但是转Map却抛出了Key重复的异常,原因是因为我们使用age作为可以,而其中有两个元素的age都为3,因此出现异常。在绝大数的使用场景下,这种情况很普遍,因此Collectors还额外提供了支持处理重复key的toMap方法

toMap(Function key,Function value,BinaryOperator mergeFunction)

三个参数中,前两个参数和之前的toMap方法含义一样,定义了获取key和value的方式,第三个参数定义了当有重复key的时候应当做的操作。上面的代码我们修改成如下即可:

Map stuFilterMap = 
    stuList.stream()
    .filter(stu -> stu.getAge() > 1)
    .collect(Collectors.toMap(stu -> stu.getAge(), stu -> stu, (stu1, stu2) -> stu2));
System.out.println(JSON.toJSONString(stuFilterMap));

其中的 (stu1, stu2) -> stu2 表示当遇到重复元素时保留新的值,我们如果希望当遇到重复的key时判断name的长度,name较长的保留,则可以写成下面的方式:

Map stuFilterMap = 
    stuList.stream()
    .filter(stu -> stu.getAge() > 1)
    .collect(Collectors.toMap(stu -> stu.getAge(), stu -> stu, 
    (stu1, stu2) -> stu1.getName().length() > stu2.getName().length() ? stu1 : stu2));
System.out.println(JSON.toJSONString(stuFilterMap));
对Stream元素进行分组

Collector的另一个功能是对Stream中的元素进行分组,在Collectors中同样提供对应的方法,常用的分组方法有两个。

partitioningBy(Predicate) 接收一个Predicate对象,按照元素在Predicate中的结果分为true和false两部分
groupingBy(Function)接收一个Function函数,分组的结果是根据Function函数的输出结果作为key,不同的元素在Function函数中的输出结果相同的在同一个key下

下面代码分别演示两个函数的使用。

                   List stuList = new ArrayList<>();
                   stuList.add(new Student("B", 1));
                   stuList.add(new Student("A", 3));
                   stuList.add(new Student("E", 3));
                   stuList.add(new Student("C", 2));
                   stuList.add(new Student("D", 4));

                   // 我们希望将学生分为年龄大于2和年龄小于2两部分
                   Map> stuPartMap = 
                   stuList.stream()
                   .collect(Collectors.partitioningBy(stu -> stu.getAge() > 2));
                   System.out.println(JSON.toJSONString(stuPartMap));

                   // 我们希望将学生按照年龄分类,年龄相同的在一组
                   Map> stuGroupMap = 
                    stuList.stream()
                    .collect(Collectors.groupingBy(stu -> stu.getAge()));
                   System.out.println(JSON.toJSONString(stuGroupMap));

第一部分输出结果:

{false:[{"age":1,"name":"B"},{"age":2,"name":"C"}],true:[{"age":3,"name":"A"},{"age":3,"name":"E"},{"age":4,"name":"D"}]}

第二部分输出结果:

 {1:[{"age":1,"name":"B"}],2:[{"age":2,"name":"C"}],3:[{"age":3,"name":"A"},   {"age":3,"name":"E"}],4:[{"age":4,"name":"D"}]}

显然,我们完全可以使用groupingBy函数代替partitioningBy函数,比如第一部分我们也可以写成下面的形式:

Map> stuGroupMap1 = 
stuList.stream().collect(Collectors.groupingBy(stu -> stu.getAge() > 2));

java8方法引用

方法引用是java8针对lambda表达式所提供的的一种简化语法,其本质是lambda表达式。前面提到的例子中,比如我们希望从stuList中获取stu的name集合,我们的一般的写法是:

List nameList = 
stuList.stream().map(stu -> stu.getName()).collect(Collectors.toList());

但是有了方法引用之后,我们可以更简化的写成下面的形式:

List nameList = 
stuList.stream().map(Student::getName).collect(Collectors.toList());

这里面Student::getName其实就是对lambda表示 stu->stu.getName() 的简化。更为常见的用法是System.out.println方法。

stuList.forEach(stu->System.out.println(stu));
stuList.forEach(System.out::println);

还有转Map的时候:

Map stuFilterMap = 
    stuList.stream()
    .collect(Collectors.toMap(stu -> stu.getAge(), stu -> stu));

Map stuFilterMap = 
    stuList.stream()
    .collect(Collectors.toMap(Student::getAge, stu -> stu));

方法引用共分为四类:

  • 静态方法引用,类名::静态方法名,如String::valueOf,等同于s->String.valueOf(s)
  • 实例对象引用,实力名::方法名
  • 构造方法引用,类名::new

java8集合新增方法

removeIf(Predicate)

删除集合中满足条件的元素,不会出现UnmodifiedException

//删除年龄小于等于2的学生
stuList.removeIf(stu -> stu.getAge() <= 2);
replaceAll(Function)

对集合中的所有元素执行Function函数,并使用执行的结果替代原始元素,一般用于对字符串的操作(复杂对象可以直接使用foreach处理)

List phones = Arrays.asList("186-1001-8262", "186-1001-8263", 
"186-1001-8264");
//需要将手机号码中的-去掉
phones.replaceAll(phone -> phone.replace("-", ""));
sort(Comparator)

这是List接口的方法,用于对元素按照跟定的规则排序

//对学生列表按照年龄升序排列
stuList.sort((stu1, stu2) -> stu1.getAge() - stu2.getAge());

java8Map新增方法

forEach

类似于集合的forEach方法,不同的是传入的是两个参数

map.forEach((k, v) -> System.out.println(k + "=" + v))
getOrDefault(k,v)

获取指定key的value,不存在的返回默认的v

putIfAbsent(k,v)

只有k不存在或者k对应的value为null时,才将k,v放入map

remove(k,v)

是对remove(k)方法的增强,只有当k对应的value和v相等时才删除

replaceAll(BiFunction)

对Map中的每个key对应的元素执行Function操作,得到的结果替换原来的value

merge(k,v,BiFunction)

如果map中的key对应的value不存在或者为null,就将v放入k。否则就执行BiFunction,执行的结果如果不为null就和k关联

你可能感兴趣的:(java)