1.Java8新特性

1.Lambda表达式

 1.1 概述

  Lambda表达式是一种函数式编程函数编程思想)方式,用于替代匿名内部类。它使得代码更具有可读性和简洁性,并提供更好的代码复用性和可维护性

面向对象编程思想

  强调的是对象必须通过对象来完成操作,情况较复杂。例如:多线程执行任务,需要创建对象。首先定义一个实现类实现接口Runnable,然后重写run方法中的代码传递给线程对象,这么麻烦?直接执行不就好了吗?

函数编程思想

  函数需要得有输入量输出量,使用输入量计算得到输出量。为了尽量忽略对象的复杂用法---强调做什么,而不是以什么去做。

  同样执行线程任务,使用函数编程思想,可以直接通过传递一段代码给线程对象执行不需要创建任务对象

小结函数编程思想可以通过一段代码完成面向对象想要做的代码量

1.2 Lambda表达式格式

1.2.1 标准格式

(参数列表)->{代码}

1.2.2 格式说明

  1. 小括号内的语法与接口中的方法括号里面的参数列表一致,空参数则空,多个参数逗号(",")隔开;
  2. ->新引入的语法格式,表示指向动作;
  3. 大括号内的内容与接口实现类中方法体的内容一致

1.2.3 代码实例

public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("第一种创建线程方式");
            }
        }).start();
        
        new Thread(()->{
            System.out.println("更简洁地方法创建多线程");
        }).start();
    }

运行结果

  在上述代码中,我们可以看到使用lambda表达式,省去了创建接口的流程(new Runnable),连里面的方法(run()方法)都省去只保留方法中的内容

List<Integer> list = new ArrayList<>();
        //为列表一次性添加多个元素
        Collections.addAll(list, 11,22,33,44,55);
        System.out.println(list);
        //比较器的正常书写格式
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;//后面数减前面数是降序排列
            }
        });
        System.out.println(list);
        //lambda表达式
        Collections.sort(list,(Integer o1, Integer o2)->{ return o1 -o2;});//前面数减后面数是升序排列
        System.out.println(list);

运行结果

1.3 Lambda表达式使用的条件

  首先,都是接口,且接口中只有一个抽象方法才可以使用Lambda表达式

  1. 接口中只有一个抽象方法,叫函数式接口
  2. 如果是函数式接口,可以用@FunctionInterface注解标识

   在上图中,Runnable是一个接口,其只有一个抽象方法,且被@FunctionInterface注解标识,只需满足其中一个就可以使用Lambda表达式。

1.4 Lambda 表达式的简写格式

  1. 小括号中的形参类型可以省略
  2. 如果小括号中只有一个参数的话,那么小括号可以省略
  3. 如果大括号中只有一条语句,那么大括号、分号、return可以一起省略
new Thread(()->System.out.println("更简洁的线程创建")).start();
运行结果
更简洁的线程创建
//继续简化lambda表达式
        Collections.sort(list, (o1, o2)->o2-o1);

运行结果
[55, 44, 33, 22, 11]

1.5 Lambda表达式的表现形式

1.变量的形式:变量为函数式接口,就么可以复制一个Lambda表达式【不常用】

        // 变量的形式
        Runnable r = ()->{
            System.out.println("任务代码");
        };
        // 函数式接口类型的变量
        Thread t = new Thread(r);

2.参数的形式:方法的形参为函数式接口,就可以传入一个Lambda表达式【常用】

        // 变量的形式-比较器
        Comparator<Integer> comparable = (o1, o2)->{return o2 - o1;};
        // 创建集合
        ArrayList<Integer> list = new ArrayList<>();
        // 存入数据
        Collections.addAll(list,11,22,33,44,55);
 
        // 将函数式接口类型  的 形参类型,传给Collections
        Collections.sort(list,comparable);

3.返回值的形式:方法的返回值类型为函数式接口,就可以返回一个Lambda表达式【常用】

        // 定义一个方法
        public static Comparator<Integer> getComparator(){
            return (Integer o1,Integer o2)->{return  o2-o1;};
        }
        
        public static void main (String[] args) {
            // 返回值形式
            Collections.sort(list,getComparator());
        }

2.Stream流

  Stream流是一种用于处理集合数据进行数据操作API。它提供了一种简单而强大的方法,可以以声明式的方式对集合进行过滤映射排序分组等操作。

2.1 Stream流引入

问题

  1. 将list集合中姓张的元素过滤到一个新的集合中
  2. 然后将过滤出来的姓张的元素中,再过滤出来长度为3的元素,存储到一个新的集合中

代码实例

//①将list集合中姓张的元素过滤到一个新的集合中
        //②然后将过滤出来的姓张的元素中,再过滤出来长度为3的元素,存储到一个新的集合中
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "张老三", "张小三", "李四", "王五", "张六", "赵八");
        List<String> list1 = new ArrayList<>();
        for (String name:list) {
            if (name.startsWith("张")){
                list1.add(name);
            }
        }
        System.out.println(list1);
        List<String> list2 = new ArrayList<>();
        for (String name:list1) {
            if (name.length() == 3){
                list2.add(name);
            }
        }
        System.out.println(list2);
普通方法

运行结果

[张老三, 张小三, 张六]
[张老三, 张小三]

用Stream流操作集合,获取流,过滤操作,打印输出

list.stream().filter((String name)->name.startsWith("张"))
                //方法体中只有一个形参时可省略小括号,方法体中只有一个语句时可省略大括号
                    .filter(name -> name.length() == 3)
                    //遍历
                    .forEach(name -> System.out.println(name));

运行结果

张老三
张小三

 2.2 Stream的格式

Stream<T> filter(Predicate<? super T> predicate);
参数:public interface Preicate<T>

   在filter()方法中的Predicate接口是一个函数式接口

2.3 获取流

  根据集合(Collection)获取流,Collection中有一个stream()方法可获取流

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

2.3.1 根据list集合获取流

List<String> list = new ArrayList<>();
        Collections.addAll(list, "张老三", "张小三", "李四", "王五", "张六", "赵八");
Stream<String>listStream = list.stream();

2.3.2 根据Set集合获取流

Set<String>set = new HashSet<>();
        Collections.addAll(set, "张老三", "张小三", "李四", "王五", "张六", "赵八");
        Stream<String> setStream = set.stream();

2.3.3 根据Map集合获取流

Map<Integer, String>map = new HashMap<>();
        map.put(1,"张老三");
        map.put(2,"张小三");
        map.put(3,"李四");
        map.put(4,"王五");
        map.put(5,"张六");
        map.put(6,"赵八");
        //获取键的流
        Set<Integer> keySet = map.keySet();
        Stream<Integer> keySetStream = keySet.stream();
        //获取值的流
        Collection<String> values = map.values();
        Stream<String> valuesStream = values.stream();
        //获取键值对对象的流
        Set<Map.Entry<Integer, String>> entries = map.entrySet();
        Stream<Map.Entry<Integer, String>> entryStream = entries.stream();

2.3.4 根据数组获取流

// 根据数组获取流
        String[] arr = {"张三","李四","赵五","刘六","王七"};
        Stream<String> arrStream = Stream.of(arr);

2.4 Stream常用方法

  1. 终结方法:返回值类型不再是Stream接口本身类型的方法,例如:forEach方法和count方法
  2. 非终结方法/延迟方法:返回值类型仍然是Stream接口自身类型的方法,除了终结方法都是延迟方法。例如:filter,limit,skip,map,conat

下面方法公用代码

List<String> list = new ArrayList<>();
Collections.addAll(list, "张老三", "张小三", "李四", "王五", "张六", "赵八");

Stream<String> arrStream = Stream.of("1","2","3","4","5");

2.4.1 count方法

概述

long count (); 统计流中的元素,返回long类型数据
long count = list.stream().count();//终结方法后续无法继续链接
System.out.println(count);
运行结果:
6

2.4.2 filter()方法

Stream<T> filter(Predicate<? super ?> predicate); 过滤出满足条件的元素
参数Predicate:函数式接口,抽象方法:boolean test (T  t)
Predicate接口:是一个判断接口
//filter方法加foreach方法
list.stream().filter(name ->name.startsWith("张"))
             .filter(name -> name.length() == 3)
             .forEach(name -> System.out.println(name));
运行结果:

张老三
张小三

2.4.3 foreach()方法

void forEach(Consumer<? super T> action):逐一处理流中的元素
参数 Consumer<? super T> action:函数式接口,只有一个抽象方法:void accept(T t);
注意:
1.此方法并不保证元素的逐一消费动作在流中是有序进行的(元素可能丢失)
2.Consumer是一个消费接口(可以获取流中的元素进行遍历操作,输出出去),可以使用Lambda表达式

2.4.4 limit()方法

Stream<T> limit(long maxSize);   取用前几个元素
注意:
参数是一个long 类型,如果流的长度大于参数,则进行截取;否则只截取已有流长度的所有元素
//limit方法(取前面几个)
list.stream().limit(3).forEach(name -> System.out.print(name)+" ");
运行结果
张老三 张小三 李四
list.stream().limit(7).forEach(name -> System.out.print(name)+" ");//比已有流的长度6大
运行结果:
张老三 张小三 李四 王五 张六 赵八

2.4.5 skip()方法

Stream<T> skip(long n);     跳过前几个元素
注意:
如果流的当前长度大于n,则跳过前n个,否则将会得到一个长度为0的空流
//skip()方法(跳过前面几个)
list.stream().skip(3).forEach(name -> System.out.print(name)+" ");
运行结果
 
王五 张六 赵八
list.stream().skip(7).forEach(name -> System.out.println(name));
运行结果:
无值

 

2.4.6 map()方法

<r> Stream <R> map(Function<? super T,? exception R> mapper;
参数Function<T,R>:函数式接口,抽象方法:R apply(T t);
Function<T,R>:其实就是一个类型转换接口(T和R的类型可以一致,也可以不一致)
//6.map()类型转换接口
arrStream.map(num -> Integer.parseInt(num))
         .forEach(i -> System.out.print(i)+" ");

运行结果
1 2 3 4 5

2.4.7 concat()方法

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)  --> 合并两个流
//concat()方法:合并两个流
Stream<String>listStream = list.stream(); Stream<String>arrStream = Stream.of(arr);
Stream.concat(listStream, arrStream).forEach(name
-> System.out.println(name));
运行结果:

报错

 原因Stream API只能被消费一次后续重复使用已建立的流会报异常!所以stream流是线程安全的前面代码我使用过arrStream的流,因此这里报错。

解决办法:创建一个新的流进行操作

String[] arr1 = {"1", "2", "3", "4"};
        Stream<String>arr1Stream = Stream.of(arr1);
        //7.concat()合并两个流
        System.out.println("-----");
        Stream.concat(listStream, arr1Stream).forEach(name -> System.out.print(name+" "));
运行结果
张老三 张小三 李四 王五 张六 赵八 1 2 3 4 

2.5 收集Stream流

Stream流中提供了一个方法,可以将流中的元素收集到一个单例流中

 <R, A> R collect(Collector<? super T, A, R> collector);     把流中的数据手机到单列集合中
返回值类型是R。R指定为什么类型,那么就收集什么类型的集合
参数Collector<? super T, A, R>中的R类型,决定把流中的元素收集到哪个集合中
参数Collector如何得到 ?,可以使用 java.util.stream.Collectors工具类中的静态方法:
- public static <T> Collector<T, ?, List<T>> toList():转换为List集合
- public static <T> Collector<T, ?, Set<T>> toSet() :转换为Set集合
ArrayList<Character> charList = new ArrayList<>();
Collections.addAll(charList, 'a', 'b', 'c', 'd', 'e');
Stream<Character> cStream = charList.stream();//获取一个流

List<Character> newList = cStream.collect(Collectors.toList());
System.out.println(newList);

Stream<Character> c1Stream = charList.stream();//不能重复消费,这里重新获取一个流对象
Set<Character> newSet = c1Stream.collect(Collectors.toSet());
System.out.println(newSet);

运行结果

[a, b, c, d, e]
[a, b, c, d, e]

 

参考链接

 

https://blog.csdn.net/m0_60489526/article/details/119959355

https://blog.csdn.net/m0_60489526/article/details/119984236

 

posted @ 2023-11-17 22:27  求知律己  阅读(68)  评论(0编辑  收藏  举报