当前位置: 首页 > article >正文

JAVA SE 4.Java各版本特性

8.Java各版本特性

1.Java8

  • 1.Lambda表达式
  • 2.函数式接口
  • 3.方法引用
  • 4.默认方法
  • 5.Stream API
  • 6.Optional
  • 7.Date Time API
  • 8.Nashorn、JavaScript引擎

1.Lambda表达式

  • 1.定义Lambda表达式是对匿名函数简写形式Lambda允许把函数作为一个方法的参数,可以写出更简洁的代码
  • 2.实质语法糖,实质底层还是一个方法
    package domains;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    
    /**
     * 员工
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    public class Employee {
        private String name;
        private int age;
        private double salary;
    }
    
    package dao;
    
    public interface TestPredicate<T> {
        boolean test(T e);
    }
    
    package dao.daoImpl;
    
    import dao.TestPredicate;
    import domains.Employee;
    
    public class FilterEmployeeByAge implements TestPredicate<Employee> {
        @Override
        public boolean test(Employee employee) {
            return employee.getAge() > 24;
        }
    }
    
    package dao.daoImpl;
    
    import dao.TestPredicate;
    import domains.Employee;
    
    public class FilterEmployeeBySalary implements TestPredicate<Employee> {
        @Override
        public boolean test(Employee employee) {
            return employee.getSalary() > 8000;
        }
    }
    
    import dao.TestPredicate;
    import dao.daoImpl.FilterEmployeeByAge;
    import dao.daoImpl.FilterEmployeeBySalary;
    import domains.Employee;
    import org.junit.Test;
    import java.util.*;
    
    public class TestLambda {
    
        List<Employee> employees = Arrays.asList(
                new Employee("张三",8,100),
                new Employee("李四",12,500),
                new Employee("王五",18,3000),
                new Employee("赵六",24,8000),
                new Employee("田七",35,15000),
                new Employee("吴八",40,25000)
        );
    
        /**
         * 原始
         */
        @Test
        public void test1(){
            //比较器 - 匿名内部类
            Comparator<Integer> comparator = new Comparator<Integer>(){
                @Override
                public int compare(Integer o1, Integer o2) {
                    return Integer.compare(o1,o2);
                }
            };
            //注意:比较器的类型需要和TreeSet类型一致
            TreeSet<Integer> treeSet = new TreeSet<>(comparator);
        }
    
        /**
         * Lambda表达式化简
         */
        @Test
        public void test2(){
            //化简1
            Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
            TreeSet<Integer> treeSet = new TreeSet<>(comparator);
            //化简2
            TreeSet<Integer> integerTreeSet = new TreeSet<>((x,y) -> Integer.compare(x,y));
        }
    
        /**
         * 需求1:获取员工年龄大于24岁的员工信息
         */
        @Test
        public void test3(){
            List<Employee> filterEmployees = filterAgeEmployees(employees);
            for (Employee filterEmployee : filterEmployees) {
                System.out.println(filterEmployee);
                /*  结果:
                 Employee(name=田七, age=35, salary=15000.0)
                 Employee(name=吴八, age=40, salary=25000.0)
                */
            }
        }
    
        /**
         * 需求1:获取员工年龄大于24岁的员工信息 优化1
         */
        @Test
        public void test4(){
            List<Employee> filterEmployees = filterEmployee(employees, new FilterEmployeeByAge());
            for (Employee filterEmployee : filterEmployees) {
                System.out.println(filterEmployee);
                /*  结果:
                    Employee(name=田七, age=35, salary=15000.0)
                    Employee(name=吴八, age=40, salary=25000.0)
                */
            }
        }
    
        /**
         * 需求2:获取员工工资大于8000的员工信息
         */
        @Test
        public void test5(){
            List<Employee> filterEmployees = filterSalaryEmployees(employees);
            for (Employee filterEmployee : filterEmployees) {
                System.out.println(filterEmployee);
                /*  结果:
                    Employee(name=田七, age=35, salary=15000.0)
                    Employee(name=吴八, age=40, salary=25000.0)
                */
            }
        }
    
        /**
         * 需求2:获取员工工资大于8000的员工信息 优化1
         */
        @Test
        public void test6(){
            List<Employee> filterEmployees = filterEmployee(employees,new FilterEmployeeBySalary());
            for (Employee filterEmployee : filterEmployees) {
                System.out.println(filterEmployee);
                /*  结果:
                    Employee(name=田七, age=35, salary=15000.0)
                    Employee(name=吴八, age=40, salary=25000.0)
                */
            }
        }
    
        /**
         * 根据年龄条件过滤员工信息
         * @param employees
         * @return
         */
        public List<Employee> filterAgeEmployees(List<Employee> employees){
            ArrayList<Employee> employeeArrayList = new ArrayList<>();
            for (Employee employee : employees) {
                if(employee.getAge() > 24){
                    employeeArrayList.add(employee);
                }
            }
            return employeeArrayList;
        }
    
        /**
         * 根据薪水条件过滤员工信息
         * @param employees
         * @return
         */
        public List<Employee> filterSalaryEmployees(List<Employee> employees){
            ArrayList<Employee> employeeArrayList = new ArrayList<>();
            for (Employee employee : employees) {
                if(employee.getSalary() > 8000){
                    employeeArrayList.add(employee);
                }
            }
            return employeeArrayList;
        }
    
        //优化方式一:策略设计模式. 优点:可以根据传入的策略执行不同的策略
        //注意:泛型类型需要填写,否则lambda会发生类型不匹配异常
        public List<Employee> filterEmployee(List<Employee> list, TestPredicate<Employee> predicate){
         ArrayList<Employee> employeeArrayList = new ArrayList<>();
            for (Employee employee : employees) {
                if(predicate.test(employee)){
                    employeeArrayList.add(employee);
                }
            }
            return employeeArrayList;
        }
    
        //优化方式二:匿名内部类
        @Test
        public void test7(){
            List<Employee> filterEmployees = filterEmployee(employees,new TestPredicate<Employee>(){
                @Override
                public boolean test(Employee employee) {
                    return employee.getSalary() < 5000;
                }
            });
            for (Employee filterEmployee : filterEmployees) {
                System.out.println(filterEmployee);
                /*  结果:
                    Employee(name=张三, age=8, salary=100.0)
                    Employee(name=李四, age=12, salary=500.0)
                    Employee(name=王五, age=18, salary=3000.0)
                */
            }
        }
        //优化方式三:Lambda表达式
        @Test
        public void test8(){
            List<Employee> filterEmployees = filterEmployee(employees,(e) -> e.getSalary() < 5000);
            filterEmployees.forEach(System.out::println);
        }
    
        //优化方式四:Stream API
        @Test
     public void test9(){
            employees.stream().filter((e) -> e.getSalary() < 5000).forEach(System.out::println);
        }
    
    }
    
1.Lambda基础语法
import org.junit.Test;

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

/**
 * Lambda表达式的基础语法:Java8中引入了一个新的操作符->,该操作符为箭>	头操作符或Lambda操作符
 * 箭头操作符将Lambda表达式拆分成两部分:
 * 左侧:Lambda表达式的参数列表
 * 右侧:Lambda表达式中所需执行的功能,即Lambda体
 *
 * 语法格式一:无参数,无返回值(小括号不可省略)
 *      () -> System.out.println("Hello Lambda");
 *
 * 语法格式二:一个参数,无返回值(小括号可省略,建议加上)
 *      x -> System.out.println(X);
 *
 * 语法格式三:两个及两个以上参数,并且Lambda体中有多条语句,小括号和大括号和return都不能省略
 *
 * 语法格式四:如果Lambda体中只有一条语句,return和大括号皆可省略
 *
 * 语法格式五:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编辑器通过上下文可以推断出数据类型,即类型推断
 *
 * 总结:
 *      1.可选类型声明:不需要声明参数类型,编译器可以通过上下文可以推断出数据类型
 *      2.可选参数圆括号:一个参数无需定义圆括号,但多个参数或无参需要定义圆括号
 *      3.可选的大括号:如果主题体只包含了一个语句,就不需要使用大括号,否则需要大括号
 *      4.可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,否则需要加return
 *
 * Lambda需要函数式接口的支持
 *      * 函数式接口:接口中只有一个抽象方法的接口
 *      * 通过注解@FuncationalInterface修饰可以检查是否为函数式接口
 *
 * 注意事项:
 *      1.lambda表达式只能引用标记了final的外层局部变量,即不能在Lambda内部修改定义在域外的局部变量,否则会编译错误(jdk7)
 *      2.lambda表达式的引用的外层局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义,jdk8)
 *      3.lambda达式当中不允许声明一个与局部变量同名的参数或者局部变量
 */
public class TestLambda2 {
    
    @Test
    public void test1(){
        //传统匿名内部类
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                System.out.println("Hello World");
            }
        };
        runnable.run();
        //lambda表达式
        Runnable lambda = () -> System.out.println("Hello Lambda");
        lambda.run();
    }

    @Test
    public void test2(){
        Consumer<String> consumer = x -> System.out.println(x);
        consumer.accept("测试成功!");
    }

    @Test
    public void test3(){
        Comparator<Integer> comparator = (x, y) -> {
            System.out.println("测试成功!");
            return Integer.compare(x, y);
        };
    }

    @Test
    public void test4(){
        Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
    }
}
2.Lambda上下文推断
//不能拆分开写,否则无法根据上下文推断出aaa,bbb,ccc的类型
String[] strs = {"aaa","bbb","ccc"}

//后面的泛型可以不写,可以通过前面的泛型推断出来
List<String> list = new ArrayList<>()//java8中show(new HashMap<>())可以不写泛型,通过上下文类型推断出结果
show(new HashMap<>());
public void show(Map<String, Integer> map){
	...
}

2.函数式接口

  • 1.定义: 只包含一个抽象方法的接口
  • 2.使用@FunctionalInterface注解,可以检查是否为函数式接口
1.四大核心函数式接口
函数式接口参数类型返回类型用途
Consumer<T> 消费型接口Tvoid对类型为T的对象应用操作。包含方法:void accept(T t)
Supplier<T> 供给型接口T返回类型为T的对象。包含方法:T get()
Function<T, R> 函数型接口TR对类型为T的对象应用操作,并返回R类型对象的结果。包含方法:R apply(T t)
Predicate<T> 断言型接口Tboolean确定类型为T的对象是否满足某约束。包含方法:boolean test(T t)
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Java8内置的四大核心函数式接口
 *
 * Consumer<T></消费型接口>
 *      void accept(T t);
 *
 * Supplier<T></供给型接口>
 *      T get();
 *
 * Function<T, R></函数型接口>
 *      R apply(T t);
 *
 * Predicate<T></断言型接口>
 *      boolean test(T t);
 */
public class TestLambda4 {

    //Consumer<T></消费型接口>
    @Test
    public void test1(){
        shopping(1000,(m) -> System.out.println("书包,消费" + m/4));
    }
    public void shopping(double money, Consumer<Double> consumer){
        consumer.accept(money);
    }

    //Supplier<T></供给型接口>
    @Test
    public void test2(){
        List<Integer> integers = getIntegers(10, () -> (int) (Math.random() * 100));
        for (Integer integer : integers) {
            System.out.println(integer);
        }
    }

    public List<Integer> getIntegers(int num, Supplier<Integer> supplier){
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Integer n = supplier.get();
            list.add(n);
        }
        return list;
    }

    //Function<T, R></函数型接口>
    @Test
    public void test3(){
        String s = strHandler("三生三世:十里桃花", (str) -> str.replace(":", "+"));
        System.out.println(s);
    }

    public String strHandler(String str, Function<String,String> function){
        return function.apply(str);
    }

    //Predicate<T></断言型接口>
    @Test
    public void test4(){
        List<String> statement = Arrays.asList("三生三世,十里桃花","人生若只如初见","三生三世,情非得已");
        List<String> stringList = filterStr(statement, (str) -> str.contains("三生三世"));
        for (String s : stringList) {
            System.out.println(s);
        }
    }

    public List<String> filterStr(List<String> list, Predicate<String> predicate){
        List<String> stringList = new ArrayList<>();
        for (String s : list) {
            if(predicate.test(s)){
                stringList.add(s);
            }
        }
        return  stringList;
    }

}
2.其他函数式接口
函数式接口参数类型返回类型用途
BiConsumer<T,U>T,Uvoid对类型为T,U的对象应用操作。包含方法:void accept(T t,U u)
BiFunction<T,U,R>T,UR对类型为T,U参数的对象应用操作,返回R类型的结果。包含方法:R apply(T t,U u)
BiPredicate<T,U>T,Uboolean确定类型为T,U的对象是否满足某约束。包含方法:boolean test(T t, U u)

3.引用

  • 1.Lambda表达式的另一种写法
  • 2.当要传递给Lambda体的操作已经有实现则可以使用方法引用
1.方法引用
import domains.Employee;
import org.junit.Test;

import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 方法引用:若Lambda体中的方法已经被其他类实现了,可以使用方法引用
 *
 * 三种法语格式:
 *      1.对象::实例方法名
 *      2.类::静态方法名
 *      3.类::实例方法名(需满足条件2)
 *
 *  注意:
 *      1.Lambda体中调用的方法需要与函数式接口的参数类型和返回值保持一致
 *      2.Lambda参数列表的第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用语法格式三
 *
 */
public class TestLambda5 {

    //1.对象::实例方法名
    @Test
    public void test1(){
        Consumer<String> consumer = (x) -> System.out.println(x);
        //等价于
        //注意:调用的方法需要与函数式接口接口的参数类型和返回值需要一致
        Consumer<String> consumer1 = System.out::println;

        consumer1.accept("200");
        shopping(1000, System.out::println);
    }
    public void shopping(double money, Consumer<Double> consumer){
        consumer.accept(money);
    }

    //2.类::静态方法名
    @Test
    public void test2(){
        Comparator<Integer> comparator = (x, y) -> Integer.compare(x,y);
        //等价于
        Comparator<Integer> comparator1 = Integer::compareTo;
    }

    //3.类::实例方法名
    @Test
    public void test3(){
        BiPredicate<String, String> biPredicate = (x, y) -> x.equals(x);
        BiPredicate<String, String> biPredicate1 = String::equals;
    }
}
2.构造器引用
/**  
*	构造器引用:若Lambda体中存在新建对象,可以使用构造器引用
*  	法语格式:
*      ClassName::new
*  	注意:
*      1.函数式接口的方法的参数列表匹配对应的实体类的构造器
*/
	//构造器引用
	@Test
	public void test4(){
		Supplier<Employee> supplier = () -> new Employee();
		supplier.get();
		//等价于
		//函数式接口的方法的参数列表匹配对应的实体类的构造器
		Supplier<Employee> supplier1 = Employee::new;
		Employee employee = supplier1.get();
		System.out.println(employee);

		//Function<Integer, Employee> function = (x) -> new Employee(x);
		Function<Integer, Employee> function = Employee::new;
		Employee apply = function.apply(1);
		System.out.println(apply);
	}
3.数组引用
/*
*  	数组引用:
*	语法格式:
*   	Type[]::new
*/     
	//数组引用
	@Test
	public void test5(){
	   Function<Integer, String[]> function = (x) -> new String[x];
	   String[] apply = function.apply(10);
	   System.out.println(apply.length);
	   //等价于
	   Function<Integer, String[]> function1 = String[]::new;
	   String[] apply1 = function1.apply(20);
	   System.out.println(apply1.length);
	}

5.Stream

1.Stream的概念
  • Stream是Java8处理集合的关键抽象概念,它可以指定对集合进行的操作,可以执行非常复杂的查找,过滤,映射数据等操作
  • 流(Stream)是数据通道,用于操作数据源(集合,数组等)所生成的元素序列
  • 完整流包含以下结构:
    • 1.数据源(集合,数组等)
    • 2.通道(做一系列流水线式的中间操作)
    • 3.新流(终止操作产生)
  • 注意:
    • 1.Stream不会存储元素
    • 2.Stream不会改变数据源,而是返回一个持有结果的新Stream
    • 3.Stream操作是延迟执行的,即等到需要结果(终止操作)的时候才会被执行
2.Stream的操作三个步骤
  • 1.创建Stream
    • 通过一个数据源(如:集合,数组),获取一个流
  • 2.中间操作
    • Stream的一个中间操作链,对数据源的数据进行处理
  • 3.终止操作
    • Stream的一个终止操作,执行中间操作链,并产生结果
3.创建Stream的四种方式
import domains.Employee;
import org.junit.Test;

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

/**
 * Stream的三个操作步骤:
 *      1.创建Stream
 *      2.中间操作
 *      3.终止操作
 */
public class TestLambda6 {

    //创建Stream
    @Test
    public void test1(){
        //1.通过Collection系列集合可以获取的stream()串行流或parallelStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream();

        //2.通过Arrays中的静态方法Stream可以获取数组流
        Employee[] employees = new Employee[10];
        Stream<Employee> stream1 = Arrays.stream(employees);

        //3.通过Stream类中的静态方法of()
        Stream<String> stream3 = Stream.of("aa", "bb", "cc");

        //4.创建无限流
            //第一种无限流:迭代
            Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
            //中间操作
            Stream<Integer> limit = stream4.limit(10);
            //终止操作
            limit.forEach(System.out::println);

            //第二种无限流:生成
            Stream.generate(() -> new Random().nextInt(100)).limit(10).forEach(System.out::println);
    }
}

4.中间操作

  • 延迟加载:中间操作默认不会执行任何操作,只有执行了终止操作,所有的中间操作才会执行,这个过程叫做惰性求值或者延迟加载(多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理)
  • 内部迭代:迭代操作由Stream API完成
  • 外部迭代:手动完成
1.筛选与切片
List<Employee> employees = Arrays.asList(
            new Employee("张三",8,100),
            new Employee("李四",12,500),
            new Employee("王五",18,3000),
            new Employee("赵六",24,8000),
            new Employee("赵九",24,6000),
            new Employee("田七",35,15000),
            new Employee("吴八",40,25000),
            new Employee("吴八",40,25000)
    );

    @Test
    public void test2(){
        /**
         * 筛选与切片
         * Stream<T> filter(Predicate<? super T> predicate):接收一个断言型的Lambda表达式,从Stream流中排除满足条件的某些元素
         * Stream<T> limit(long maxSize):截断流,使其元素不超过指定数量;断路:如果发现了满足条件的数据就不再迭代
         * Stream<T> skip(long n):调过元素,返回一个跳过了前n个元素的流,若流中的元素不足n个,则返回一个空流(与limit(n)互补)
         * Stream<T> distinct():筛选,通过流所生成元素的hashCode()和equals()去除重复元素;注意需要重新写hashCode和equals()方法
         */
        //注意:没有终止操作,中间操作不会执行任何操作
        //外层迭代:手动完成
//        Iterator<Employee> iterator = employees.iterator();
//        while (iterator.hasNext()) {
//            System.out.println(iterator.next());
//        }
        //内层迭代:迭代操作由Stream API
        employees.stream().filter((e) -> e.getAge() > 24).forEach(System.out::println);
        employees.stream().filter((e) -> e.getAge() > 24).limit(1).forEach(System.out::println);
        employees.stream().filter((e) -> e.getAge() > 24).skip(1).forEach(System.out::println);
        employees.stream().filter((e) -> e.getAge() > 24).distinct().forEach(System.out::println);
    }
2.映射
@Test
    public void test3(){
        /**
         * 映射
         * map:接收一个函数型的Lambda表达式,该参数会被应用到每个元素上,并将其映射成一个新的元素,用于将元素转换成其他形式或者提取信息。(把流加入其中)
         * flatMap:接收一个函数型的Lambda表达式,将流中的每个元素都转换成另一个流,然后把所有流连接成一个流(把一个个流中的元素加入其中)
         */
        List<String> list = Arrays.asList("aaa","bbb","ccc");
        list.stream()
                .map((str) -> str.toUpperCase())
                .forEach(System.out::println);
     	employees.stream().map(Employee::getName).forEach(System.out::println);

        //map与flatMap的区别
        Stream<Stream<Character>> streamStream = list.stream().map(TestLambda6::filterCharacter);
        streamStream.forEach((stream) -> {
            stream.forEach(System.out::println);
        });

        Stream<Character> characterStream = list.stream().flatMap(TestLambda6::filterCharacter);
        characterStream.forEach(System.out::println);

        //类似list的add和addAll
        List<String> list1 = Arrays.asList("aaa", "bbb", "ccc");
        List list2 = new ArrayList<>();
        list2.add("111");
        list2.add("222");
        list2.add("333");
        list2.add(list1);
        /**
         * 结果
         * [111, 222, 333, [aaa, bbb, ccc]]
         */
        list2.addAll(list1);
        /**
         * 结果
         * [111, 222, 333, [aaa, bbb, ccc], aaa, bbb, ccc]
         */
        System.out.println(list2);
    }

    public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for (Character c : str.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }
3.排序
@Test
    public void test4(){
        /**
         * 排序
         * sorted():自然排序(Comparable)
         * sorted(Comparator c):定制排序(Comparator)
         */
        List<String> list = Arrays.asList("ccc", "aaa", "bbb");
        list.stream()
                .sorted()
                .forEach(System.out::println);
        employees.stream().sorted((e1 ,e2) -> {
            if(e1.getAge() == e2.getAge()){
                return e1.getName().compareTo(e2.getName());
            }else {
                return -e1.getAge() - e2.getAge();
            }
        }).forEach(System.out::println);
    }

5.终止操作

  • 终止操作会从流的流水线生成结果
1.查找与匹配
@Test
    public void test5(){
        /**
         * 终止操作
         * 1.查找与匹配
         *      allMatch(Predicate<? super T> predicate):接收一个断言型的Lambda表达式,检查是否流中的所有元素都满足条件
         *      anyMatch(Predicate<? super T> predicate):接收一个断言型的Lambda表达式,检查流中是否至少有一个元素满足条件
         *      noneMatch(Predicate<? super T> predicate):接收一个断言型的Lambda表达式,检查是否流中的所有元素都不满足条件
         *      findFirst:返回第一个元素
         *      findAny:返回任意元素
         *      count:返回流中元素的总个数
         *      max:返回流中最大值
         *      min:返回流中最小值
         */

        boolean b = employees.stream().allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b);

        boolean b1 = employees.stream().anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b1);

        boolean b2 = employees.stream().noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b2);

        Optional<Employee> first = employees.stream().findFirst();
        System.out.println(first.get());

        Optional<Employee> any = employees.parallelStream().findAny();
        System.out.println(any.get());

        long count = employees.stream().count();
        System.out.println(count);

        Optional<Employee> max = employees.stream().max(Comparator.comparingDouble(Employee::getSalary));
        System.out.println(max.get());

        Optional<Double> min = employees.stream().map(Employee::getSalary).min(Double::compareTo);
        System.out.println(min.get());
    }
2.归约
	@Test
    public void test6(){
        /**
         * 终止操作
         * 2.归约
         *     T reduce(T identity, BinaryOperator<T> accumulator):将流中元素反复结合起来(类似累加),得到一个值;identity:初始值,BinaryOperator<T>:二元操作
         */
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        Integer sum = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(sum);
    }
3.收集
 @Test
    public void test7(){
        /**
         * 终止操作
         * 2.收集
         *     collect:将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
         *     Collector:接口中方法的实现决定了如何对流执行收集操作
         *     Collectors:实现类提供了很多静态方法,可以方便创建收集器实例
         *          1.mapping
         *          2.join()
         *          3.toList()
         *          4.toSet()
         *          5.toMap()
         *          6.toConcurrentMap()
         *          7.toCollection()
         *          8.counting()
         *          9.averagingDouble
         *          10.summarizingDouble
         *          11.maxBy()
         *          12.minBy()
         */

        //映射
        List<Person> list = new ArrayList<>();
        list.add(new Person("Ram", 30));
        list.add(new Person("Shyam", 20));
        list.add(new Person("Shiv", 20));
        list.add(new Person("Mahesh", 30));

        String nameByAge = list.stream().collect(Collectors.mapping(Person::getName, Collectors.joining(",", "[", "]")));
        System.out.println(nameByAge);
        nameByAge = list.stream().map(person -> person.getName()).collect(Collectors.joining(",", "[", "]"));
        System.out.println(nameByAge);

        Map<Integer, String> nameByAgeMap = list.stream().collect(
                Collectors.groupingBy(Person::getAge, Collectors.mapping(Person::getName, Collectors.joining(",", "[", "]"))));
        nameByAgeMap.forEach((k, v) -> System.out.println("Age:" + k + "  Persons: " + v));

        //连接符,前缀,后缀
        List<String> list1 = Arrays.asList("A","B","C","D");
        String result=  list1.stream().collect(Collectors.joining("-","[","]"));
        System.out.println(result);

        List<String> collect = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        collect.forEach(System.out::println);

        Set<String> collect1 = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());
        collect1.forEach(System.out::println);

        //Duplicate key:使用toMap()的重载方法,如果已经存在则不再修改,直接使用上一个数据
        //(e1, e2) -> e1 这里使用的箭头函数,也就是说当出现了重复key的数据时,会回调这个方法,可以在这个方法里处理重复Key数据问题
        Map<String, Double> collect8 = employees.stream().collect(Collectors.toMap(Employee::getName, Employee::getSalary, (e1, e2) -> e1));
        for (Map.Entry<String, Double> stringDoubleEntry : collect8.entrySet()) {
            System.out.println(stringDoubleEntry.getKey() + ":" + stringDoubleEntry.getValue());
        }

        ConcurrentMap<String, Double> collect9 = employees.stream().collect(Collectors.toConcurrentMap(Employee::getName, Employee::getSalary, (e1, e2) -> e1));
        for (Map.Entry<String, Double> stringDoubleEntry : collect9.entrySet()) {
            System.out.println(stringDoubleEntry.getKey() + ":" + stringDoubleEntry.getValue());
        }

        HashSet<String> collect2 = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        collect2.forEach(System.out::println);

        Long collect3 = employees.stream()
                .collect(Collectors.counting());
        System.out.println(collect3);

        Double collect4 = employees.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(collect4);
		
		//包含总数,总和,最小值,平均值,最大值
        DoubleSummaryStatistics collect5 = employees.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(collect5);

        Optional<Employee> collect6 = employees.stream()
                .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(collect6.get());

        Optional<Employee> collect7 = employees.stream()
                .collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(collect7.get());
    }
4.分组
	@Test
    public void test8(){
        /**
         * 1.分组
         * 2.多级分组
         */
        Map<Employee.Status, List<Employee>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
        for (Map.Entry<Employee.Status, List<Employee>> statusListEntry : collect.entrySet()) {
            System.out.println(statusListEntry.getKey() + ":" + statusListEntry.getValue());
        }

        Map<Employee.Status, Map<String, List<Employee>>> collect1 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
            if (e.getAge() <= 35) {
                return "青年";
            } else if (e.getAge() <= 50) {
                return "中年";
            } else {
                return "老年";
            }
        })));
        for (Map.Entry<Employee.Status, Map<String, List<Employee>>> statusMapEntry : collect1.entrySet()) {
            System.out.println(statusMapEntry.getKey() + ":" + statusMapEntry.getValue().entrySet());
        }
    }
5.分区
	@Test
    public void test9(){
        /**
         * 分区 true和false分区,满足条件的一区,不满足条件的一区
         */
        Map<Boolean, List<Employee>> collect = employees.stream().collect(Collectors.partitioningBy((e) -> e.getSalary() > 8000));
        for (Map.Entry<Boolean, List<Employee>> booleanListEntry : collect.entrySet()) {
            System.out.println(booleanListEntry.getKey() + ":" + booleanListEntry.getValue());
        }
    }

4.并行流与顺序流

  • 并行流是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流
  • Java8中将并行流进行了优化,可以更方便的对数据进行并行操作,Stream API可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换

Fork/Join框架

  • 注意:需要继承RecursiveTask
  • 概念:在必要的情况下将一个大任务,进行拆分(fork)成若干个小任务,拆到不可再拆时,再将一个个的小任务运算的结果进行join汇总
  • Fork/Join框架与传统线程池的区别
    • 1.Fork/Join框架采用工作窃取模式(work-stealing)
    • 即:当执行新的任务时它可以将其拆分,分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中
    • 2.相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上,在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态;而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行,那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行,这种方式减少了线程的等待时间,提高了性能
    package domains;
    
    import java.util.concurrent.RecursiveTask;
    
    public class ForkJoinCalculate extends RecursiveTask<Long> {
    
        private static final long serialVersionUID = -8301484471666236858L;
    
        private long start; //开始值
        private long end;   //结束值
    
        private static final long THRESHOLD = 10000;    //拆分终止值:拆分到什么时候才结束
    
        public ForkJoinCalculate(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;
                }
                return sum;
            }else {
                //拆分
                //一半一半的拆
                long middle = (start + end) / 2;
    
                ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
                left.fork(); //拆分子任务,同时压入线程队列
    
                ForkJoinCalculate right = new ForkJoinCalculate(middle+1, end);
                right.fork();
    
                //合并
                return left.join() + right.join();
            }
    
        }
    }
    
    import domains.ForkJoinCalculate;
    import org.junit.Test;
    
    import java.time.Duration;
    import java.time.Instant;
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.ForkJoinTask;
    import java.util.stream.LongStream;
    
    public class TestForkJoin {
    
        @Test
        public void test1(){
            Instant start = Instant.now();
    
            ForkJoinPool pool = new ForkJoinPool();
            ForkJoinTask<Long> task = new ForkJoinCalculate(0,1000000000L);
            Long sum = pool.invoke(task);
            System.out.println("累加和为:" + sum);
    
            Instant end = Instant.now();
    
            System.out.println("消耗时间为:" + Duration.between(start, end).toMillis());
            /**
             *  CPU利用率:100%
             *  累加和为:500000000500000000
             *  消耗时间为:111
             */
        }
    
        @Test
        public void test2(){
            Instant start = Instant.now();
    
            long sum = 0L;
            for (int i = 0; i < 1000000000L; i++) {
                sum += i;
            }
            System.out.println("累加和为:" + sum);
    
            Instant end = Instant.now();
    
            System.out.println("消耗时间为:" + Duration.between(start, end).toMillis());
            /**
             *  CPU利用率:20%左右
             *  累加和为:499999999500000000
             * 消耗时间为:364
             */
        }
    
        @Test
        public void test3(){
            /**
             * java8并行流
             */
    
            Instant start = Instant.now();
    
            long sum = LongStream.rangeClosed(0, 1000000000L)
                    .parallel() //并行流
                    .reduce(0, Long::sum);
    
            System.out.println("累加和为:" + sum);
    
            Instant end = Instant.now();
    
            System.out.println("消耗时间为:" + Duration.between(start, end).toMillis());
            /**
             *  CPU利用率:100%左右
             *  累加和为:500000000500000000
             *  消耗时间为:136
             */
        }
    }
    

5.Optional类

  • java.util.Optional类是一个容器类,代表一个值存在或不存在
  • 原来用null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常
    package domains;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Godness {
    
        private String name;
    }
    
    package domains;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Man {
    
        private Godness godness;
    }
    
    package domains;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.Optional;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class NewMan {
    
        //可能为空的属性用Optional包装
        //需要初始值,不能为null
        private Optional<Godness> godness = Optional.empty();
    }
    
    import domains.Employee;
    import domains.Godness;
    import domains.Man;
    import domains.NewMan;
    import org.junit.Test;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    
    public class TestOptional {
        @Test
        public void test1(){
            /**
             *  Optional.of(T t):创建一个Optional实例,不能传递null会报空指针异常,可以快速定位
             *  Optional.empty():创建一个空的Optional实例
             *  Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例
             *  isPresent():判断是否包含值
             *  orElse(T t):如果调用对象包含值,返回该值,否则返回t
             *  orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值;和orElse区别是可以通过供给型接口的实现类实现更多功能
             *  map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
             *  flatMap(Function mapper):与map类似,要求返回值必须是Optional
             *  filter:接收一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty
             */
            Optional<Employee> optional = Optional.of(new Employee());
            Employee employee = optional.get();
            System.out.println(employee);
    
    //        Optional<Object> empty = Optional.empty();
    //        System.out.println(empty.get());
    
    //        Optional<Object> o = Optional.ofNullable(null);
    //        System.out.println(o.get());
    
            Optional<Object> o = Optional.ofNullable(null);
            if(o.isPresent()){
                System.out.println(o.get());
            }
    
            Optional<Employee> objectOptional = Optional.ofNullable(null);
            System.out.println(objectOptional.orElse(new Employee(25)));
    
            System.out.println(objectOptional.orElseGet(() -> new Employee("李四",3,35.5, Employee.Status.BUSY)));
    
            Optional<Employee> employee1 = Optional.ofNullable(new Employee("李四",3,35.5, Employee.Status.BUSY));
            Optional<String> optional1 = employee1.map(Employee::getName);
            System.out.println(optional1.get());
    
            Optional<String> s = employee1.flatMap((e) -> Optional.of(e.getName()));
            System.out.println(s.get());
    
            List<Employee> employees = Arrays.asList(
                    new Employee("张三",8,100, Employee.Status.BUSY),
                    new Employee("李四",12,500, Employee.Status.BUSY),
                    new Employee("王五",18,3000, Employee.Status.FREE),
                    new Employee("赵六",24,8000, Employee.Status.FREE),
                    new Employee("赵九",24,6000, Employee.Status.FREE),
                 new Employee("田七",35,15000, Employee.Status.VOCATION),
                    new Employee("吴八",40,25000, Employee.Status.FREE)
            );
            Optional<Employee> optional2 = Optional.ofNullable(employees.get(5)).filter(e -> e.getAge() > 24);
            System.out.println(optional2.get());
        }
    
        @Test
        public void test2(){
            Man man = new Man();
    //        String godnessName = getGodnessName(man);
         String godnessName = getGodnessName2(Optional.ofNullable(null));
            System.out.println(godnessName);
        }
    
        public String getGodnessName(Man man){
            //空指针异常 return man.getGodness().getName();
    
            //传统if嵌套过深
            if(man != null){
                Godness godness = man.getGodness();
                if(godness != null){
                    return godness.getName();
                }
            }
            return "测试";
        }
    
        public String getGodnessName2(Optional<NewMan> man){
            //Optional
            return man.orElse(new NewMan())
                       .getGodness()
                       .orElse(new Godness("测试"))
                       .getName();
        }
    }
    

6.接口中的默认方法与静态方法

  • Java8中允许接口中包含具有具体实现的方法,该方法称为默认方法,默认方法使用default关键字修饰

接口默认方法的类优先原则

  • 若一个接口定义了一个默认方法,而另外一个父类或接口又定义了一个同名的方法时
    • 1.选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略
    • 2.接口冲突。如果一个父接口提供一个默认方法,而另一个父接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么实现类必须选择一个接口或者覆盖该方法来解决冲突
    package dao;
    
    public interface MyFun1 {
    
        default String getName(){
            return "myFun1";
        }
    
    	static void show(){
            System.out.println("接口中的静态方法");
        }
    }
    
    package dao;
    
    public interface MyFun2 {
    
        default String getName(){
            return "myFun2";
        }
    }
    
    package domains;
    
    public class MyClassFun {
    
        public String getName(){
            return "MyClassFun";
        }
    }
    
    package domains;
    
    import dao.MyFun1;
    import dao.MyFun2;
    
    //public class SubClass extends MyClassFun implements MyFun1 {
    //}
    
    public class SubClass implements MyFun1, >	MyFun2 {
        @Override
        public String getName() {
            return MyFun1.super.getName();
        }
    }
    
    import domains.SubClass;
    import org.junit.Test;
    
    public class TestDefault {
    
        @Test
        public void test1(){
            SubClass subClass = new SubClass();
            String name = subClass.getName();
            System.out.println(name);
    
    		MyFun1.show()
        }
    }
    

7.传统时间格式化的线程安全问题

  • java.util.Date() JDK1.0版本
  • java.util.Calender() JDK1.1版本
  • java.util.TimeZone()
  • java.text.SimpleDateFormat
  • 声明不规范,不安全

8.新时间与日期API

  • java.time包下
  • java.time.
  • java.time.format
  • java.time.temporal
  • java.time.zone
  • 不可变,会产生新的实例
  • 线程安全

9.重复注解与类型注解


http://www.kler.cn/a/582609.html

相关文章:

  • 手机端安装包签名原理
  • C++中数组作为函数参数传递,和使用指针作为函数参数有什么区别?
  • Webpack 打包详细教程
  • 推理模型对SQL理解能力的评测:DeepSeek r1、GPT-4o、Kimi k1.5和Claude 3.7 Sonnet
  • ELK traceId实现跨服务日志追踪
  • 传球游戏(蓝桥云课)
  • 附下载 | 2024 OWASP Top 10 基础设施安全风险.pdf
  • 【Godot4.0】EasyClock时钟组件
  • upload-labs-master通关攻略(9~12)
  • CentOS 7.9安装OpenStack
  • Python和Docker实现AWS ECR/ECS上全自动容器化部署网站前端
  • Sass (Scss) 与 Less 的区别与选择
  • RAGFlow版本升级-Win10系统Docker
  • 淘晶驰 屏幕 应用 之 esp8266/arduino 简约时钟 2025/3/12
  • Python:函数式编程
  • 图神经网络学习笔记—纯 PyTorch 中的多 GPU 训练(专题十二)
  • 095:vue+cesium 使用Cesium3DTileset加载3D瓦片数据
  • 使用netlify部署github的vue/react项目或本地的dist,国内也可以正常访问
  • Deepseek -> 如何在PyTorch中合并张量
  • K8S学习之基础二十五:k8s存储类之storageclass