JDK8 新特性

Java 8

现在 Java 按照周期发布,每六个月就更新一次。所谓 “小步快跑,快速迭代”

JDK8 是 Java 语言开发的一个主要版本,2014年 3月发布,是 JDK5 以来最具革命性的版本。

新特性简介:

  1. 速度更快 (HashMap 的数据结构更改)

  2. 代码更少 (Lambda 表达式)

  3. Stream API

  4. 便于并行

  5. 减少空指针异常 (Optional)

  6. Nashorn引擎,允许在 JVM 上运行 JS 应用



Lambda 表达式

Lambda 表达式是对象,而不是函数。

// 以前创建接口实现类的对象

public interface MyInterface {
    void testMethod();
}


class MyClass implements MyInterface{

    @Override
    public void testMethod() {
        System.out.println("重写接口");
    }
}


class Demo {
    public static void main(String[] args) {
        MyClass mc = new MyClass();
        mc.testMethod();
    }
}
// 使用匿名内部类创建对象

public interface MyInterface {
    void testMethod();
}

class Demo {
    public static void main(String[] args) {
        MyInterface ms = new MyInterface() {
            @Override
            public void testMethod() {
                System.out.println("Test");
            }
        };

        ms.testMethod();
    }
}
// 利用 Lambda 表达式

public interface MyInterface {
    void testMethod();
}

class Demo {
    public static void main(String[] args) {
        MyInterface ms = () -> System.out.println("Lambda");
        ms.testMethod();
    }
}

Lambda 本质就是 Java 中接口的一个实例,即接口的实现类的具体的对象。


语法:
-> :         箭头操作符
-> 左侧:      Lambda 形参列表 对应接口中抽象方法的形参列表
-> 右侧:      重写方法的,方法体的具体内容
// 无参,无返回值. 方法体中只有一条语句, 花括号可以省略
MyInterface my = () -> {System.out.println("PS");};
my.testMethod();

// 有一个参数, 无返回值
MyInterface my = (int x) -> System.out.println("PS" + x);
my.testMethod(12);

// 类型推断, 可以去掉参数类型
MyInterface my = (x) -> System.out.println("ps" + x);
my.testMethod(12);

// 一个参数的时候 小括号也能不写
MyInterface my = x -> System.out.println("ps" + x);
my.testMethod(12);
// 多个参数且带返回值
public interface MyInterface {
    String testMethod(int age, int num);
}

class Demo {
    public static void main(String[] args) {

        MyInterface my = (x,y) -> {
            System.out.println("Test");
            return "Hello" + "ps" + x;
        };
        System.out.println( my.testMethod(12,17));
    }
}
// 如果只剩一个 return 的时候
MyInterface my = (x,y) -> {
    return "Hello" + "ps" + x;
};


// 可以直接这样
MyInterface my = (x,y) -> "Hello" + "ps" + x;


函数式接口

Lambda 必须依赖一个接口,只有一个抽象方法的接口,称为函数式接口

@FunctionalInterface 此注解在接口中有多个抽象方法时,便会爆红。不过除了 Object 类中的抽象方法。

JDK8 中新加入了一个包 java.util.function ,这个包里面有几个常用的函数式接口:

  1. Consumer 消费型接口
  2. Function 函数型接口
  3. Predicate 断定型接口
  4. Supplier 供给型接口

这些接口功能就是,不用你再去创建新接口,按照名字的语义。直接用它的就行了,比如:

public static void main(String[] args) {

    Consumer<Double> c = new Consumer<Double>() {
        @Override
        public void accept(Double o) {
            System.out.println("accept");
        }
    };

    c.accept(1.00);
}


// 简洁一下
Consumer<Double> c = o -> System.out.println("accept");

public static void main(String[] args) {

	List<String> list = Arrays.asList("asss", "aaaa", "vvvv", "vvv");

// 简化前
//        List<String> result = filterString(list, new Predicate<String>() {
//            @Override
//            public boolean test(String o) {
//                return o.length() < 4;
//            }
//        });


// 简化后
    List<String> result = filterString(list, o -> o.length() < 4);

    System.out.println(list);
    System.out.println(result);
}

public static List<String> filterString(List<String> waitTest, Predicate<String> predicate) {
    List<String> filterList = new ArrayList<>();
    for (String item : waitTest) {
        if (predicate.test(item)) {
            filterList.add(item);
        }
    }
    return filterList;
}



方法引用

当要传递给 Lambda 体的操作,已经有实现的方法了,可以使用方法引用。

使用操作符 :: 将类(或者对象)与方法名分开。

如下三种情况使用:

// 1. 对象::实例方法名 (非静态方法)

class Student {
    String name;
    Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }

    public Integer getAge() { return age; }
}

class Demo {
    public static void main(String[] args) {
        Student student = new Student("jc", 18);

        // 第一种写法
        Supplier<String> s1 = () -> {
            return student.getName();
        };
        System.out.println(s1.get());

        // 第二种写法
        Supplier<String> s2 = () -> student.getName();
        System.out.println(s2.get());

        // 方法引用的写法
        Supplier<String> s3 = student::getName;
        System.out.println(s3.get());
    }
}

只要接口的抽象方法和具体的实现方法,参数一样、返回值一样,就可以这样用。上面代码中,student.getName()Supplier 中的 get() 就满足此条件。

// 2. 类::静态方法名

// 写法一
Comparator<Integer> c1 = (x, y) -> Integer.compare(x, y);
System.out.println(c1.compare(12, 20));

// 写法二
Comparator<Integer> c2 = Integer::compareTo;
System.out.println(c2.compare(12, 20));
// 3. 类::实例方法名 (非静态方法)

// 写法一
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abc", "abc"));

// 写法二
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));

这个就有点奇怪了,显然 equals() 的参数列表,与下方 test() 的明显不同。与上面的说明并不符啊。

其实 (x, y) -> ... 中第一个参数表示:调用方法的调用者

第二个参数就是调用方法的实际传入参数。只要是这种第一个参数用来调用方法的,第二个参数是传入参数的,就能使用 类::实例方法名 的方式



构造器引用

利用供给型接口,返回一个对象

class Student {
    String name;

    public Student() { }

    public Student(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Demo {
    public static void main(String[] args) {
		// 第一种写法
        Supplier supp = () -> new Student();
        Object stuent = supp.get();
        System.out.println(stuent);
        
       	// 第二种写法
        Supplier supp = Student::new;
        Object stuent = supp.get();
        System.out.println(stuent);
        
        // 试一个可以传参的 可以自己看下 Function 注释
        Function<String, Student> f = Student::new;
        Object o = f.apply("zhouxuan");
        System.out.println(o);
    }
}

不过需要注意的是,实现的方法的参数,和接口的方法的参数必须一至。为啥上面调用的是空构造器,因为 get() 没有参数,那构造函数也调用的是无参的。

// 最后再看个创建数组的例子

// Lambda 表达式
Function<Integer, String[]> f1 = (x) -> new String[x];
String[] r1 = f1.apply(8);
System.out.println(r1.length);

// 数组引用
Function<Integer, String[]> f2 = String[]::new;
String[] r2 = f2.apply(5);
System.out.println(r2.length);


StreamAPI

除了 Lambda,JDK8 另一个重要改变就是 StreamAPI 了。

StreamAPI 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作。可以进行复杂的查找、过滤、映射等操作。使用 StreamAPI 对集合数据,在 java 层面进行操作,就类似于使用 SQL 执行的数据库查询。StreamAPI 提供了一种高效且易于使用的数据处理方式。

在实际开发中,很多数据来自缓存、数据库。我们就可以使用 StreamAPI 在 Java 层面高效简单地处理这些数据。

以前学习的集合,他们的增删改查,都是数据结构、内存层面的,即数据源是随之改变的。

现在的 Stream 操作,是 CPU 层面的,数据源不会随之改变。

使用的步骤:

  1. 先产生一个流 Stream (一个数据源获取一个流)
  2. 中间链式操作 (对数据源的数据进行处理)
  3. 产一个新流 (进行终止操作,此时去执行上一步的中间操作链,那就相当于是晚一步延迟执行)


创建 Stream 的几种方式:

        // 方式1:Collection 接口的方法
        Collection<String> col = new ArrayList<>();
        // 获取串行流
        Stream<String> s1 = col.stream();
        // 获取并行流
        Stream<String> s2 = col.parallelStream();


        // 方式2:Arrays 中的 Stream 方法
        IntStream s3 = Arrays.stream(new int[]{1, 2, 4});


        // 方式3:Stream 中的 of 方法
        Stream<String> s4 = Stream.of("aa", "cc", "dd");


        // 方式4:创建无限流 (就是每次 +2 无限加, 很神奇 执行一下就知道了)
        Stream<Integer> s5 = Stream.iterate(2, (x) -> x + 2);
        s5.forEach(System.out::println);

        // 也是创建无限流,产生无限多个
        Stream<Double> s6 = Stream.generate(() -> Math.random());
        s6.forEach(System.out::println);



StreamAPI 的中间操作

// 这是数据源 下面的都是操作这儿的        
	List<Student> list = Arrays.asList(
                new Student("lili", 19),
                new Student("ffff", 20),
                new Student("lll", 21),
                new Student("tttt", 22),
                new Student("aaaa", 23),
                new Student("cccc", 24)
        );
// filter 过滤

		// 以前这么写
        Iterator<Student> it = list.iterator();
        while (it.hasNext()) {
            Student item = it.next();
            if (item.getAge() > 21) {
                System.out.println(item);
            }
        }


		// 这是现在
        // 1. 创建 Stream
        Stream<Student> s = list.stream();

        // 2. 中间操作
        Stream<Student> s1 = s.filter((x) -> x.getAge() > 21);

        // 3. 终止操作
        s1.forEach(System.out::println);


		// 再体验下链式操作
        list.stream()
            .filter((x) -> x.getAge() > 21)
            .forEach(System.out::println);

// limit, 截断(短路)  
        list.stream()
                .filter((x) -> {
                    System.out.println("正在进行 " + x.getName());
                    return x.getAge() > 21;
                })
            	// 只要找到两个 满足了条件 后面就不管了
                .limit(2)
                .forEach(System.out::println);
// skip, 这个和 limit 相反        
		list.stream()
                .filter((x) -> {
                    System.out.println("正在进行 " + x.getName());
                    return x.getAge() > 21;
                })
            	// 跳过前两个满足条件的
                .skip(2)
                .forEach(System.out::println);
// distinct, 去重, 但是需要重写 hashCode() 和 equals()         
		list.stream()
                .distinct()
                .forEach(System.out::println);
// map, 映射   把原先的 Student 都映射成单个的名字     
		list.stream()
            	// .map((x) -> x.getName())
                .map(Student::getName)
                .forEach(System.out::println);
// 例子: 输出 age > 21 的名字
		list.stream()
                .map((x) -> {
                    if (x.getAge() > 21) {
                        return x.getName();
                    }
                    return null;
                })
                .filter((x) -> x != null)
                .forEach(System.out::println);
// sorted 排序, 这里暂时新建一个 list
        List<Integer> list = Arrays.asList(1, 3, 6, 2, 8, 11);

        list.stream()
                .sorted()
                .forEach(System.out::println);
// 例子: 按照学生年龄排序
        list.stream()
                .sorted((x, y) -> x.getAge() - y.getAge())
                .forEach(System.out::println);


StreamAPI 的终止操作

// allMatch, 是不是所有 Student 的 age 都满足条件
        boolean result = list.stream()
                .allMatch((x) -> x.getAge() > 20);
        System.out.println(result);
// anyMatch, 有一个满足条件就返回 true
        boolean result = list.stream()
                .anyMatch((x) -> x.getAge() > 21);
        System.out.println(result);
// noneMatch, 都不满足条件就返回 true        
		boolean result = list.stream()
                .noneMatch((x) -> x.getAge() > 100);
        System.out.println(result);
// 还有一些关于查找和匹配的
		findFirst() 返回第一个
		findAny() 返回任意一个	
		count() 获取此流的元素数
		max() 获取此流中的最大元素,需要传入 Comparator
		min() 获取此流中的最小元素,需要传入 Comparator
		forEach() 对此流的每个元素执行操作

接着是规约操作,下面的代码的意思是:reduce(0,.. 这里的 0 作为初始值,初始值赋值给 (x, y) ... 中的 x, y 是每次遍历得到的 age,这样每次将 y 加进 x 中,就能得到年龄的总和了。最后使用方法引用,语法更简便。

        Integer sumAge = list.stream()
                .map(Student::getAge)
                // .reduce(0, (x, y) -> x + y);
                .reduce(0, Integer::sum);
        System.out.println(sumAge);

最后一个终止操作,收集操作。用于给 Stream 中的元素进行汇总。下面是手机所有 Student 的 name,返回一个 集合。

// List, 简单的 Demo 献给大家
		List<String> allName = list.stream()
                .map(Student::getName)
                .collect(Collectors.toList());
        System.out.println(allName);

// Set, 当然可以使用 Set, 当然 Set 是唯一的哦
        Set<String> allName = list.stream()
                .map(Student::getName)
                .collect(Collectors.toSet());
        System.out.println(allName);

// Map, 最后是 Map
        Map<String, Integer> map = list.stream()
                .collect(Collectors.toMap((k) -> k.getName(), (v) -> v.getAge()));
        System.out.println(map);

// count, 得到学生总数
        Long sum = list.stream()
                .collect(Collectors.counting());
        System.out.println(sum);

// averaging, 得到平均年龄
        Double d = list.stream()
                .collect(Collectors.averagingDouble((x) -> x.getAge()));
        System.out.println(d);

// sum, 年龄总和
        Double d = list.stream()
                .collect(Collectors.summingDouble((x) -> x.getAge()));
        System.out.println(d);

// max, 获取年龄最大的, 当然也有 minBy()
        Optional<Student> op = list.stream()
                .collect(Collectors.maxBy((x, y) -> Double.compare(x.getAge(), y.getAge())));
        System.out.println(op.get());
// 分组, 按年龄分组        
		Map<Integer, List<Student>> map = list.stream()
                .collect(Collectors.groupingBy((x) -> x.getAge()));
        Set<Map.Entry<Integer, List<Student>>> entries = map.entrySet();
        for (Map.Entry<Integer, List<Student>> e : entries) {
            System.out.println(e.getKey() + "--" + e.getValue());
        }


// 分区, 满足条件的一个区 不满足条件的一个区
        Map<Boolean, List<Student>> map = list.stream()
                .collect(Collectors.partitioningBy((x) -> x.getAge() > 20));
        Set<Map.Entry<Boolean, List<Student>>> entries = map.entrySet();
        for (Map.Entry<Boolean, List<Student>> e : entries) {
            System.out.println(e.getKey() + "--" + e.getValue());
        }

// 拼接, 将所有的名字进行拼接
        String str = list.stream()
                .map((x) -> x.getName())
                .collect(Collectors.joining());
        System.out.println(str);
posted @ 2019-10-28 06:46  园友1676433  阅读(214)  评论(0)    收藏  举报