• Java8-新特性


    Lambda表达式

    为什么使用Lambda表达式?

    Lambda 是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码。可以写出更简洁、高效的代码。
     

    Lambda初体验

    我们先来看一段匿名内部类的代码 使用Lambda表达式后的样子

    		//匿名内部类
    		Comparator<Integer> com = new Comparator<Integer>() {
    			@Override
    			public int compare(Integer o1, Integer o2) {
    				return Integer.compare(o1, o2);
    			}
    		};
    		
    		TreeSet<Integer> treeSet = new TreeSet<>(com);
    
    		//使用了Lambda式后的匿名内部类
    		Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
    		TreeSet<Integer> treeSet = new TreeSet<>(com);
    

     
    准备工作: 创建一个Employee 实体类

    public class Employee {
    	
    	private String name;
    	private Integer age;
    	private Double salary;
    	private Status status;
    	
    	//getsetequalshashcode方法
    	`````````````
    	`````````````
    	public enum Status{
    		FREE,BUSY,VACATION;
    	}
    	
    	
    }
    

    接下来假设我们有一个需求: 获取公司员工年龄大于等于35的员工信息

    那么我们可能需要这么干

    public class TestLambda {
    	
    	List<Employee> list = Arrays.asList(
    			new Employee("张三", 18, 9999.99),
    			new Employee("李四", 38, 5555.99),
    			new Employee("王五", 50, 6666.99),
    			new Employee("赵六", 16, 3333.33),
    			new Employee("田七", 8, 7777.77)
    	);
    	//传统方式
    	@Test
    	public void test1(){
    		List<Employee> empList = filterEmployeeByAge(list);
    		
    		for (Employee e:empList) {
    			System.out.println(e);
    		}
    		
    	}
    	
    	public List<Employee> filterEmployeeByAge(List<Employee> list){
    		List<Employee> emps = new ArrayList<>();
    		
    		for(Employee e:list){
    			if(e != null && e.getAge() >= 35)
    				emps.add(e);
    		}
    		return emps;
    	}
    	
    }
    

    然后来了第二个需求:获取工资大于的5000的员工,而此时原来的方法可能被别的方法引用着

    所以我们又这么干,新加了一个方法满足第二个需求

    public List<Employee> filterEmployeeBySalary(List<Employee> list){
    		List<Employee> emps = new ArrayList<>();
    		
    		for(Employee e:list){
    			if(e != null && e.getSalary() >= 5000)
    				emps.add(e);
    		}
    		return emps;
    }
    

    那么现在有一个问题,如果接下来有第三、第四或第五个需求,我们需要创建第三、第四、第五个方法么

    这好像是一件十分愚蠢的事

    所以接下来我们使用策略模式来做一次优化 , 那这跟Lambda表达式有半毛钱关系么,别急,耐心看下去

    public interface MyPredicate<T> {
    	
    	public boolean test(T t);
    	
    }
    public class FilterEmployeeByAge implements MyPredicate<Employee>{
    
    	@Override
    	public boolean test(Employee t) {
    		return t == null ? false:t.getAge() >= 35;
    	}
    	
    }
    public class TestLambda {
    	
    	List<Employee> list = Arrays.asList(
    			new Employee("张三", 18, 9999.99),
    			new Employee("李四", 38, 5555.99),
    			new Employee("王五", 50, 6666.99),
    			new Employee("赵六", 16, 3333.33),
    			new Employee("田七", 8, 7777.77)
    	);
    	
    	@Test
    	public void test2(){
    		List<Employee> empsList = filterEmployee(list,new FilterEmployeeByAge());
    		for(Employee e:empsList){
    			System.out.println(e);
    		}
    	}
    	
    	//优化方式一:策略设计模式
    	public List<Employee> filterEmployee(List<Employee> list,MyPredicate<Employee> mp){
    		List<Employee> emps = new ArrayList<>();
    		
    		for (Employee e:list) {
    			if(mp.test(e)){
    				emps.add(e);
    			}
    		}
    		return emps;
    	}
    	
    	
    }
    

     
    不知道大家看明白了没有,如果我有新的需求,我只需要去改变,我的实现接口就可以了,无需创建新的方法

    但是有人可能会说,好麻烦呀,我每次想用一次filterEmployee方法去完成不同的需求,我就务必要创建一个新的接口实现类,而且实现类中,还就一个方法,一点也不可爱

    好的,接下来我们这么干,使用匿名内部类

    	@Test
    	public void test3(){
            //匿名内部类
    		List<Employee> empsList = filterEmployee(list,new MyPredicate<Employee>() {
    			@Override
    			public boolean test(Employee t) {
    				return t == null ? false:t.getSalary() < 5000 ;
    			}
    		});
    		
    		for(Employee em:empsList){
    			System.out.println(em);
    		}
    
    	}
    	
    	//优化方式二:策略设计模式
    	public List<Employee> filterEmployee(List<Employee> list,MyPredicate<Employee> mp){
    		List<Employee> emps = new ArrayList<>();
    		
    		for (Employee e:list) {
    			if(mp.test(e)){
    				emps.add(e);
    			}
    		}
    		return emps;
    	}
    

     
    接下来大家应该想到了点什么,匿名内部类 到 Lambda

    	@Test
    	public void test4(){
    		List<Employee> empsList = filterEmployee(list,(e) -> e == null ? false:e.getSalary() < 5000 );
    		empsList.forEach(System.out::println);
    
    	}
    

    Lambda表达式的基础语法

    Lambda表达式的基础语法: Java8中引入了一个新的操作符 "—>" 该操作符称为箭头操作符 或 Lambda操作符,箭头操作符将Lambda表达式 拆分成两个部分

    左侧:Lambda表达式的参数列表

    右侧:Lambda表达式中所需执行的功能 即Lambda体
     

    语法格式一:无参数,无返回值

    ​ Runnable ru = () -> System.out.println("实现Runnable的接口的Lambda");

    ​ ru.run();
     
    语法格式二:有一个参数,且无返回值

    ​ Consumer co = (x) -> System.out.println(x);

    ​ co.accept("呵呵");
     
    语法格式三:若只有一个参数,小括号可以使用不写

    ​ x -> System.out.println(x);
     
    语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句

    ​ Comparator com = (x,y) -> {
    ​  System.out.println("函数式接口");
    ​  return Integer.compare(x,y);
    ​ };
     
    语法格式五:Lambda体中只有一条语句,return和大括号可以省略不写

    ​ Comparator com = (x,y) -> Integer.compare(x,y);
     
    语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM可以推断出,数据类型,即类型推断

    ​ Comparator com = (Integer x,Integer y) -> Integer.compare(x,y);

    ​ 注:数据类型要么全写 要么全不写

    Lambda 表达式需要"函数式的接口"的支持

    函数式接口:接口中只有一个抽象方法的接口,成为函数式接口,可以使用注解 @FunctionalInterface修饰

    可以检查是否是函数式接口
     
    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 TestLambda3 {
    
    	// Consumer<T> 消费型接口
    	@Test
    	public void test1() {
    		happy(10000, x -> System.out.println("我今天花了" + x + "元"));
    	}
    
    	public void happy(double money, Consumer<Double> con) {
    		con.accept(money);
    	}
    
    	// Supplier<T> 供给型接口
    	@Test
    	public void test2() {
    		List<Integer> numList = getNumList(10,() -> (int)(100*Math.random()));
    		for(Integer i:numList){
    			System.out.println(i);
    		}
    	}
    
    	// 需求:产生指定个数的整数 放到集合中
    	public List<Integer> getNumList(int num, Supplier<Integer> sup) {
    		List<Integer> list = new ArrayList<>();
    
    		for (int i = 0; i < num; i++) {
    			Integer e = sup.get();
    			list.add(e);
    		}
    
    		return list;
    	}
    	
    	//Functional<T> 函数式接口
    	@Test
    	public void test3(){
    		String string = strHandler(" 我前面后面都有空格额  ",(str) -> str.trim());
    		System.out.println(string);
    	}
    	
    	//需求:用于处理字符串
    	public String strHandler(String str,Function<String, String> fuc){
    		return fuc.apply(str);
    	}
    	
    	//Predicate<T> 断言型接口
    	@Test
    	public void test4(){
    		List<String> list = Arrays.asList("Hello","hi","Apple","zoo","beautiful");
    		List<String> result = filterStr(list, (str) -> str.length() > 3);
    		for(String s:result){
    			System.out.println(s);
    		}
    	}
    	//需求:将满足条件的字符串放入集合
    	public List<String> filterStr(List<String> list,Predicate<String> pre){
    		List<String> strList = new ArrayList<>();
    		
    		for(String str:list){
    			if(pre.test(str)){
    				strList.add(str);
    			}
    		}
    		return strList;
    	}
    	
    }
    

     
    当然还有其他函数式接口

    Lambda体中的方法引用与构造器引用

    (一)、方法引用
    若Lambda 体中的内容有方法已经实现了,我们可以使用 "方法引用",(可以理解方法引用为Lambda的另一种表现形式)
    主要有三种语法格式:

    对象 : : 实例方法名

    类 : : 静态方法名

    类 : : 实例方法名

    注意:

    ①、Lambda 体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致

    ②、若 Lambda 参数列表中的第一个参数是 实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName : : methodName

     
    (二)、构造器引用

    格式:ClassName : : New

    注意:需要 调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致!
     
    (三)、数组引用

    格式:Type[ ]::new
     

    public class TestMethodRef {
    	
    	//对象::实例方法名
    	@Test
    	public void test1(){
    		Consumer<String> con = (x) -> System.out.println(x);
    		
    		PrintStream ps = System.out;
    		Consumer<String> con1 = ps::println;
    		
    		Consumer<String> con2 = System.out::println;
    		con2.accept("abcd");
    
    	}
    	
    	//对象::实例方法名
    	@Test
    	public void test2(){
    		Employee emp = new Employee();
    		Supplier<String> sup = () -> emp.getName();
    		
    		Supplier<String> sup1 = emp::getName;
    	}
    	
    	//类::静态方法名
    	@Test
    	public void test3(){
    		Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
    		
    		Comparator<Integer> com1 = Integer::compare;
    	}
    	
    	//类::实例方法名
    	@Test
    	public void test4(){
    		BiPredicate<String, String> bp = (x,y) -> x.equals(y);
    		
    		BiPredicate<String, String> bp1 = String::equals;
    	}
    	
    	//构造器引用
    	@Test
    	public void test5(){
    		Supplier<Employee> sup = () -> new Employee();
    		//构造器引用方式  调用哪个构造器取决于 接口的参数类型
    		Supplier<Employee> sup2 = Employee::new;
    		Employee employee = sup2.get();
    		System.out.println(employee);
    	}
    	
    	//数组引用
    	@Test
    	public void test6(){
    		Function<Integer, String[]> fun = (x) -> new String[x]; 
    		String[] strs = fun.apply(10);
    		System.out.println(strs.length);
    		
    		Function<Integer, String[]> fun2 = String[]::new;
    		String[] apply = fun2.apply(20);
    		System.out.println(apply.length);
    	}
    }
    

    Stream API

    简介:

    Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。

    Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

    使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式

     
    流(Stream) 到底是什么?

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

    “集合讲的是数据,流讲的是计算!”

    注意:

    ①、Stream 自己不会存储元素

    ②、Stream 不会改变源对象。相反它们会返回一个持有对象的结果

    ③、Stream 操作是延迟执行的,这意味着它们会等到需要结果的时候才会执行

    Stream的三个操作步骤

    1. 创建 Stream

    2. 中间操作

    3. 终止操作

    创建Stream

    public class TestStreamAPI {
    	
    	//创建Stream
    	@Test
    	public void test1(){
    		//1.可以通过 Collection 系列集合提供的stream() 或 parallelStream()
    		List<String> list = new ArrayList<>();
    		Stream<String> stream1 = list.stream();
    		
    		//2.通过 Arrays 中的静态方法 stream() 获取数组流
    		Employee[] emps = new Employee[10];
    		Stream<Employee> stream2 = Arrays.stream(emps);
    		
    		//3.通过 Stream 类中的静态方法 of()
    		Stream<String> stream3 = Stream.of("aa","bb","cc");
    		
    		//4.创建无限流
    		//迭代
    		Stream<Integer> stream4 = Stream.iterate(0, (x) -> x+2);
    		//stream4.forEach(System.out::println);
    		
    		//生成
    		Stream.generate(() -> Math.random()).limit(5).forEach(System.out::println);
    	}
    	
    }
    

    中间操作

    public class TestStreamAPI {
    	
    	
    	List<Employee> employees = Arrays.asList(
    			new Employee("张三", 18, 9999.99),
    			new Employee("李四", 38, 5555.99),
    			new Employee("王五", 50, 6666.99),
    			new Employee("王五", 50, 6666.99),
    			new Employee("王五", 50, 6666.99),
    			new Employee("赵六", 16, 3333.33),
    			new Employee("田七", 8, 7777.77)
    	);
    	
    	/**
    	 * 筛选与切片
    	 * filter-接收 Lambda, 从流中排除某些元素。
    	 * limit-截断流,使其元素不超过给定数量
    	 * skip(n) - 跳过元素,返回了一个扔掉了前 n 个元素的流.若流中元素不足n个,则返回一个空流.与limit互补
    	 * distinct-筛选,跳过流所生成元素的 hashCode() 和 equals() 去除重复元素
    	 * 		   -此方法务必需要重写equals 和 hashcode 方法
    	 */
    	@Test
    	public void test2(){
    		//中间操作:不会执行任何操作
    		Stream<Employee> stream = employees.stream().filter((e) -> {
    			System.out.println("Stream API的中间操作");
    			return e.getAge() > 20;
    		});
    		//终止操作:一次执行全部内容,即"惰性求值"
    		stream.forEach(System.out::println);
    	}
    	
    	@Test
    	public void test3(){
    		employees.stream().filter((e) -> {
    			System.out.println("短路");
    			return e.getSalary() > 3000;
    		}).limit(2).forEach(System.out::println);
    	}
    	
    	@Test
    	public void test4(){
    		employees.stream().filter(e -> e.getSalary() >3000).skip(2).forEach(System.out::print);
    	}
    	
    	@Test
    	public void test5(){
    		employees.stream().filter((e) -> e.getSalary() > 	          5000).skip(2).distinct().forEach(System.out::println);
    	}
    	
    }
    
    
    public class TestStreamAPI2 {
    	
    	List<Employee> employees = Arrays.asList(
    			new Employee("张三", 18, 9999.99),
    			new Employee("李四", 38, 5555.99),
    			new Employee("王五", 50, 6666.99),
    			new Employee("王五", 50, 6666.99),
    			new Employee("王五", 50, 6666.99),
    			new Employee("赵六", 16, 3333.33),
    			new Employee("田七", 8, 7777.77)
    	);
    	
    	/**
    	 * 映射:
    	 * map - 接收Lambda,将元素转换成 其他形式或提取信息.接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
    	 * flatMap - 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流,连接成一个新流
    	 */
    	@Test
    	public void test2(){
    		List<String> list = Arrays.asList("aa","bb","cc","dd","ee");
    		
    		// list.stream().map((str) -> str.toUpperCase()).forEach(System.out::println);
    		
    		employees.stream().map(e -> e.getName()).forEach(System.out::println);
    		System.out.println("--------------------------------------");
    		
    		Stream<Stream<Character>> stream = list.stream().map(TestStreamAPI2::filterCharacter);
    		stream.forEach(sm -> sm.forEach(System.out::print));
    		System.out.println("
    --------------------------------------");
    		
    		Stream<Character> stream2 = list.stream().flatMap(TestStreamAPI2::filterCharacter);
    		stream2.forEach(System.out::print);
    	}
    	
    	public static Stream<Character> filterCharacter(String str){
    		List<Character> list = new ArrayList<>();
    		
    		for(Character ch:str.toCharArray()){
    			list.add(ch);
    		}
    		
    		return list.stream();
    	}
    	
    	/**
    	 * 排序
    	 * sorted-自然排序(Comparable)
    	 * sorted(Comparable com)-定制排序(Comparator)
    	 */
    	@Test
    	public void test3(){
    		List<String> list = Arrays.asList("ccc","aaa","bbb","ddd");
    		list.stream().sorted().forEach(System.out::println);
    		
    		System.out.println("----------------------------");
    		
    		employees.stream().sorted((e1,e2) -> {
    			if(e1.getAge().equals(e2.getAge())){
    				return e1.getSalary().compareTo(e2.getSalary());
    			}else{
    				return e1.getAge().compareTo(e2.getAge());
    			}
    		}).forEach(System.out::println);;
    	}
    	
    }
    
    

    终止操作

    /**
     * 
     * 终止操作
     *
     */
    public class TestStreamAPI3 {
    	
    	List<Employee> employees = Arrays.asList(
    			new Employee("张三", 18, 9999.99,Status.BUSY),
    			new Employee("李四", 38, 5555.99,Status.FREE),
    			new Employee("王五", 50, 6666.99,Status.VACATION),
    			new Employee("王五", 50, 6666.99,Status.BUSY),
    			new Employee("王五", 50, 6666.99,Status.BUSY),
    			new Employee("赵六", 16, 3333.33,Status.FREE),
    			new Employee("田七", 8, 7777.77,Status.VACATION)
    	);
    	
    	/**
    	 * 查找与匹配
    	 * allMatch-检查是否匹配所有元素
    	 * anyMatch-检查是否至少匹配一个元素
    	 * noneMatch-检查是否没有匹配元素
    	 * findFirst-返回第一个元素
    	 * findAny-返回当前流中的任意元素
    	 * count-返回流中元素的总个数
    	 * max-返回流中最大值
    	 * min-返回流中最小值
    	 */
    	@Test
    	public void test1(){
    		//检查所有员工是否 都为忙碌状态
    		boolean allMatch = employees.stream().allMatch(e -> e.getStatus().equals(Status.BUSY));
    		System.out.println(allMatch);
    		//检查是否有一个员工为忙碌状态
    		boolean anyMatch = employees.stream().anyMatch(e -> e.getStatus().equals(Status.BUSY));
    		System.out.println(anyMatch);
    		//检查是否 没用员工处于忙碌状态
    		boolean noneMatch = employees.stream().noneMatch(e -> e.getStatus().equals(Status.BUSY));
    		System.out.println(noneMatch);
    		//查询 工资排在首位的 员工
    		Optional<Employee> findFirst = employees.stream().sorted((e1,e2) -> -Double.compare(e1.getSalary(), e2.getSalary())).findFirst();
    		System.out.println(findFirst.get());
    		//查找任意 一个处于空闲状态的员工
    		Optional<Employee> findAny = employees.parallelStream().filter(e -> e.getStatus().equals(Status.FREE)).findAny();
    		System.out.println(findAny.get());
    		
    		//查询员工的个数
    		long count = employees.stream().count();
    		System.out.println(count);
    		//查询 拿最大工资的员工
    		Optional<Employee> max = employees.stream().max((e1,e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
    		System.out.println(max.get());
    		
    		//查询最小的工资数
    		Optional<Double> minSalary = employees.stream().map(Employee::getSalary).min(Double::compare);
    		System.out.println(minSalary);
    	}
    /**
    	 * 归约: reduce(T identity, BinaryOperator)/reduce(BinaryOperator)
    	 * -可以将流中元素反复结合起来得到一个值
    	 */
    	@Test
    	public void test2() {
    		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
    		// 有起始值 所有不可能为空 故返回值为Integer
    		Integer sum = list.stream().reduce(0, (x, y) -> (x + y));
    		System.out.println(sum);
    
    		System.out.println("------------------------");
    		Optional<Double> salarySum = employees.stream().map(Employee::getSalary).reduce(Double::sum);
    		System.out.println(salarySum.get());
    	}
    
    	/**
    	 * 收集 collect-将流转换成其他形式,接收一个Collector接口的实现,用于给Stream中元素作汇总的方法
    	 */
    	@Test
    	public void test3() {
    		List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
    		list.forEach(System.out::println);
    
    		System.out.println("------------------------");
    		Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
    
    		System.out.println("------------------------");
    		HashSet<String> hashSet = employees.stream().map(Employee::getName)
    				.collect(Collectors.toCollection(HashSet::new));
    		hashSet.forEach(System.out::print);
    	}
    	
    	@Test
    	public void test4(){
    		//总数
    		Long count = employees.stream().collect(Collectors.counting());
    		System.out.println(count);
    		
    		System.out.println("-------------------------------");
    		//平均值
    		Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
    		System.out.println(avg);
    		//总和
    		Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
    		System.out.println(sum);
    		//最大值
    		Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
    		System.out.println(max.get());
    		//最小值
    		Optional<Double> min = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));
    		System.out.println(min.get());
    	}
    	
    	//分组
    	@Test
    	public void test5(){
    		//单级分组
    		Map<Status, List<Employee>> map = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
    		System.out.println(map);
    		//多级分组
    		Map<Status, Map<String, List<Employee>>> map2 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus,Collectors.groupingBy(e -> {
    			if(((Employee)e).getAge() < 18){
    				return "未成年";
    			}else{
    				return "成年";
    			}
    		})));
    		System.out.println(map2);
    	}
    	//分区
    	@Test
    	public void test6(){
    		Map<Boolean, List<Employee>> part = employees.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 8000));
    		System.out.println(part);
    	}
    	
    	@Test
    	public void test7(){
    		DoubleSummaryStatistics doubleSummaryStatistics = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
    		System.out.println(doubleSummaryStatistics.getSum());
    		System.out.println(doubleSummaryStatistics.getAverage());
    		System.out.println(doubleSummaryStatistics.getMax());
    	}
    	
    }
    
  • 相关阅读:
    cssReset
    CSS的一些小技巧
    前端图标神器
    单例模式
    CSS 控制Html页面高度导致抖动问题的原因
    PHP中include()与require()的区别说明
    extends和implements区别
    静态,抽象类、接口、类库
    jQuery轮播图(手动点击轮播)
    jQuery实现大图轮播
  • 原文地址:https://www.cnblogs.com/dwlovelife/p/10136699.html
Copyright © 2020-2023  润新知