天天看点

Java8新技术一.接口默认方法二.函数式接口三.Lambda表达式和方法引用四.Stream

摘要: 最近学习了一下java8的一些技术,做了一下简单的整理,同时做了一个组内简单的分享和讨论,下一步打算在项目中真正的使用一下,真正体会到java8在工程编码上给我们带来的方便和好处。。。 新特性的优势 速度更快 底层的数据结构调整、JVM永久代的去除、以及Stream并行(fork join框架) 代码更少 (Lambda表达式) 强大的Stream API 便于并行 Optional避免空指针

首先盗一张图,这张图上面列出了java8所体现的全部优点:

Java8新技术一.接口默认方法二.函数式接口三.Lambda表达式和方法引用四.Stream

本次梳理的知识点大概有默认方法、函数式接口、Lambda表达式和方法引用、Stream API

一.接口默认方法

1.什么是接口默认方法?

顾名思义:默认方法就是接口的默认的方法。

2.用法

@FunctionalInterface
public interface MyInterface {

     Integer handler(Integer t);

     default Integer handler2(Integer t) {
          return null;
     }

     default Integer handler3(Integer t) {
          return null;
     }
}                

3.优势

1).默认方法的主要优势是提供一种拓展接口的方法,而不破坏现有代码。加入我们有一个已经投入使用接口需要拓展一个新的方法,在JDK8以前,如果为一个使用的接口增加一个新方法,则我们必须在所有实现类中添加该方法的实现,否则编译会出现异常。如果实现类数量少并且我们有权限修改,可能会工作量相对较少。如果实现类比较多或者我们没有权限修改实现类源代码,这样可能就比较麻烦。而默认方法则解决了这个问题,它提供了一个实现,当没有显示提供其他实现时就采用这个实现。这样新添加的方法将不会破坏现有代码。

2).默认方法的另一个优势是该方法是可选的,子类可以根据不同的需求Override默认实现。例如,我们定义一个集合接口,其中有增、删、改等操作。如果我们的实现类90%都是以数组保存数据,那么我们可以定义针对这些方法给出默认实现,而对于其他非数组集合或者有其他类似业务,可以选择性复写接口中默认方法。

4.注意

实现类里面调用接口的默认方法不能访问实现类的成员变量,这个是需要注意的。

二.函数式接口

1.什么是函数式接口?

所谓的函数式接口就是只有一个抽象方法的接口。

2.栗子:

普通的接口

public interface MyInterface {
     Integer handler(Integer t);

     Integer handler1(Integer t);

     default Integer handler2(Integer t) {
          return null;
     }

     default Integer handler3(Integer t) {
          return null;
     }
}                

函数式接口

@FunctionalInterface
public interface MyInterface {

     Integer handler(Integer t);

     default Integer handler2(Integer t) {
          return null;
     }

     default Integer handler3(Integer t) {
          return null;
     }
}                

3.作用:

为Lambda表达式服务,是Lambda表达式各种实现的抽象方法。

三.Lambda表达式和方法引用

有了函数式接口之后,就可以使用Lambda表达式和方法引用了。其实函数式接口的表中的函数描述符就是Lambda表达式,在函数式接口中Lambda表达式相当于匿名内部类的效果.

1.栗子:

@Test
public void test(){
    //Jdk8之前
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("t thread start...");
        }
    });
    t.start();

    //Jdk8之后
    Thread t1 = new Thread(() -> System.out.println("t1 thread start..."));
    t1.start();
}                

2.语法:

Java8新技术一.接口默认方法二.函数式接口三.Lambda表达式和方法引用四.Stream

3.使用:

List<Employee> emps = Arrays.asList(
			new Employee(, "张三", , ),
			new Employee(, "李四", , ),
			new Employee(, "王五", , ),
			new Employee(, "赵六", , ),
			new Employee(, "田七", , )
	);

	//需求:获取公司中年龄小于 35 的员工信息
	public List<Employee> filterEmployeeAge(List<Employee> emps){
		List<Employee> list = new ArrayList<>();
		
		for (Employee emp : emps) {
			if(emp.getAge() <= ){
				list.add(emp);
			}
		}
		return list;
	}
	
	@Test
	public void test3(){
		List<Employee> list = filterEmployeeAge(emps);
		
		for (Employee employee : list) {
			System.out.println(employee);
		}
	}
	
	//需求:获取公司中工资大于 5000 的员工信息
	public List<Employee> filterEmployeeSalary(List<Employee> emps){
		List<Employee> list = new ArrayList<>();
		
		for (Employee emp : emps) {
			if(emp.getSalary() >= ){
				list.add(emp);
			}
		}
		return list;
	}

    @Test
	public void test4(){
		List<Employee> list = filterEmployeeSalary(emps);
		
		for (Employee employee : list) {
			System.out.println(employee);
		}
	}

    //优化方式一:策略设计模式
	public List<Employee> filterEmployee(List<Employee> emps, MyPredicate<Employee> mp){
		List<Employee> list = new ArrayList<>();
		
		for (Employee employee : emps) {
			if(mp.test(employee)){
				list.add(employee);
			}
		}
		
		return list;
	}
	
	@Test
	public void test5(){
		List<Employee> list = filterEmployee(emps, new FilterEmployeeForAge());
		for (Employee employee : list) {
			System.out.println(employee);
		}
		
		System.out.println("------------------------------------------");
		
		List<Employee> list2 = filterEmployee(emps, new FilterEmployeeForSalary());
		for (Employee employee : list2) {
			System.out.println(employee);
		}
	}
	
	//优化方式二:匿名内部类
	@Test
	public void test6(){
		List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() {
			@Override
			public boolean test(Employee t) {
				return t.getId() <= ;
			}
		});
		
		for (Employee employee : list) {
			System.out.println(employee);
		}
	}
	
	//优化方式三:Lambda 表达式
	@Test
	public void test7(){
		List<Employee> list = filterEmployee(emps, (e) -> e.getAge() <= );
		list.forEach(System.out::println);
		
		System.out.println("------------------------------------------");
		
		List<Employee> list2 = filterEmployee(emps, (e) -> e.getSalary() >= );
		list2.forEach(System.out::println);
	}
	
	//优化方式四:Stream API
	@Test
	public void test8(){
		emps.stream()
			.filter((e) -> e.getAge() <= )
			.forEach(System.out::println);
		
		System.out.println("----------------------------------------------");
		
		emps.stream()
			.map(Employee::getName)
			.limit()
			.sorted()
			.forEach(System.out::println);
	}
	                

4.四大内置函数式接口的支持

java.util.function

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

5.方法引用

我们要表达的lambda表达式已经有方法实现了,我们就直接调用,调用的方式就叫做方法引用,方法引用也是一种lambda的表现形式。

方法引用的标准形式是:

类名::方法名

。(注意:只需要写方法名,不需要写括号)

有以下四种形式的方法引用:

类型示例

引用静态方法 ContainingClass::staticMethodName
引用某个对象的实例方法 containingObject::instanceMethodName
引用某个类型的任意对象的实例方法 ContainingType::methodName
引用构造方法 ClassName::new

引用对象实例方法

//对象的引用 :: 实例方法名
	@Test
	public void test2(){
		Employee emp = new Employee(, "张三", , );
		
		Supplier<String> sup = () -> emp.getName();
		System.out.println(sup.get());
		
		System.out.println("----------------------------------");
		
		Supplier<String> sup2 = emp::getName;
		System.out.println(sup2.get());
	}
           

引用的方法的参数列表和返回值要和替换的lambda表达式的参数列表和返回值保持一直。

引用类的实例方法

//类名 :: 实例方法名
	@Test
	public void test5(){
		BiPredicate<String, String> bp = (x, y) -> x.equals(y);
		System.out.println(bp.test("abcde", "abcde"));
		
		System.out.println("-----------------------------------------");
		
		BiPredicate<String, String> bp2 = String::equals;
		System.out.println(bp2.test("abc", "abc"));
		
		System.out.println("-----------------------------------------");
		
		
		Function<Employee, String> fun = (e) -> e.show();
		System.out.println(fun.apply(new Employee()));
		
		System.out.println("-----------------------------------------");
		
		Function<Employee, String> fun2 = Employee::show;
		System.out.println(fun2.apply(new Employee()));
	
	}
           

lambda表达式的第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,才可以使用 ClassName::method

四.Stream

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

1.与集合的关系

可以简单理解为集合是描述的数据,stream表示的如何操作数据的计算方式。 slogen:不生产数据,只是数据的搬运工~ 

2.使用(把大象装冰箱分三步~)

  • 1.创建Stream 

    一个数据源(如:集合、数组),获取一个流

  • 2.中间操作 

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

  • 3.终止操作(终端操作) 

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

3.创建

@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[];
    Stream<Employee> stream2=Arrays.stream(emps);
 
    //3.通过Stream 类中的静态方法of()
    Stream<String> stream3=Stream.of("aa","bb","cc");
 
    //4.创建无限流
    //迭代
    Stream<Integer> stream4=Stream.iterate(, (x) -> x+);
    stream4.limit().forEach(System.out::println);
 
    //生成
    Stream.generate(() -> Math.random())
          .limit()
          .forEach(System.out::println);
}                

4.中间操作

4.1.筛选与切片

//中间操作
List<Employee> employees=Arrays.asList(
        new Employee("张三",,),
        new Employee("李四",,),
        new Employee("王五",,),
        new Employee("赵六",,),
        new Employee("田七",,),
        new Employee("田七",,)
        );
 
/*  筛选与切片
 *  filter--接收Lambda,从流中排除某些元素。
 *  limit--截断流,使其元素不超过给定数量。
 *  skip(n)--跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n) 互补
 *  distinct--筛选,通过流所生成元素的 hashCode() 和 equals() 去掉重复元素
 */
 
//内部迭代:迭代操作由 Stream API 完成
@Test
public void test1(){
    //中间操作:不会执行任何操作
    Stream<Employee> stream=employees.stream()
                            .filter((e) -> e.getAge()> );
    //终止操作:一次性执行全部内容,即 惰性求值
    stream.forEach(System.out::println);
}
//外部迭代
@Test
public void test2(){
    Iterator<Employee> it=employees.iterator();
    while(it.hasNext()){
        System.out.println(it.next());
    }
}
 
@Test
public void test3(){//发现“短路”只输出了两次,说明只要找到 2 个 符合条件的就不再继续迭代
    employees.stream()
             .filter((e)->{
                 System.out.println("短路!");
                 return e.getSalary()>;
             })
             .limit()
             .forEach(System.out::println);
}
 
@Test
public void test4(){
    employees.stream()
             .filter((e)->e.getSalary()>)
             .skip()//跳过前两个
             .distinct()//去重,注意:需要Employee重写hashCode 和 equals 方法
             .forEach(System.out::println);
}                

4.2.映射

//中间操作
/*
 * 映射
 * map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新元素。
 * flatMap--接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
 */
@Test
public void test5(){
    List<String> list=Arrays.asList("aaa","bbb","ccc","ddd");
    list.stream()
         .map((str)->str.toUpperCase())
         .forEach(System.out::println);
 
    System.out.println("------------------------");
 
    employees.stream()
             .map(Employee::getName)
             .forEach(System.out::println);
 
    System.out.println("------------------------");
 
    Stream<Stream<Character>> stream=list.stream()
                                         .map(TestStreamAPI2::filterChatacter);
    stream.forEach((sm)->{
        sm.forEach(System.out::println);
    });
 
    System.out.println("------------------------");
 
    Stream<Character> sm=list.stream()
                             .flatMap(TestStreamAPI2::filterChatacter);
    sm.forEach(System.out::println);
}
 
public static Stream<Character> filterChatacter(String str){
    List<Character> list=new ArrayList<>();
    for (Character ch : str.toCharArray()) {
        list.add(ch);
    }
    return list.stream();
}
 
@Test
public void test6(){//map和flatMap的关系  类似于 add(Object)和addAll(Collection coll)
    List<String> list=Arrays.asList("aaa","bbb","ccc","ddd");
    List list2=new ArrayList<>();
    list2.add();
    list2.add();
    list2.addAll(list);
    System.out.println(list2);
}                

4.3.排序

//中间操作
/*
 * 排序
 * sorted()-自然排序(按照对象类实现Comparable接口的compareTo()方法 排序)
 * sorted(Comparator com)-定制排序(Comparator)
 */
@Test
public void test7(){
    List<String> list=Arrays.asList("ccc","bbb","aaa");
    list.stream()
        .sorted()
        .forEach(System.out::println);
 
    System.out.println("------------------------");
 
    employees.stream()
             .sorted((e1,e2)->{
                 if(e1.getAge().equals(e2.getAge())){
                     return e1.getName().compareTo(e2.getName());
                 }else{
                     return e1.getAge().compareTo(e2.getAge());
                 }
             }).forEach(System.out::println);
}                

5.终止操作

5.1查找与匹配 

List<Employee> employees=Arrays.asList(
            new Employee("张三",,,Status.FREE),
            new Employee("李四",,,Status.BUSY),
            new Employee("王五",,,Status.VOCATION),
            new Employee("赵六",,,Status.FREE),
            new Employee("田七",,,Status.BUSY)
            );
    /*
     * 查找与匹配
     *
     */
 
    @Test
    public void test1(){
        boolean b1=employees.stream()//allMatch-检查是否匹配所有元素
                            .allMatch((e)->e.getStatus().equals(Status.BUSY));
        System.out.println(b1);//false
 
        boolean b2=employees.stream()//anyMatch-检查是否至少匹配一个元素
                            .anyMatch((e)->e.getStatus().equals(Status.BUSY));
        System.out.println(b2);//true
 
        boolean b3=employees.stream()//noneMatch-检查是否没有匹配所有元素
                            .noneMatch((e)->e.getStatus().equals(Status.BUSY));
        System.out.println(b3);//false
 
        Optional<Employee> op=employees.stream()//findFirst-返回第一个元素//Optional是Java8中避免空指针异常的容器类
                 .sorted((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()))
                 .findFirst();
        System.out.println(op.get());//Employee [name=王五, age=26, salary=3333.33, Status=VOCATION]
 
        Optional<Employee> op2=employees.parallelStream()//findAny-返回当前流中的任意元素
                                        .filter((e)->e.getStatus().equals(Status.FREE))
                                        .findAny();
        System.out.println(op2.get());//Employee [name=赵六, age=36, salary=6666.66, Status=FREE]
 
        Long count=employees.stream()//count-返回流中元素的总个数
                            .count();
        System.out.println(count);//5
 
        Optional<Employee> op3=employees.stream()//max-返回流中最大值
                                        .max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(op3.get());//Employee [name=张三, age=18, salary=9999.99, Status=FREE]
 
        Optional<Double> op4=employees.stream()//min-返回流中最小值
                                      .map(Employee::getSalary)
                                      .min(Double::compare);
        System.out.println(op4.get());//3333.33
    }
           

5.2归约 

/*
     * 归约
     * reduce(T identity,BinaryOperator b) / reduce(BinaryOperator b)-可以将流中元素反复结合起来,得到一个值。
     */
    @Test
    public void test3(){
        List<Integer> list=Arrays.asList(,,,,,,,,,);
        Integer sum=list.stream()//reduce(T identity,BinaryOperator b)
                        .reduce(, (x,y)->x+y);//0为起始值
        System.out.println(sum);
 
        System.out.println("--------------------------");
 
        Optional<Double> op=employees.stream()//reduce(BinaryOperator b)//没有起始值,map返回可能为空,所以返回Optional类型
                                     .map(Employee::getSalary)
                                     .reduce(Double::sum);
        System.out.println(op.get());
    }
           

5.3收集 

/*
     * 收集
     * collect-将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。
     */
    @Test
    public void test4(){
        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());
        set.forEach(System.out::println);
 
        System.out.println("----------------------------");
 
        HashSet<String> hs=employees.stream()
                                    .map(Employee::getName)
                                    .collect(Collectors.toCollection(HashSet::new));
        hs.forEach(System.out::println);
 
        System.out.println("----------------------------");
 
        //总和
        Long count=employees.stream()
                            .collect(Collectors.counting());
        System.out.println(count);
 
        //平均值
        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());
 
        System.out.println("----------------------------");
 
        //分组
        Map<Status,List<Employee>> map=employees.stream()
                                                .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);//{FREE=[Employee [name=张三, age=18, salary=9999.99, Status=FREE], Employee [name=赵六, age=36, salary=6666.66, Status=FREE]], VOCATION=[Employee [name=王五, age=26, salary=3333.33, Status=VOCATION]], BUSY=[Employee [name=李四, age=58, salary=5555.55, Status=BUSY], Employee [name=田七, age=12, salary=8888.88, Status=BUSY]]}
 
        //多级分组
        Map<Status,Map<String,List<Employee>>> map2=employees.stream()
                                                            .collect( Collectors.groupingBy( Employee::getStatus,Collectors.groupingBy((e)->{
                                                                if(e.getAge()<=){
                                                                    return "青年";
                                                                }else if(e.getAge()<=){
                                                                    return "中年";
                                                                }else{
                                                                    return "老年";
                                                                }
                                                            }) ) );
        System.out.println(map2);//{FREE={青年=[Employee [name=张三, age=18, salary=9999.99, Status=FREE]], 中年=[Employee [name=赵六, age=36, salary=6666.66, Status=FREE]]}, VOCATION={青年=[Employee [name=王五, age=26, salary=3333.33, Status=VOCATION]]}, BUSY={青年=[Employee [name=田七, age=12, salary=8888.88, Status=BUSY]], 老年=[Employee [name=李四, age=58, salary=5555.55, Status=BUSY]]}}
 
        //分区
        Map<Boolean,List<Employee>> map3=employees.stream()
                                                 .collect(Collectors.partitioningBy((e)->e.getSalary()>));
        System.out.println(map3);//{false=[Employee [name=李四, age=58, salary=5555.55, Status=BUSY], Employee [name=王五, age=26, salary=3333.33, Status=VOCATION], Employee [name=赵六, age=36, salary=6666.66, Status=FREE]], true=[Employee [name=张三, age=18, salary=9999.99, Status=FREE], Employee [name=田七, age=12, salary=8888.88, Status=BUSY]]}
 
        System.out.println("--------------------------------");
 
        DoubleSummaryStatistics dss=employees.stream()
                                             .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(dss.getSum());
        System.out.println(dss.getAverage());
        System.out.println(dss.getMax());
 
        System.out.println("--------------------------------");
        String strr=employees.stream()
                             .map(Employee::getName)
                             .collect(Collectors.joining(","));
        System.out.println(strr);//张三李四王五赵六田七
     }
           

备注:

Java8新技术一.接口默认方法二.函数式接口三.Lambda表达式和方法引用四.Stream