Stream流、lambda表达式、方法引用、构造引用

函数式接口

  函数接口为lambda表达式和方法引用提供目标类型,就是提供支持的接口里面只有且必须只有一个抽象方法,

  如果接口只有一个抽象方法,java默认他为函数式接口

  @FunctionalInterfafce注解限定只能有一个抽象方法

 

  • 一个函数式接口有且只有一个抽象方法。
  • 默认方法不是抽象方法,因为它们已经实现了。
  • 重写了超类Object类中任意一个public方法的方法并不算接口中的抽象方法

 @FunctionalInterface规定重写了超类Object类中的任意一个public方法的方法并不算入该接口中的抽象方法计数当中,因为该接口的任何实

现都将具有java.lang.Object或其他地方的实现

内置四大核心函数式接口

 

 

 其他接口

 

 

 

eg:

@FunctionalInterface
public interface Consumer<T>
接受单个输入参数且没有返回值。和其他大部分函数式接口不同,Consumer被期望用于操作副作用
Represents an operation that accepts a single input argument and returns no result. Unlike most other functional interfaces, Consumer is expected to operate via side-effects.
This is a functional interface whose functional method is accept(Object).

@FunctionalInterface
public interface Supplier<T>

表示结果的提供者
Represents a supplier of results. 
不需要每次调用时返回新的或者不同的结果
There is no requirement that a new or distinct result be returned each time the supplier is invoked.
该函数式接口的方法时get()
This is a functional interface whose functional method is get().


@FunctionalInterface
public interface Function<T,R>
接受一个参数并返回一个结果
Represents a function that accepts one argument and produces a result.
This is a functional interface whose functional method is apply(Object).

@FunctionalInterface
public interface Predicate<T>
对一个参数的断言
Represents a predicate (boolean-valued function) of one argument.
This is a functional interface whose functional method is test(Object).

 

    1 public class FunctionUse {
    2         public static void main(String[] args) {
    3                 //NumberFormatException
    4                 //System.out.println(Integer.valueOf("a"));
    5                 //只能作用域数字型的字符串
    6                 //System.out.println(Integer.valueOf("11"));
    7                 //NumberFormatException
    8                 //System.out.println(Integer.parseInt("a"));
    9                 Consumer<String> consumer =t->{
   10                         System.out.println("消费者型函数式接口,有参数,无返回值");
   11                         System.out.println("参数是"+t);
   12                 };
   13                 consumer.accept("abc");
   14                 Supplier<String> supplier = ()->{
   15                         System.out.println("无参数,有返回值");
   16                         return "a result";
   17                 };
   18                 supplier.get();
   19 
   20                 Function<String , Integer> function = (String input)->{
   21                         System.out.println("有参数,有返回值");
   22                         return input.length();
   23                 };
   24                 System.out.println(function.apply("Abc"));
   25 
   26                 Predicate<String> predicate = t->{
   27                         return true;
   28                 };
   29                 System.out.println(predicate.test("小仙女"));
   30         }

 

 

Lambda表达式

只有函数式接口才能使用lambda表达式

允许把函数作为一个方法的参数传递

可以当成匿名函数使用

使代码变得更加简洁紧凑

语法:

(parameters) -> expression
或者
 (parameters) ->{ statements; }
 
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。JDK8能够根据上下文推断出来
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体只包含了一个语句,就不需要使用大括号
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,使用大括号(就)需要显示return出来。  单表达式值不需要return
 
对比匿名函数:

 

 

 

方法引用

若lambda方法提中的功能已经提供了方法实现,可以使用方法引用=》可以理解为lambda表达式的另一种形式

形式

*一:对象::实例方法名
*二:类名::静态方法名
*三(特殊):类名::实例方法名 条件是第一个参数为lambda体的方法调用者

 

注意:
*①(前两种形式)方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!(最重要,同样适用于构造引用)
*②(特殊形式)若Lambda的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式:ClassName::MethodName

    1 public class MethodReferenceTest {
    2         /**
    3      * @description 对象 :: 实例方法名
    4      */
    5         @Test
    6         public void test1(){
    7                 PrintStream ps = System.out;
    8                 Consumer<String> con = x -> ps.println(x);
    9                 //hello
   10                 con.accept("hello");
   11 
   12                 PrintStream ps1 = System.out;
   13                 Consumer<String> con1 = ps1::println;
   14                 //world
   15                 con1.accept("world");
   16 
   17                 Consumer<String> con3 = System.out::println;
   18                 //hello world
   19                 con3.accept("hello world");
   20         }
   21         @Test
   22         public void test2(){
   23                 Teacher teacher1 = new Teacher("王尼玛",600,"京山小学");
   24                 Supplier<String> supplier1 = () -> teacher1.getName();
   25                 System.out.println(supplier1.get());
   26                 System.out.println("--------------------------");
   27 
   28                 Supplier<Integer> supplier2 = teacher1 :: getAge;
   29                 System.out.println(supplier2.get());
   30                 System.out.println("--------------------------");
   31 
   32                 Supplier<String> supplier3 = new Teacher("王尼玛",600,"京山小学") ::getName;
   33                 System.out.println(supplier3.get());
   34         }
   35         /**
   36      * @description 类名 :: 静态方法名
   37      */
   38         @Test
   39         public void test3(){
   40                 //Integer 的compare()方法是静态方法
   41                 Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
   42                 System.out.println(comparator.compare(2,3));
   43 
   44                 Comparator<Integer> comparator1 = Integer::compare;
   45                 System.out.println(comparator1.compare(4,2));
   46         }
   47         /**
   48      * @description 类名 :: 实例方法名
   49      */
   50         @Test
   51         public void test4(){
   52                 //x为lambda体中的调用者
   53                 BiPredicate<String,String> bp = (x,y) -> x.equals(y);
   54                 System.out.println(bp.test("abc","ABC"));
   55                 System.out.println("--------------");
   56 
   57                 BiPredicate<String,String> bp1 = String::equals;
   58                 System.out.println(bp1.test("abc","cba"));
   59                 System.out.println("-----------");
   60                 //参数e为调用者
   61                 Function<Teacher,String> function = e -> e.show();
   62                 System.out.println(function.apply(new Teacher()));
   63                 System.out.println("----------------");
   64 
   65                 Function<Teacher,String> function1 = Teacher::show;
   66                 System.out.println(function1.apply(new Teacher()));
   67 
   68                 //不是静态方法,也没有参数 不能使用 类名:: 方法名 的形式  Non-static method cannot be referenced from a static context
   69               // Supplier<String> supplier1 = Teacher::show;
   70         }
          }

 

构造引用

/**
*形式:
*对象名::new
*@author夜神
*@notice 最重要的一点,和方法引用一样,需要和函数式接口的抽象方法的返回值类型、参数列表一致
*


*数组构造引用
*类型[]::new
*就是对象::方法的形式
*集合构造引用
*类型::new
*
*/

    1 public class ConstructorReference {
    2         @Test
    3         public void test5(){
    4                 Function<Integer, List<String>> function = ArrayList::new;
    5                 List<String> stringList = function.apply(5);
    6         }
    7 
    8         /**
    9      * 数组引用
   10      * @description
   11      *              形式: 类型[] :: new
   12      *              new String[size] ->一个参数一个返回值(String[]对象)->Function接口
   13      */
   14         @Test
   15         public void test4() {
   16                 //lambda表达式
   17                 Function<Integer, String[]> function = args -> new String[args];
   18                 String[] str = function.apply(3);
   19                 System.out.println(str.length);
   20 
   21                 //数组引用
   22                 Function<Integer, String[]> function2 = String[] :: new;
   23                 String[] str2 = function2.apply(4);
   24                 System.out.println(str2.length);
   25         }
   26 
   27         /**
   28      * @description 无参构造
   29      * @because Supplier的抽象方法参数列表为空  ->无参数,有返回值 和无参构造一致
   30      */
   31         @Test
   32         public void test() {
   33                 //lambda表达式的写法
   34                 Supplier<Teacher> supplier = () -> new Teacher();
   35                 System.out.println(supplier.get());
   36                 //升级,构造引用
   37                 Supplier<Teacher> supplier1 = Teacher::new;
   38                 System.out.println(supplier1.get());
   39         }
   40 
   41         /**
   42      * 单参构造
   43      *
   44      * @desctiption Function 支持一个参数,一个返回值
   45      */
   46         @Test
   47         public void test2() {
   48                 //lambda表达式
   49                 Function<String, Teacher> function = x -> new Teacher();
   50                 System.out.println(function.apply("王尼玛"));
   51 
   52                 //构造引用
   53                 Function<String, Teacher> function1 = Teacher::new;
   54                 System.out.println(function1.apply("王尼玛"));
   55         }
   56 
   57         /**
   58      * 双参构造
   59      *
   60      * @description BiFunction支持两个参数和一个返回值
   61      */
   62         @Test
   63         public void test3() {
   64                 //lambda表达式
   65                 BiFunction<String, Integer, Teacher> function = (x, y) -> new Teacher(x, y);
   66                 System.out.println(function.apply("狗东西", 600));
   67 
   68                 //构造引用
   69                 BiFunction<String, Integer, Teacher> biFunction = Teacher::new;
   70                 System.out.println(biFunction.apply("王尼玛", 600));
                     }
             }

 

 Stream流

Classes to support functional-style operations on streams of elements, such as map-reduce transformations on collections.

类,以支持元素流上的函数式操作,例如集合上的映射-还原转换。

 

 

 

Stream 流提供了惰性计算和并行处理的能力,在使用并行计算方式时数据会被自动分解成多段然后并行处理,最后将结果汇总。所以 Stream 操作可以让程序运

行变得更加高效。

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列

 

  • Stream自己不会存储元素
  • Strream不会改变源对象,相反,他们会返回一个持有结果的新Stream
  • Stream的操作是延迟执行的,这意味着他们会等到需要结果时才执行

 

 

 

Stream的操作步骤:

创建Stream:一个数据源,获取一个流

中间操作:一个中间操作链,对数据源的数据进行处理

终止操作:执行中间操作链,并产生结果

 

创建Stream流的方式

集合提供的Stream()方法

Collectin接口提供了Stream()的默认实现

 

 通过Arrays数组工具创建流

Arrays数组工具提供了静态的创建流的方法

 

通过Stream类提供的方法创建流

of方法创建指定流

     */
    public static<T> Stream<T> of(T t) {
        return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
    }

    /**
     * Returns a sequential ordered stream whose elements are the specified values.
     *
     * @param <T> the type of stream elements
     * @param values the elements of the new stream
     * @return the new stream
     */
    @SafeVarargs
    @SuppressWarnings("varargs") // Creating a stream from an array is safe
    public static<T> Stream<T> of(T... values) {
        return Arrays.stream(values);
    }

iterate和generate无限流

    //迭代
    //public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
    Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);

    //生成
    Stream.generate(Math::random).limit(10).forEach(System.out::println);

 

 

常用中间操作

• map:通过一个 Function 把一个元素类型为 T 的流转换成元素类型为 R 的流。对每一个元素进行转换操作
• flatMap:通过一个 Function 把一个元素类型为 T 的流中的每个元素转换成一个元素类型为 R 的流,再把这些转换之后的流合并。对每一个元素进行转换操作,再平铺
• filter:过滤流中的元素,只保留满足由 Predicate 所指定的条件的元素。
• distinct:使用 hashCode()与equals ()方法来删除流中的重复元素。 去重
• limit:截断流使其最多只包含指定数量的元素。 限制个数
• skip:返回一个新的流,并跳过原始流中的前 N 个元素。
• sorted:对流进行排序
• peek:返回的流与原始流相同。当原始流中的元素被消费时,会首先调用 peek 方法中指定的 Consumer 实现对元素进行处理。多用于调试,IDEA可能提示将map变为peek,实际不建议这样做
• dropWhile:从原始流起始位置开始删除满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。
• takeWhile:从原始流起始位置开始保留满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。

map和flatMap

map 返回一个包含每个元素经过函数操作后产生的对象的流
flatMap 将进每个元素过函数操作后产生的新的对象转换成流,将这些流平铺连接成一个流,而且去除null值 (提取数据合并起来放入一个流中) so用flatMap能进行一对多操作

 

 

常用终止操作

foreach:遍历执行

reduce:进行递归计算

collect:生成新的数据结构

 

执行流程

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

返回ReferencePipeline.Head实例

ReferencePipeline实际上是一个双向链表的数据结构。而ReferencePipeline对Stream的操作做了实现,每一个中间操作都会返回一个Stream对象,实际上就是ReferencePipeline对象,因此可以得到结论:Stream底层是通过双向链表来实现的

源阶段返回的是ReferencePipeline.Head对象,而中间操作阶段返回的是ReferencePipeline对象。在流的源阶段和中间阶段仅仅只是返回了ReferencePipeline对象,并没有做其他的方法调用操作,这也是为什么流在执行中间操作并不会有任何的输出或者结果产生

 

 

 

eg

    1         /**
    2      * 筛选与切片
    3      *         filter——接收 Lambda , 从流中排除某些元素。
    4      *         limit——截断流,使其元素不超过给定数量。
    5      *         skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
    6      *         distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
    7      *         map——映射,接收 Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
    8      *
    9      */
   10         //filter 过滤
   11         @Test
   12         public void test4(){
   13                 Stream<User> stream = userList.stream()
   14                                 .filter(x -> {
   15                                         System.out.println("中间过滤操作");
   16                                         return x.getId()>12;
   17                                 });
   18                 stream.forEach(System.out::println);
   19         }
   20         //limt 截流
   21         @Test
   22         public void test5(){
   23                 Stream<User> stream = userList.stream()
   24                                 .filter(x -> x.getId()>12)
   25                                 .limit(2);//只要两个,满足后不会再迭代  e g
   26                 stream.forEach(System.out::println);
   27         }
   28         //skip跳过
   29         @Test
   30         public void test6(){
   31                 Stream<User> stream = userList.stream()
   32                                 .filter(x -> x.getId()>12)
   33                                 .skip(1)//把e跳过了
   34                                 .limit(2);//g d
   35                 stream.forEach(System.out::println);
   36         }
   37         //distinct 去重
   38         @Test
   39         public void test7(){
   40                 System.out.println(userList);
   41 
   42                   userList.stream()
   43                                         .distinct()
   44                                         .forEach(System.out::println);
   45         }
   46         /**
   47      * map 映射
   48      *       Returns a stream
   49      *        consisting of the results
   50      *        of applying the given function
   51      *        to the elements of this stream.
   52      *        返回一个Stream,包含所有被函数作用后产生的新值。元素个数一一对应。
   53      *接收 Lambda , 将元素转换成其他形式或提取信息。
   54      * 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
   55      *
   56      * flatMap 平面映射
   57      *          接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流(平铺)连接成一个流
   58      *          元素个数可能更多,因为把所有流中的值连接成一个流,而一个流中可能有多个值
   59      */
   60         @Test
   61         public void test8(){
   62                 //map 返回一个包含每个元素经过函数操作后产生的对象的流
   63                 Stream<Object> objectStream = userList.stream()
   64                                 .distinct()
   65                                 .map(x -> {
   66                                         if (x.getId() > 11) {
   67                                                 List<Object> list = new ArrayList<>();
   68                                                 list.add(x);
   69                                                 return list;
   70                                         }
   71                                         return null;
   72                                 });//返回的是Stream<R> R:返回值类型
   73                 //[null, [User{id=13, name='e', age=25}], [User{id=15, name='g', age=28}], [User{id=18, name='d', age=30}]]
   74                 System.out.println(objectStream.collect(Collectors.toList()));
   75                 System.out.println("------------------------------");
   76                 //flatMap 将进每个元素过函数操作后产生的新的对象转换成流,将这些流平铺连接成一个流,而且去除null值
   77                 Stream<Object> objStream = userList.stream()
   78                                 .distinct()
   79                                 .flatMap(x -> {
   80                                         if (x.getId()>11){
   81                                                 List<Object> list = new ArrayList<>();
   82                                                 list.add(x);
   83                                                 return list.stream();
   84                                         }
   85                                         return null;
   86                                 });
   87                 

 

使用流进行自定义去重

单属性去重

List<Student> collect = list.stream().collect(
    Collectors.collectingAndThen(
            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(People::getName))), ArrayList::new));

 

多属性去重

通常多个属性值之间用“;”隔开

1 ArrayList<Student>  collect1 = list.stream().collect(collectingAndThen(
2         toCollection(() -> new TreeSet<>(
3                 Comparator.comparing(element -> element.getName() + ";" + element.getAge()))), ArrayList::new));

 

使用流进行自定义排序

 List<People> peopleList = getPeopleList();
 7         // 正序
 8         List<People> sortPeople = peopleList.stream()
 9                 .sorted(Comparator.comparing(People::getName)).collect(Collectors.toList());
10         // [People(name=张三, age=23), People(name=张三, age=20), People(name=李四, age=20)]
11         System.out.println(sortPeople);
12         // 反序 reversed()
13         List<People> reSortPeople = peopleList.stream()
14                 .sorted(Comparator.comparing(People::getName).reversed()).collect(Collectors.toList());
15         // [People(name=李四, age=20), People(name=张三, age=23), People(name=张三, age=20)]
16         System.out.println(reSortPeople);
17         // 组合排序 comparing().thenComparing()
18         List<People> combineSort = peopleList.stream()
19                 .sorted(Comparator.comparing(People::getName).thenComparing(People::getAge)).collect(Collectors.toList());
20         // [People(name=张三, age=20), People(name=张三, age=23), People(name=李四, age=20)]
21         System.out.println(combineSort);

 

使用流转List为Map

Collectors下有toMap方法

    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper) {
        return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
    }

 

// mergeFunction:解决相同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) {
    return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

 

    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) {
        BiConsumer<M, T> accumulator
                = (map, element) -> map.merge(keyMapper.apply(element),
                                              valueMapper.apply(element), mergeFunction);
        return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
    }

 

声明一个集合

//声明一个List集合
List<People> list = new ArrayList<>();
list.add(new People("张三", 22));
list.add(new People("张三", 23));
list.add(new People("李四", 24));
 

 

List转Map

 Map<String, Integer> map = list.stream().collect(Collectors.toMap(People::getName, People::getAge));

key重复,报错

 

 

一般会碰到两个问题,key重复和空指异常 key重复可以选用,空指异常就在转换前保证没有null

key重复问题

选用后面的key  (根据业务需求)

// key重复,选用后者
        Map<String, Integer> map = list.stream().collect(Collectors.toMap(People::getName, People::getAge, (key1, key2) ->key2));

 

 

空指异常问题

转换前就保证没有null

转换时进行判断处理

 

        Map<String, Integer> map = list.stream().collect(Collectors.toMap(People::getName, p->p.getAge()==null?0:p.getAge(), (key1, key2) ->key2));

 

 

 

 

 

 

 

 

 

 

 

posted on 2023-03-03 04:15  or追梦者  阅读(79)  评论(0编辑  收藏  举报