jdk8的新特性

1、Lambda表达式&函数式接口

1.1 什么是Lambda表达式

Lambda表达式可以理解为一种匿名函数的替代,Lambda表达式允许将函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递,目的是简化代码的编写

1.2 什么是函数式接口

lambda表达式需要函数式接口的支持,

所谓函数式接口,是指只有一个抽象方法

另外JDK8也提供了一个注解,帮助我们编译时检查语法是否符合

注解:@FunctionInterface

1.3 Lambda表达式使用案例

lambda 基本语法

函数式接口(A) 变量名(b) = (参数1,参数2...)->{
	//方法体;
}

A类型是函数式接口 或者 是被@FunctionInterface注解修饰的接口

案例1

//普通方式

//匿名内部类
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("run....");
    }
};
Thread t1 = new Thread(runnable);
t1.start();

//lambda 方式1
Runnable runnable1 = ()->{
    System.out.println("lambda..");
};
Thread t2 = new Thread(runnable1);
t2.start(); 
//lambda 方式2
//单个语句可省略大括号
Thread t3 = new Thread(()-> System.out.println("hello Lambda!"));
t3.start();

案例2

//如果方法体有返回值并且方法体只有一条语句时,可以省略return 和 ; 和 {}
//如果方法体没有返回值并且方法体只有一条语句时,可省略 ; 和 {}

//普通方式
//匿名内部类
Comparator<String> comparator = new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        };
TreeSet set = new TreeSet(comparator);

//lambda 方式1
Comparator<String> com1 = ((o1, o2) -> {
    return o1.length() - o2.length();
});
TreeSet treeSet = new TreeSet(com1);

//lambda 方式2
//这种方式有时候需加泛型,有可能后面参数调用的方法在Object类型中找不到
TreeSet<String> strings = new TreeSet<>(((o1, o2) -> o1.length() - o2.length()));

1.4 lambda表达式注意事项

lambda 引入新的操作符:->(箭头操作符),->将表达式分成两部分

左侧:(参数1,参数2...)表示参数列表;

右侧:{} 内部是方法体

  1. 形参列表的参数类型会自动判断;

  2. 如果参数列表为空,只需保留();

  3. 如果形参只有一个,()可以省略,只需要参数的名称即可;

  4. 如果方法体有返回值并且方法体只有一条语句时,可以省略return 和 ; 和 {}

    如果方法体没有返回值并且方法体只有一条语句时,可以省略 ; 和 {}

  5. 使用lambda表达式不会生成一个单独的内部类文件

  6. lambda表达式若访问了局部变量,则局部变量必须是final修饰的;若是局部变量没有加final关键字,系统会自动添加,此后再修改局部变量,会报错

2、流式编程-StreamAPI

2.1 什么是Stream

Stream是jdk8中处理数组、集合的抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。

一个Stream表面上与一个集合很类似,集合中保存的是数据,流设置的是对数据的操作。

Stream的特点:

  1. Stream自己不会存储元素。
  2. Stream不会改变源对象。相反,它会返回一个持有结果的新Stream。
  3. Stream 操作是延迟执行的,这意味着他会等到需要结果的时候才执行。详见下面例子
  4. Stream 遵循“做什么,而不是怎么去做”的原则。只需要描述做什么,而不用考虑程序是怎么实现的
  5. Stream 的终止操作执行完后,会关闭;该Stream不能被再次使用。

2.2 体验StreamAPI的特点

public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("wang");
    list.add("han");
    list.add("qing");

    int c = 0;
    for (String s : list) {
        if (s.length() > 3){
            c++;
        }
    }
    System.out.println(c);

    //Stream的方式
    // s -> s.length() > 3 lambda表达式
    //1、链式编程
    //2、函数式接口
    //3、lambda表达式
    //刚开始这一步并没有执行
    Stream<String> stream = list.stream().filter(s -> s.length() > 3);//中间操作
    //count() 结果的时候才执行上面的步骤
    long c1 = stream.count();
    System.out.println(c1);
    
    long c2 = stream.count();//运行报错 :流已经被操作或关闭
    System.out.println(c2);
}

2.3 使用StreamAPI的步骤

  1. 创建一个Stream 。(创建)
  2. 在一个或多个步骤中,将初始stream 转化到另一个Stream的中间操作。 (中间操作)
  3. 使用一个终止操作来产生一个结果。该操作会强制它之前的延时操作立即执行。在这之后,该Stream就不能再被使用了。(终止操作)

2.4 创建Stream的方式

  1. 集合创建:

    详见上面示例

  2. 数组创建:

int[] arr = {15,6,1,3,8};
IntStream stream1 = Arrays.stream(arr);
long c3 = stream1.filter((i) -> i > 2).count();
System.out.println(c3);

2.5 Stream的中间操作

2.5.1 筛选和切片(filter,limit(n),skip(n),distinct)

class Employee{
    private String name;
    private int age;
    private int salary;

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

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

    @Override
    public boolean equals(Object o) {
        System.out.println("------调用了equals方法-----");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                salary == employee.salary &&
                Objects.equals(name, employee.name);

    }

    @Override
    public int hashCode() {
//        System.out.println(Objects.hash(name, age, salary));
        return Objects.hash(name, age, salary);
    }

    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;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }
}
package com.wanghq.learning.JDK8;

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

/**
 * Stream的中间操作
 */
public class StreamAPI_1 {
    public static void main(String[] args) {
        Employee e1 = new Employee("zhangsan", 25, 6000);
        Employee e2 = new Employee("lisi", 30, 25000);
        Employee e3 = new Employee("wangwu", 40, 50000);
        Employee e4 = new Employee("wangwu", 40, 50000);
        List<Employee> list = new ArrayList<Employee>();
        list.add(e1);
        list.add(e2);
        list.add(e3);
        list.add(e4);

        //筛选年龄大于 28 的员工信息
        Stream<Employee> s1 = list.stream().filter(e -> e.getAge() > 28);
        s1.forEach(e-> System.out.println(e));

        //筛选年龄大于 28 的员工 并 去重
        // 去重方法会自动调用hashCode() 和 equals方法,hashCode方法值相同再判断equals方法
        Stream<Employee> s2 = list.stream().filter(e -> e.getAge() > 28).distinct();
        System.out.println(s2.count());

        System.out.println("=========== skip =============");

        //跳过前几个(长度)
        Stream<Employee> skip = list.stream().skip(2);
        skip.forEach(System.out::println);

        System.out.println("=========== limit =============");

        //获取前几个(长度)
        Stream<Employee> limit = list.stream().limit(2);
        limit.forEach(System.out::println);


    }
}

  • 《Effective Java》是本好书,连Java之父James Gosling都说,这是一本连他都需要的Java教程。在这本书中,作者指出,如果重写了一个类的equals()方法,那么就必须一起重写它的hashCode()方法!必须!没有商量的余地!

    必须使得重写后的equals()满足如下条件:

    ​ 根据equals()进行比较,相等的两个对象,hashCode()的值也必须相同;
    ​ 根据equals()进行比较,不相等的两个对象,hashCode()的值可以相同,也可以不同;
    因为这是Java的规定,违背这些规定将导致Java程序运行不再正常。

2.5.2 映射(map)

  • 将元素转成其他形式 或者提取信息。接受一个函数作为参数,该函数会被应用到每个元素上, 并将其映射成一个新的元素
  • 将一个集合的数据转换成另一个集合
//map接受 lambda表达式 
Stream<Integer> integerStream = list.stream().map(e -> e.getAge());
 integerStream.forEach(e-> System.out.println(e));

 //小写转大写
 List<String> lists = Arrays.asList("a", "b", "c");
 Stream<String> stream = lists.stream().map(String::toUpperCase/*e->e.toUpperCase()*/);
//        stream.forEach(System.out::println);两种写法都行
 stream.forEach(e-> System.out.println(e));

2.5.3 排序 (sorted)

//排序
System.out.println("====按年龄自然排序===");
Stream<Integer> sorted = list.stream().map(e -> e.getAge()).sorted();
sorted.forEach(System.out::println);

System.out.println("====定制排序1===");
Stream<String> sorted1 = list.stream().map(e -> e.getName()).sorted((x, y) -> {
    return x.length() - y.length();
});
sorted1.forEach(System.out::println);

System.out.println("====定制排序2===");
Stream<Employee> sorted2 = list.stream().sorted((x, y) -> {
    if (x.getAge() == y.getAge()) {
        return x.getName().compareTo(y.getName());
    } else
        return y.getAge() - x.getAge();
});
sorted2.forEach(employee -> System.out.println(employee));

//定制排序2 的匿名内部类写法  复习下匿名内部类
System.out.println("====定制排序2===");
        Stream<Employee> sorted2 = list.stream().sorted(
            	//匿名内部类, Comparator类也是个函数式接口,所以能写成lambda表达式
                new Comparator<Employee>() {
                    @Override
                    public int compare(Employee x, Employee y) {
                        if (x.getAge() == y.getAge()) {
                            return x.getName().compareTo(y.getName());
                        } else
                            return y.getAge() - x.getAge();
                    }
                }
            
               );
        sorted2.forEach(employee -> System.out.println(employee));

2.6 终止操作

2.6.1 遍历操作

forEach

2.6.2 查找和匹配

  • allMatch 检查当前流中是否匹配所有元素
  • anyMatch 检查当前流中是否至少匹配一个元素
  • noneMatch 检查当前流中是否没有匹配的元素
  • findFirst 返回当前流中的第一个元素
  • findAny 返回当前流中的任意元素
  • count 返回当前流的总个数
  • max 返回当前流中自定义比较规则(Comparator函数式接口)的最大值
  • min 返回当前流中自定义比较规则(Comparator函数式接口)的最小值
boolean b = list.stream().allMatch(employee -> employee.getAge() >30);
System.out.println(b);

boolean b1 = list.stream().anyMatch(e -> e.getAge() > 30);
System.out.println(b1);

System.out.println(list.stream().noneMatch(e -> e.getAge() > 40));

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

Optional<Employee> any = list.stream().findAny();
System.out.println(any.get());

Optional<Employee> max = list.stream().max((x, y) -> {
    return x.getAge() - y.getAge();
});
System.out.println(max.get());

2.7 Stream的串行 和 并行

Stream有串行和并行两种,串行Stream的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

串行

public static void main(String[] args) {

        List list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            list.add(UUID.randomUUID().toString());
        }
        long start = System.currentTimeMillis();
    
        long count = list.stream().sorted().count();
    
        System.out.println(count);
        long end = System.currentTimeMillis();
        //计算排序的时间
        System.out.println(end-start);// 669
}

并行

public static void main(String[] args) {   
    
	List list = new ArrayList<>();
    for (int i = 0; i < 1000000; i++) {
        list.add(UUID.randomUUID().toString());
    }
    long start = System.currentTimeMillis();
    //命令式的
    long count = list.parallelStream().sorted().count();
    
    System.out.println(count);
    long end = System.currentTimeMillis();
    //计算排序的时间
    System.out.println(end-start);// 390
}
posted @ 2021-08-29 11:30  wannx  阅读(59)  评论(0)    收藏  举报