java基础19--java8新特性,Lambda表达式,函数式接口,方法引用于构造器引用,Stream API,Optional类

Java8新特性

新特性简介

在这里插入图片描述

一、Lambda表达式

代码演示

package com.bijing.lambda;

import org.junit.jupiter.api.Test;

import java.util.Comparator;
import java.util.function.Consumer;

/**
 * @author 毕晶
 * @date 2022/8/18 15:20
 * 演示lambda表达式的使用
 * 1.举例:(o1,o2)->Integer.compare(o1,o2)
 * 2.格式: ->:lambda操作符或者箭头操作符
 * ->左边:lambda形参列表(其实就是接口中的抽象方法的形参列表)
 * ->右边:lambda体(其实就是重写的抽象方法的方法体)
 * 3.lambda的使用(分6种情况)
 * 总结:
 * 左边:lambda形参列表的参数类型可以省略(类型推断),如果形参列表只有一个参数可以省略小括号
 * 右边:lambda体如果只有一个条执行语句(可能是return语句),可以省略一对{}和return;
 * 4.lambda表达式的本质:作为函数式接口的实例
 * 5.如果一个接口中,只声明了一个抽象方法,则此接口称为函数式接口
 */
@SuppressWarnings({"all"})
public class Lambda_ {
    //语法格式一:无参,无返回值
    @Test
    public void example01() {
        Runnable r1 = () -> System.out.println("test1");
        r1.run();


    }

    //语法格式二:lambda需要一个参数,但是没有返回值
    @Test
    public void example02() {
        Consumer<String> consumer = (String s) -> System.out.println(s);
        consumer.accept("test2");
    }

    //语法格式三:数据类型可以省略,因为可由编译器推断出,称为类型维护
    @Test
    public void example03() {
        Consumer<String> consumer = (s) -> System.out.println(s);
        consumer.accept("test3");
    }

    //语法格式四:lambda表达式若只需要一个参数时,参数的小括号可以省略
    @Test
    public void example04() {
        Consumer<String> consumer = s -> System.out.println(s);
        consumer.accept("test4");
    }

    //语法格式五:lambda表达式需要两个以上的参数,多条执行语句,并且可以有返回值
    @Test
    public void example05() {
        Comparator<Integer> comparator = (o1, o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
        System.out.println(comparator.compare(32, 43));
    }

    //语法格式六:当lambda体只有一条语句的时候,return 与大括号若有,都可以省略;
    @Test
    public void example06() {
        Comparator<Integer> comparator = (o1, o2) -> o1.compareTo(o2);
        System.out.println(comparator.compare(5, 9));
    }
}

二、函数式接口

介绍

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

java内置四大核心函数式接口

注意:提供了函数式接口是为了更好地使用Lambda表达式,而不用再自己去定义各种接口

在这里插入图片描述

Consumer

package com.bijing.functionInterface;

import java.util.function.Consumer;

/**
 * @author 毕晶
 * @date 2022/8/20 14:44
 * 想要使用有传参无返回值时的接口考虑Consumer
 */
public class Consumer_ {
    public static void main(String[] args) {
        downCase(s -> System.out.println("s转换为小写" + s.toLowerCase())
        );
    }

    public static void downCase(Consumer<String> consumer) {
        consumer.accept("Hello World");
    }
}

Supplier

package com.bijing.functionInterface;

import java.util.Arrays;
import java.util.function.Supplier;

/**
 * @author 毕晶
 * @date 2022/8/20 14:37
 * 想要使用无传参,有返回值的接口使用Supplier
 */
public class Supplier_ {
    public static void main(String[] args) {
        fun1(() -> {
            int arr[] = {11, 2, 44, 5, 42};
            Arrays.sort(arr);
            return arr[arr.length - 1];
        });

    }

    public static void fun1(Supplier<Integer> supplier) {
        Integer max = supplier.get();
        System.out.println("max=" + max);
    }
}

Function

package com.bijing.functionInterface;

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

/**
 * @author 毕晶
 * @date 2022/8/20 15:12
 * 想要使用有传参,有返回值的接口使用Function<T t,R r>,前一个t是传参,r是返回值
 */
public class Function_ {
    public static void main(String[] args) {
        List<String> stringList = getList(new String[]{"bj", "study", "java"}, strings -> Arrays.asList(strings));
        for (String s : stringList) {
            System.out.println(s);
        }
//        getList("bjstudyjava", Arrays::asList);

        andThenTest("9899", Integer::valueOf, num -> num * 10);
        composeTest("234", Integer::valueOf, num -> num * 10);
    }

    public static List<String> getList(String[] strings, Function<String[], List<String>> function) {
        return function.apply(strings);
    }

    //andThen()方法,先f1 得到结果后,在结果的基础上then f2
    public static void andThenTest(String num, Function<String, Integer> f1, Function<Integer, Integer> f2) {
        Integer integer = f1.andThen(f2).apply(num);
        System.out.println(integer);
    }

    //compose()方法与andThen()相反
    public static void composeTest(String num, Function<String, Integer> f1, Function<Integer, Integer> f2) {
        Integer integer = f2.compose(f1).apply(num);
        System.out.println(integer);
    }

}

Predicate

package com.bijing.functionInterface;

import java.util.function.Predicate;

/**
 * @author 毕晶
 * @date 2022/8/20 15:58
 * 有传参,返回boolean类型使用Predicate
 */
public class Predicate_ {
    public static void main(String[] args) {
        functionTest(99.51, num -> Math.round(num) >= 100);
        functionAnd(100.1, num -> Math.round(num) >= 100, num -> Math.sqrt(num) > 10);
        functionOr(100.1, num -> Math.round(num) >= 10000, num -> Math.sqrt(num) > 10);
        functionNegate(100.1, num -> Math.round(num) >= 10000);
    }

    public static void functionTest(Double num, Predicate<Double> p1) {
        boolean test = p1.test(num);
        System.out.println(test);
    }

    public static void functionAnd(Double num, Predicate<Double> p1, Predicate<Double> p2) {
        boolean test = p1.and(p2).test(num);
        System.out.println(test);
    }

    public static void functionOr(Double num, Predicate<Double> p1, Predicate<Double> p2) {
        boolean test = p1.or(p2).test(num);
        System.out.println(test);
    }

    public static void functionNegate(Double num, Predicate<Double> p1) {
        boolean test = p1.negate().test(num);
        System.out.println(test);
    }


}

其他接口

在这里插入图片描述

三、方法引用与构造器引用

方法引用

在这里插入图片描述
应用场景:如果Lambda表达式所要实现的方案,已经有其他方法存在相同的方案,则可以使用方法引用

package com.bijing.methodRef;

import org.junit.jupiter.api.Test;

import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author 毕晶
 * @date 2022/8/19 14:06
 * 演示方法引用的使用
 * 1.使用情景:当要传给Lambda体的操作,已经有方法实现了,可以可以使用方法引用
 * 2.方法引用本质上就是Lambda表达式,而Lambda表达式作为函数传接口的实例,所以方法引用也是函数式接口的实例
 * 3.使用格式:-----类(或对象)::方法名
 * 4.具体分为如下三种情况
 * 1)对象 :: 非静态方法
 * 2)类 ::静态方法
 * 3)类 ::非静态方法
 * 5.方法引用的使用要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的形参列表和返回值类型相同!
 */
public class MethodRef {

    //情况一 对象::实例方法
    //Consumer中的void accept(T t)
    //PrintStream中的void println(T t)
    @Test
    public void test1() {
        Consumer<String> consumer = str -> System.out.println(str);
        consumer.accept("北京");

        PrintStream ps = System.out;
        Consumer<String> consumer2 = ps::println;
        consumer2.accept("北京");
        
		Date date = new Date();
        Supplier<Long> supplier = date::getTime;
//        Long time = date::getTime;报错因为使用方法引用必须是函数式接口类型
        System.out.println(supplier);

    }

    //Supplier中的T get()
    //Employee中的String getName()
    @Test
    public void test2() {
    }

    //情况二 类::静态方法
    //Comparator中的int compare(T t1,T t2)
    //Integer中的int compare(T t1,T t2)
    @Test
    public void test3() {
        Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
        System.out.println(com1.compare(12, 32));

        Comparator<Integer> com2 = Integer::compare;

    }

    //Function中的R apply(T t)
    //    Math中的Long round(Double d)
    @Test
    public void test4() {
        Function<Double, Long> func = d -> Math.round(d);
        Function<Double, Long> func1 = Math::round;
        System.out.println(func1.apply(3.23));
    }

    //情况三: 类::实例方法(有难度)
    //Comparator中的int compare(T t1,T t2)
    //String中的int t1.compareTo(t2)
    @Test
    public void test5() {
        Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);

        Comparator<String> com2 = String::compareTo;
    }

    //    BiPredicate中的boolean test(T t2,T t2);
    //    String中的boolean t1.equals(t2);
    @Test
    public void test6() {
        BiPredicate<String, String> b1 = (s1, s2) -> s1.equals(s2);
        System.out.println(b1.test("abc", "ABC"));
        BiPredicate<String, String> b2 = String::equals;
    }

    //    Function中的R apply(T t)
    //    Employee中的String getName()
    @Test
    public void test7() {
        
    }
}

构造器引用

package com.bijing.constructorRef;

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @author 毕晶
 * @date 2022/8/19 15:23
 * 演示构造器引用和数组引用
 * 一:构造器引用
 * 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致
 * 抽象方法的返回值类型即为构造器所属的类的类型
 * 二:数组引用
 * 把数组看成一个特殊的类,则写法与构造器引用一致
 */
public class ConstructorRef {

    //构造器引用
    //Supplier中的T get()
    //StringBuilder的空参构造器:StringBuilder(),返回一个StringBuilder类型的字符串
    @Test
    public void test() {
        Supplier<StringBuilder> supplier = () -> new StringBuilder();
        Supplier<StringBuilder> supplier1 = StringBuilder::new;
    }

    //Function中的R apply(T t)
    //StringBuilder的一个参数的构造器:StringBuilder(int),传参是Integer类型,返回StringBuilder,如果两个传参使用BiFunction函数式接口
    @Test
    public void test2() {
        Function<Integer, StringBuilder> function = num -> new StringBuilder(num);
        Function<Integer, StringBuilder> function2 = StringBuilder::new;
    }

    //数组引用
    //Function中的R apply(T t)
    @Test
    public void test4() {
        Function<Integer, String[]> function = length -> new String[length];
        Function<Integer, String[]> function2 = String[]::new;
        String[] arr = function2.apply(5);
        System.out.println(Arrays.toString(arr));
    }


四、强大的Stream API

引出:集合处理数据的弊端

package com.bijing.streamApi;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author 毕晶
 * @date 2022/8/23 20:22
 * 演示集合存在的问题
 */
public class SteamTest01 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("张三", "张三丰", "张无忌", "abc");
        //1.获取所以张开头的名字
        List<String> list1 = new ArrayList<>();
        for (String s : list) {
            if (s.startsWith("张")) {
                list1.add(s);
            }
        }
        //2.获取所有名字长度为3的用户
        List<String> list2 = new ArrayList<>();
        for (String s : list1) {
            if (s.length() == 3) {
                list2.add(s);
            }
        }
        //3.输出所有的用户信息
        for (String s : list2) {
            System.out.println(s);
        }
    }
//上面代码针对不同需求总是一次次循环,这时候我们希望有更加高效的处理方法,这时候可以通过jdk8中提供的stream api来解决这个问题
}

class StreamTest02 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("张三", "张三丰", "张无忌", "abc");
        //代码含义,获取流,过滤张,过滤长度,逐一打印
        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
    }
}

Stream流的思想概述

Stream Api能够让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复、统计、匹配和规约

在这里插入图片描述

Stream流的获取方式

根据Collection获取

首先java.util.Collection接口中加入了default方法stream,也就是说Collection接口下的所有实现类可以通过stream方法获取Stream流
但是Map不属于Collection接口,此时可以对Map的keySet或者values或者entrySet集合进行操作

package com.bijing.streamApi;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

/**
 * @author 毕晶
 * @date 2022/8/23 20:42
 */
public class StreamCollection {
    public static void main(String[] args) {
        HashSet<String> hashSet = new HashSet<>();
        hashSet.add("狗蛋");
        hashSet.add("狗屎");
        hashSet.add("狗子");
        hashSet.add("狗腿子");
        hashSet.add("二哈");
        Stream<String> stream = hashSet.stream();
        stream.filter(s -> s.contains("狗")).filter(s -> s.length() == 2).forEach(System.out::println);

        Map<Integer, String> maps = new HashMap<>();
        maps.put(1, "狗蛋");
        maps.put(2, "狗屎");
        maps.put(3, "狗子");
        maps.put(4, "狗腿子");
        maps.put(5, "二哈");
        Set<Map.Entry<Integer, String>> entries = maps.entrySet();
        Stream<Map.Entry<Integer, String>> stream1 = entries.stream();
        stream1.filter(s -> s.getValue().contains("狗")).filter(s -> s.getValue().length() == 2).forEach(System.out::println);
    }
}

根据Stream的of方法

package com.bijing.streamApi;

import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.Stream;

/**
 * @author 毕晶
 * @date 2022/8/24 19:18
 */
public class StreamOf {
    public static void main(String[] args) {
        //Stream.of()方法的传参是可变参数,返回的类型是Stream类型
        //可以直接传入多个参数
        Stream<String> a = Stream.of("dc", "u", "a", "b", "c", "d");
        a.sorted().forEach(System.out::println);
        String[] arr = {"wl", "wtt", "u", "i", "a"};
        //可以直接传入数组
        Stream<String> arr1 = Stream.of(arr);
        //可以调用Arrays.stream()方法
        Arrays.stream(arr).sorted().forEach(System.out::println);
        arr1.sorted((Comparator.reverseOrder())).forEach(System.out::println);
        //基本数据类型的数组不能作为Stream.of()的传参
        int[] nums = {4, -2, 44, 25, 8};
        Stream<int[]> nums1 = Stream.of(nums);
        nums1.sorted().forEach(System.out::println);
    }
}

Stream常用方法介绍

在这里插入图片描述

注意:这边的Stream只能操作一次是指一个流进行加工完后,原来的流不能进行第二次加工了

package com.bijing.streamApi;

import java.util.Objects;
import java.util.stream.Stream;

/**
 * @author 毕晶
 * @date 2022/8/24 19:43
 * 演示Stream的方法
 */
public class StreamMethods {
    public static void main(String[] args) {

        Stream<String> stringStream = Stream.of("bi", "jing", "study", "java", "java");
//        1.forEach方法,遍历流中的数据
//        void forEach(Consumer<? super T> action);
//        stringStream.filter(s -> s.contains("i")).forEach(System.out::println);

//        2.count方法,统计流中元素的个数
//        long count();
//        long count1 = stringStream.count();
//        System.out.println(count1);

//        3.filter方法是用来过滤数据的,返回符合条件的数据.可以通过filter方法将一个流转换成一个子集流
//        Stream<T> filter(Predicate<? super T> predicate);该接口接收一个Predicate函数式接口参数作为筛选条件
//        stringStream.filter(s -> s.contains("i")).forEach(System.out::println);

//        4.limit方法,可以对流进行截取处理只取前n个
//        Stream<T> limit(long maxSize);
//        stringStream.limit(2).forEach(System.out::println);

//        5.skip方法,如果希望跳过前面几个元素,可以使用skip方法,获取一个截取之后的流
//        Stream<T> skip(long n);
//        stringStream.skip(3).forEach(System.out::println);

//        6.map方法,如果想要将流中的数据映射到另一流中可以使用map方法,传参是一个Function,可以将流中的数据类型T转换为另一种类型R
//        <R> Stream<R> map(Function<? super T, ? extends R> mapper);
//        Stream.of("1", "2", "3", "4").map(Integer::parseInt).forEach(System.out::println);

//        7.sorted方法,如果需要将数据排序,可以使用该方法
//        Stream<T> sorted();//对数据自然排序
//        Stream<T> sorted(Comparator<? super T> comparator);//根据比较器指定排序的规则
//        stringStream.sorted(String::compareTo).forEach(System.out::println);

//        8.distinct方法,对于基本数据类型可以直接去重的,但是对于自定义类型需要重写hashCode方法和equals方法来去重
//        Stream<T> distinct();
//        Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
//                new Person("王五", 32, 40000),
//                new Person("张三", 18, 2400)).distinct().forEach(System.out::println);

//        9.match方法,如果需要判断数据是否匹配指定的条件可以使用match相关的方法(allMatch,anyMatch,noneMatch)
//        boolean anyMatch(Predicate<? super T> predicate);//元素是否有任意一个满足条件
//        boolean allMatch(Predicate<? super T> predicate);//元素是否都满足条件
//        boolean noneMatch(Predicate<? super T> predicate);//元素是否都不满足条件
//        boolean a = stringStream.anyMatch(s -> s.contains("a"));
//        System.out.println(a);

//        10.find方法,如果需要找到某些元素可以使用find方法来实现,
//        Optional<T> findAny();
//        Optional<T> findFirst();
//        Optional<String> first = stringStream.findFirst();
//        System.out.println(first.get());
//        Optional<String> any = stringStream.findAny();
//        System.out.println(any.get());

//        11.max和min方法,如果想要最大值或者最小值可以使用,
//        Optional<T> max(Comparator<? super T> comparator);
//        Optional<T> min(Comparator<? super T> comparator);
//        Optional<Integer> max = Stream.of(1, 2, 4, 66).max((o1, o2) -> o1 - o2);
//        System.out.println(max.get());

//        12.reduce方法 将所有数据归纳成一个方法
//        T reduce(T identity, BinaryOperator<T> accumulator);identity默认值,第一次操作将值赋给x,之后每次将结果赋给x,y是每次从数据中获取的元素
//        Integer total = Stream.of(1, 4, 5, 7).reduce(0, Integer::sum);
//        Integer max = Stream.of(4, 3, 9, 9, 0, 10).reduce(0, (x, y) -> x > y ? x : y);
//        Integer max2 = Stream.of(4, 3, 9, 9, 0, 10).reduce(0, Integer::max);
//        System.out.println(total);
//        System.out.println(max);

//        13.map和reduce的组合,实际开发中两个常常结合使用
        //如,求年龄总和(这边的map可以传入person返回age)
//        Optional<Integer> sum = Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
//                new Person("王五", 32, 40000),
//                new Person("张三", 18, 2400)).map(p -> p.age).reduce(Integer::sum);
//        System.out.println(sum);
        //统计字符a出现的次数
//        Integer sum = Stream.of("a", "b", "a", "e").map(ch -> "a".equals(ch) ? 1 : 0).reduce(0, Integer::sum);
//        System.out.println(sum);

//        14.mapToInt方法
        //如果要将Steam中的Integer类型转换成int类型,可以使用mapToInt方法
        //Integer占用的内存比int多,在stream流操作中会自动进行装箱和拆箱操作
//        Integer arr[] = {1, 2, 3, 4, 5, 6};
//        Stream.of(arr).filter(i -> i > 0).forEach(System.out::println);
        //为了提高程序代码的效率,我们可以先将流中Integer数据转换成int数据,然后再进行操作
//        Stream.of(arr).mapToInt(Integer::intValue).forEach(System.out::println);

//        15.concat方法,如果有两个流希望合并成一个流可以使用concat方法
        /*public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
            Objects.requireNonNull(a);
            Objects.requireNonNull(b);

            @SuppressWarnings("unchecked")
            Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
                    (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
            Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
            return stream.onClose(Streams.composedClose(a, b));
        }*/
//        Stream<String> stream1 = Stream.of("a", "b", "c");
//        Stream<String> stream2 = Stream.of("d", "efg");
//        Stream<String> newStream = Stream.concat(stream1, stream2);
//        newStream.forEach(System.out::println);
    }
}

class Person {
    String name;
    int age;
    double sal;

    public Person(String name, int age, double sal) {
        this.name = name;
        this.age = age;
        this.sal = sal;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sal=" + sal +
                '}';
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != (obj.getClass())) return false;
        Person p = (Person) obj;
        return p.name.equals(name) && p.age == age && p.sal == sal;
    }
}


综合案例

package com.bijing.streamApi;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author 毕晶
 * @date 2022/8/26 19:54
 * 演示Stream方法的综合案例
 * 定义两个集合,然后在集合中存储多个用户名称,然后完成以下操作
 * 1.第一个队伍中只保留姓名长度为3的成员
 * 2.第一个队伍筛选后只要前三个人
 * 3.第二个队伍只要姓张的人
 * 4.第二个队伍筛选后不要前两个人
 * 5.将两个队伍合并成一个队伍
 * 6.根据姓名创建Person对象
 * 7.打印整个队伍的Person信息
 */
public class StreamExample {
    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子", "洪七公");
        List<String> list2 = Arrays.asList("古力娜扎", "张无忌", "张三丰", "赵丽颖", "张二狗", "张天爱", "张三");

//        * 1.第一个队伍中只保留姓名长度为3的成员
//        * 2.第一个队伍筛选后只要前三个人
        Stream<String> stream1 = list1.stream().filter(s -> s.length() == 3).limit(3);
//        * 3.第二个队伍只要姓张的人
//        * 4.第二个队伍筛选后不要前两个人
        Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("张")).skip(2);
//        * 5.将两个队伍合并成一个队伍
//        * 6.根据姓名创建Person对象
//        * 7.打印整个队伍的Person信息
        Stream<String> stream = Stream.concat(stream1, stream2);
        Stream<Person> personStream = stream.map(Person::new);
        personStream.forEach(System.out::println);
    }
}

stream结果收集

结果收集到集合中

 //收集到集合中
    @Test
    public void test01() {
        //收集到List集合
        List<String> list = Stream.of("aa", "bb", "cc").collect(Collectors.toList());
        System.out.println(list);
        //收集到Set集合
        Set<String> collect = Stream.of("aa", "bb", "cc", "aa").collect(Collectors.toSet());
        System.out.println(collect);
        //如果需要获取的类型为具体实现,如ArrayList和HashSet
        ArrayList<String> arrayList = Stream.of("aa", "bb", "cc").collect(Collectors.toCollection(ArrayList::new));
        HashSet<String> hashSet = Stream.of("aa", "bb", "cc").collect(Collectors.toCollection(HashSet::new));
    }

结果收集到数组中

    //收集到数组中
    @Test
    public void test02() {
        //默认返回Object类型
        Object[] array = Stream.of("aa", "bb", "cc").toArray();
        System.out.println(Arrays.toString(array));
        //如果需要指定返回的类型
        String[] strings = Stream.of("aa", "bb", "cc", "aa").toArray(String[]::new);
        System.out.println(Arrays.toString(strings));
    }

对流中的数据做聚合计算

当我们使用Stream流后可以像数据库中的聚合函数一样对某个字段进行操作,比如获得最值,求和,求平均值,统计数量

 //聚合计算
    @Test
    public void test03() {
        //获取年龄最大值
        Optional<Person> max = Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
                new Person("王五", 32, 40000),
                new Person("张三", 18, 2400)).max(Comparator.comparingInt(p -> p.age));
        //或者
        Optional<Person> max2 = Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
                new Person("王五", 32, 40000),
                new Person("张三", 18, 2400)).collect(Collectors.maxBy((p1, p2) -> p1.age - p2.age));
        System.out.println("最大年龄的人" + max2);
        //获取年龄最大值
        int sum = Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
                new Person("王五", 32, 40000),
                new Person("张三", 18, 2400)).mapToInt(s -> s.age).sum();
        //或者
        Integer sumAge = Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
                new Person("王五", 32, 40000),
                new Person("张三", 18, 2400)).collect(Collectors.summingInt(p -> p.age));
        System.out.println("年龄的总和" + sumAge);
        //获取年龄的平均值
        Double ageAvg = Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
                new Person("王五", 32, 40000),
                new Person("张三", 18, 2400)).collect(Collectors.averagingInt(p -> p.age));
        System.out.println("年龄的平均值" + ageAvg);
        //统计数量
        Long count = Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
                new Person("王五", 32, 40000),
                new Person("张三", 18, 2400)).filter(p -> p.age > 20).collect(Collectors.counting());
        //或者
        long count1 = Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
                new Person("王五", 32, 40000),
                new Person("张三", 18, 2400)).filter(p -> p.age > 20).count();
        System.out.println("年龄大于20的人的数量是" + count);
    }

对流中的数据做分组操作

当我们使用Stream流处理数据后,可以根据某个属性对数据进行分组

 //分组计算
    @Test
    public void test04() {
        Map<String, List<Person>> collect = Stream.of(new Person("张三", 18, 2400), new Person("李四", 28, 23400),
                new Person("王五", 32, 40000),
                new Person("张三", 18, 2400)).collect(Collectors.groupingBy(Person::getName));
        collect.forEach((k, v) -> System.out.println(k + ":" + v));
        System.out.println("-------------------");
        Map<String, List<Person>> collect1 = Stream.of(new Person("张三", 11, 2400), new Person("李四", 28, 23400),
                new Person("王五", 2, 40000),
                new Person("张三", 18, 2400)).collect(Collectors.groupingBy(p -> p.age >= 18 ? "成年" : "未成年"));
        collect1.forEach((k, v) -> System.out.println(k + ":" + v));
    }

对流中数据做多级分组

    //分组计算--多级分组
    @Test
    public void test05() {
        //先根据name分组,再根据成年和未成年分组
        Map<String, Map<String, List<Person>>> collect = Stream.of(new Person("张三", 11, 2400), new Person("李四", 28,
                        23400),
                new Person("王五", 2, 40000),
                new Person("张三", 18, 2400)).collect(Collectors.groupingBy(Person::getName,
                Collectors.groupingBy(p -> p.age >= 18 ? "成年" : "未成年")));
        collect.forEach((k, v) -> {
            System.out.println(k);
            v.forEach((a, b) -> System.out.println(" " + a + ":" + b));
        });
    }

对流中数据做分区处理

    //分区处理
    @Test
    public void test06() {
        Map<Boolean, List<Person>> collect = Stream.of(new Person("张三", 11, 2400), new Person("李四", 28,
                        23400),
                new Person("王五", 2, 40000),
                new Person("张三", 18, 2400)).collect(Collectors.partitioningBy(p -> p.age > 10));
        collect.forEach((k, v) -> {
            System.out.println(k + "\n" + v);

        });
    }

对流中的数据做拼接处理


    //拼接处理
    @Test
    public void test07() {
        //joining()中的参数可以是无参的,可以是一个参数(分隔符),也可以三个参数(分隔符,前缀,后缀)
        String collect = Stream.of(new Person("张三", 11, 2400), new Person("李四", 28,
                        23400),
                new Person("王五", 2, 40000),
                new Person("张三", 18, 2400)).map(Person::getName).collect(Collectors.joining("-", "^^^", "$$$"));
        System.out.println(collect);
    }
    

并行的Stream流

串行的Stream流

前面使用的Stream流都是串行的,也就是在一个线程中使用

    //串行流
    @Test
    public void test1() {
        Stream.of(5, 6, 8, 3, 1, 6).filter(s -> {
            System.out.println(Thread.currentThread() + " " + s);
            return s > 3;
        }).count();
    }
    

输出

Thread[main,5,main] 5
Thread[main,5,main] 6
Thread[main,5,main] 8
Thread[main,5,main] 3
Thread[main,5,main] 1
Thread[main,5,main] 6

并行的Stream流

parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速度

获取并行流的两种方式
  1. 直接通过List接口获取
  2. 将已有的串行流转换为并行流
    //获取并行流的两种方式
    @Test
    public void test2() {
        List<Integer> list = new ArrayList<>();
        //直接通过List接口获取
        Stream<Integer> integerStream = list.parallelStream();
        //将已有的串行流转换为并行流
        Stream<Integer> parallel = Stream.of(5, 6, 8, 3, 1, 6).parallel();
        parallel.filter(s -> {
            System.out.println(Thread.currentThread() + " " + s);
            return s > 3;
        }).count();
    }

输出

Thread[ForkJoinPool.commonPool-worker-9,5,main] 5
Thread[ForkJoinPool.commonPool-worker-7,5,main] 1
Thread[ForkJoinPool.commonPool-worker-3,5,main] 6
Thread[ForkJoinPool.commonPool-worker-11,5,main] 8
Thread[ForkJoinPool.commonPool-worker-5,5,main] 6
Thread[main,5,main] 3
并行流和串行流对比
  @Test//240
    public void test3() {
        long start = System.currentTimeMillis();
        int res = 0;
        for (int i = 0; i < 500000000; i++) {
            res += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("普通方法用时:" + (end - start));
    }

    @Test//280
    public void test4() {
        long start = System.currentTimeMillis();
        LongStream.rangeClosed(0, 500000000).reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("串行流用时:" + (end - start));
    }

    @Test//86
    public void test5() {
        long start = System.currentTimeMillis();
        LongStream.rangeClosed(0, 500000000).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("并行流用时:" + (end - start));
    }

线程安全问题

 //并行流中数据安全问题
    @Test
    public void test6() {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
        System.out.println(list.size());

        List<Integer> listNew = new ArrayList<>();
        list.parallelStream().forEach(listNew::add);
        System.out.println(listNew.size());
        //出现问题:两次的大小不相等
        //解决方案1.synchronized 2.使用Vector 3.将线程不安全的容器转换成线程安全的容器 4.还可以通过Stream中的toArray或者collect方法来操作
        List<Integer> list3 = new ArrayList<>();
        List<Integer> list3New = Collections.synchronizedList(list3);
        list.parallelStream().forEach(list3New::add);
        System.out.println(list3New.size());

        List<Integer> list4New = IntStream.rangeClosed(1, 1000).parallel().boxed().collect(Collectors.toList());
        System.out.println(list4New.size());

    }

Fork/Join框架

在这里插入图片描述

Fork/Join原理—分治法

在这里插入图片描述

Fork/Join原理—工作窃取法

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

Fork/Join案例
package com.bijing.streamApi;

import org.junit.Test;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

/**
 * @author 毕晶
 * @date 2022/8/29 18:42
 * 演示Fork/Join案例
 */
public class ForkJoin {
    //需求:使用Fork/Join计算1-10000的和,当一个任务的计算数量大于3000的时候才分任务,数量小于3000的时候开始计算

    @Test
    public void test01() {
        long start = System.currentTimeMillis();
        ForkJoinPool pool = new ForkJoinPool();
        SumRecursiveTask task = new SumRecursiveTask(1, 10000l);
        Long result = pool.invoke(task);
        System.out.println("result=" + result);
        long end = System.currentTimeMillis();
        System.out.println("总的用时=" + (end - start));
    }
    
}

class SumRecursiveTask extends RecursiveTask<Long> {
    //定义一个拆分的临界值
    private static final long THRESHOLD = 3000L;

    private final long start;
    private final long end;

    public SumRecursiveTask(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end - start;
        if (length <= THRESHOLD) {
            //任务不用拆分,可以计算
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            System.out.println("计算:" + start + "--->" + end + "的结果为:" + sum);
            return sum;
        } else {
            //数量大于预定的数量,那说明任务还要继续拆分
            long middle = (start + end) / 2;
            System.out.println("拆分:坐标" + start + "--->" + middle + "右边" + (middle + 1) + "--->" + end);
            SumRecursiveTask left = new SumRecursiveTask(start, middle);
            //执行子任务
            left.fork();
            SumRecursiveTask right = new SumRecursiveTask(middle + 1, end);
            right.fork();
            //合并子任务
            return left.join() + right.join();
        }
    }
}

五、Optional类

这个Optional类主要是用来解决空指针的问题

1.以前对于null的处理

    @Test
    public void test1() {
        String userName = null;
        if (userName != null) {
            System.out.println("字符串的长度" + userName.length());
        } else {
            System.out.println("字符串为空");
        }
    }

2.Optional类

Optional类是一个没有子类的工具类,Optional是一个可以为null的容器对象,它的主要作用就是避免Null检查,防止出现NullPointerException

3.Optional的基本使用

Optional对象的创建方式

    //获取Optional对象
    @Test
    public void test02() {
        //1.通过Optional的of方法,of方法是不支持null的
        Optional<String> op1 = Optional.of("张三");
        //Optional<String> op2 = Optional.of(null);


        //2.第二种方式通过ofNullable方法,支持null
        Optional<String> op3 = Optional.ofNullable("lisi");
        Optional<String> op4 = Optional.ofNullable(null);

        //3.第三种方式 通过empty方法直接创建一个空的Optional对象
        Optional<Object> op5 = Optional.empty();

    }

4.Optional的常用方法

   //Optional中的常用方法介绍
    /* get() :如果Optional有值则返回,否则抛出NoSuchElementException异常
              get()通常和isPresent()方法一块使用
        isPresent():判断是否包含值,返回boolean类型
        orElse(T t):如果调用对象包含值就返回该值,否则返回t
        orElseGet(Supplier s)如果调用对象包含值就返回该值,否则返回Lambda表达式中的返回值
     */
    @Test
    public void test03() {
        Optional<String> op1 = Optional.of("张三");
        Optional<String> op2 = Optional.empty();
        //获取Optional中的值
        if (op1.isPresent()) {
            String s1 = op1.get();
            System.out.println(s1);
        }
        if (op2.isPresent()) {
            Object s2 = op2.get();
            System.out.println(s2);
        } else {
            System.out.println("op2是一个空的Optional对象 ");
        }

        String s3 = op1.orElse("李四");
        System.out.println(s3);
        String s4 = op2.orElse("王五");
        System.out.println(s4);
        op2.orElseGet(() -> "hello");

    }

    @Test
    public void test04() {
        Optional<String> op1 = Optional.of("张三");
        Optional<String> op2 = Optional.empty();
        //如果存在值 就做什么
        op1.ifPresent(s -> System.out.println("有值:" + s));
        op1.ifPresent(System.out::println);
    }

    //自定义一个方法,将Person对象中的name转换为大写并返回
    @Test
    public void test05() {
        String nameForOptional = getNameForOptional(Optional.of(new Person("vhsj", 19)));
        System.out.println(nameForOptional);
    }

    @Test
    public String getNameForOptional(Optional<Person> op) {
        if (op.isPresent()) {
            return op.map(Person::getName).orElse("为空").toUpperCase();
        } else {
            return "为空";
        }
    }

    @Test
    public String getName(Person person) {
        if (person != null) {
            if (person.getName() != null) {
                return person.getName().toUpperCase();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }
}

class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值